Merge lp:~fluidity-core/fluidity/iceshelf into lp:fluidity

Proposed by Satoshi Kimura
Status: Rejected
Rejected by: Adam Candy
Proposed branch: lp:~fluidity-core/fluidity/iceshelf
Merge into: lp:fluidity
Diff against target: 202417 lines (+4975/-195824)
115 files modified
aclocal.m4 (+7/-25)
configure (+53/-85)
examples/backward_facing_step_3d/Ercoftac-test31-BFS/readme (+28/-0)
examples/backward_facing_step_3d/Makefile (+1/-1)
examples/hokkaido-nansei-oki_tsunami/hokkaido-nansei-oki_tsunami.xml (+2/-2)
examples/restratification_after_oodc/Makefile (+1/-1)
examples/tephra_settling/tephra_settling.flml (+0/-5)
examples/tephra_settling/tephra_settling.xml (+0/-11)
examples/top_hat/top_hat.xml (+1/-1)
femtools/Diagnostic_Fields.F90 (+1/-19)
femtools/Halos_Communications.F90 (+10/-13)
femtools/Makefile.dependencies (+0/-12)
femtools/Makefile.in (+1/-1)
femtools/Parallel_fields.F90 (+8/-20)
femtools/Vertical_Extrapolation.F90 (+0/-1290)
main/Fluids.F90 (+11/-16)
main/Makefile.dependencies (+1/-1)
manual/embedded_models.tex (+0/-7)
manual/model_equations.tex (+6/-37)
manual/numerical_discretisation.tex (+5/-26)
manual/parameterisations.tex (+0/-12)
manual/visualisation_and_diagnostics.tex (+0/-1)
parameterisation/Gls_vertical_turbulence_model.F90 (+103/-355)
parameterisation/Ice_Melt_Interface.F90 (+950/-0)
parameterisation/Makefile.dependencies (+5/-6)
parameterisation/Makefile.in (+1/-1)
parameterisation/iceshelf_meltrate_surf_normal.F90 (+0/-678)
schemas/multiphase_interaction.rnc (+0/-58)
schemas/multiphase_interaction.rng (+0/-65)
schemas/prognostic_field_options.rnc (+1/-2)
schemas/prognostic_field_options.rng (+1/-2)
schemas/spatial_discretisation.rnc (+1/-1)
schemas/spatial_discretisation.rng (+1/-1)
tests/diff_src_robin_1d/diff_src_robin_1d.xml (+1/-16)
tests/diff_src_robin_1d/diff_src_robin_1d_cvelegrad_p1_A.flml (+0/-244)
tests/flux_bc/Makefile (+0/-22)
tests/flux_bc/flux_bc.xml (+0/-54)
tests/flux_bc/flux_bc_2d.flml (+0/-396)
tests/flux_bc/flux_bc_3d.flml (+0/-405)
tests/flux_bc/src/flux_bc_2d.geo (+0/-19)
tests/flux_bc/src/flux_bc_3d.geo (+0/-69)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-gen-CA.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-gen-CB.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-gen-GL.flml (+8/-8)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-gen-KC94.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_e-CA.flml (+2/-1)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_e-CB.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_e-GL.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_e-KC94.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_w-CA.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_w-CB.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_w-GL.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth-k_w-KC94.flml (+4/-4)
tests/gls-Kato_Phillips-mixed_layer_depth/gls-Kato_Phillips-mixed_layer_depth.xml (+38/-39)
tests/gls-Kato_Phillips-mixed_layer_depth/mixed_layer_depth.py (+1/-1)
tests/gls-Kato_Phillips-mixed_layer_depth/mixed_layer_depth_all.py (+3/-3)
tests/gls-Kato_Phillips-mixed_layer_depth/mld_calc.py (+1/-1)
tests/gls-ocean-param/Makefile (+0/-8)
tests/gls-ocean-param/gls-StationPapa-orig.flml (+0/-1024)
tests/gls-ocean-param/gls-StationPapa-param.flml (+0/-1032)
tests/gls-ocean-param/gls-StationPapa-setup.flml (+0/-1071)
tests/gls-ocean-param/gls-StationPapa-setup_CoordinateMesh_1_checkpoint.ele (+0/-601)
tests/gls-ocean-param/gls-StationPapa-setup_CoordinateMesh_1_checkpoint.face (+0/-805)
tests/gls-ocean-param/gls-StationPapa-setup_CoordinateMesh_1_checkpoint.node (+0/-405)
tests/gls-ocean-param/gls-StationPapa-setup_VelocityMesh_1_checkpoint.vtu (+0/-27)
tests/gls-ocean-param/gls-ocean-param.xml (+0/-57)
tests/gls-ocean-param/gls_ocean_param.py (+0/-285)
tests/gls-ocean-param/salinity.csv (+0/-125)
tests/gls-ocean-param/src/gls-StationPapa-3D.geo (+0/-25)
tests/gls-ocean-param/temperature.csv (+0/-125)
tests/mphase_divergence_free_velocity/mphase_divergence_free_velocity.xml (+0/-10)
tests/mphase_dusty_gas_shock_tube/Makefile (+0/-18)
tests/mphase_dusty_gas_shock_tube/mphase_dusty_gas_shock_tube.flml (+0/-733)
tests/mphase_dusty_gas_shock_tube/mphase_dusty_gas_shock_tube.xml (+0/-160)
tests/mphase_dusty_gas_shock_tube/plot.py (+0/-93)
tests/mphase_dusty_gas_shock_tube/shocktube.py (+0/-157)
tests/mphase_dusty_gas_shock_tube/single_phase_frozen_flow_test.flml (+0/-379)
tests/mphase_dusty_gas_shock_tube/single_phase_frozen_flow_test.py (+0/-75)
tests/mphase_identical_velocities/mphase_identical_velocities.xml (+0/-10)
tests/mphase_identical_velocities_dg/mphase_identical_velocities_dg.xml (+0/-11)
tests/mphase_inlet_velocity_bc_incompressible/Makefile (+0/-17)
tests/mphase_inlet_velocity_bc_incompressible/mphase_inlet_velocity_bc_incompressible.flml (+0/-527)
tests/mphase_inlet_velocity_bc_incompressible/mphase_inlet_velocity_bc_incompressible.xml (+0/-73)
tests/mphase_mms_p1dgp2_test_cty_cv_no_interactions/mphase_mms_p1dgp2_test_cty_cv_no_interactions.xml (+0/-10)
tests/mphase_mms_p1p1stabilised_no_interactions/mphase_mms_p1p1stabilised_no_interactions.xml (+0/-9)
tests/mphase_rising_body/mphase_rising_body.xml (+0/-9)
tests/mphase_rogue_shock_tube_dense_bed_glass/Makefile (+0/-18)
tests/mphase_rogue_shock_tube_dense_bed_glass/mphase_rogue_shock_tube_dense_bed_glass.flml (+0/-824)
tests/mphase_rogue_shock_tube_dense_bed_glass/mphase_rogue_shock_tube_dense_bed_glass.xml (+0/-118)
tests/mphase_rogue_shock_tube_dense_bed_glass/plot.py (+0/-70)
tests/mphase_rogue_shock_tube_dense_bed_glass/src/mphase_rogue_shock_tube_dense_bed_glass.geo (+0/-19)
tests/mphase_rogue_shock_tube_dense_bed_nylon/Makefile (+0/-17)
tests/mphase_rogue_shock_tube_dense_bed_nylon/mphase_rogue_shock_tube_dense_bed_nylon.flml (+0/-824)
tests/mphase_rogue_shock_tube_dense_bed_nylon/mphase_rogue_shock_tube_dense_bed_nylon.xml (+0/-118)
tests/mphase_rogue_shock_tube_dense_bed_nylon/plot.py (+0/-96)
tests/mphase_rogue_shock_tube_dense_bed_nylon/src/mphase_rogue_shock_tube_dense_bed_nylon.geo (+0/-19)
tests/mphase_sedimentation_1d/mphase_sedimentation_1d.xml (+0/-9)
tests/mphase_sedimentation_2d/mphase_sedimentation_2d.xml (+0/-10)
tests/mphase_stokes_law/mphase_stokes_law.flml (+0/-5)
tests/mphase_stokes_law/mphase_stokes_law.xml (+0/-10)
tests/mphase_tephra_settling/mphase_tephra_settling.flml (+0/-5)
tests/mphase_tephra_settling/mphase_tephra_settling.xml (+0/-10)
tests/mphase_three_layers/mphase_three_layers.xml (+0/-10)
tests/mphase_wen_yu_drag_correlation/Makefile (+0/-17)
tests/mphase_wen_yu_drag_correlation/c_against_re.py (+0/-26)
tests/mphase_wen_yu_drag_correlation/mphase_wen_yu_drag_correlation.flml (+0/-793)
tests/mphase_wen_yu_drag_correlation/mphase_wen_yu_drag_correlation.xml (+0/-86)
tests/mphase_wen_yu_drag_correlation/src/mphase_wen_yu_drag_correlation.geo (+0/-19)
tests/photosynthetic_radiation/Makefile (+0/-1)
tests/photosynthetic_radiation/photosynthetic_radiation.flml (+1/-1)
tests/photosynthetic_radiation/photosynthetic_radiation.xml (+2/-2)
tests/photosynthetic_radiation/src/column.geo (+4/-4)
tests/photosynthetic_radiation/src/column.msh (+1837/-177170)
tests/photosynthetic_radiation_cg/photosynthetic_radiation.flml (+1/-1)
tests/photosynthetic_radiation_cg/src/column.msh (+1837/-3568)
To merge this branch: bzr merge lp:~fluidity-core/fluidity/iceshelf
Reviewer Review Type Date Requested Status
Adam Candy Needs Fixing
Stephan Kramer Disapprove
Review via email: mp+117538@code.launchpad.net

Description of the change

Adding iceshelf parameterization to the trunk.
As of today, this branch has green light from Buildbot (http://buildbot-ocean.ese.ic.ac.uk:8080/builders/iceshelf/builds/9).
The merge will enable us to:
1) prescribe ice as a boundary condition on T and S;
2) allow inhomogeneous Neumann BC on scalar fields discretized in P1DG.

To post a comment you must log in.
Revision history for this message
Alexandros Avdis (alexandros-avdis) wrote :

The in-homogeneous Neumann BC is incomplete, will be making some additions (really made by Stefan) to lp:~fluidity-core/fluidity/DG_Neumann.

lp:~fluidity-core/fluidity/iceshelf updated
4021. By Jon Hill

New GLS updates which include:
 - Ocean parameterisation which parameterises internal wave breaking, adding some surface TKE into deeper water
 - Updates to assembly routines which are now mostly element-wise
 - Updates to tests
 - Manual updates

4022. By Jon Hill

Adding tests for PAR and updating manual

4023. By Rhodri Davies

Bug fix for CV, when a diffusion term is present. Again supplied by Cian.

4024. By Christian Jacobs

Merging compressible-multiphase branch revisions r3955 to r4067 (inclusive) into trunk.

Summary of changes:
===================

- Added multiphase support for the compressible projection method (currently only for CG Pressure).

- Small changes to the logic in Momentum_Equation.F90. Replaced the use_compressible_projection logical with compressible_eos which is .true. if option_count("/material_phase/equation_of_state/compressible") > 0, and .false. otherwise.

- Added multiphase support for the InternalEnergy equation. Note that the whole state array is now passed to solve_field_equation_cg.

- Implemented the heat transfer term by Gunn (1978).

- Added three P2-P1 multiphase MMS tests for model verification: one without InternalEnergy fields, one with InternalEnergy fields, and one with InternalEnergy fields and heat transfer. The tests will become longtests after merging into the trunk.

- The manual now has documentation on the compressible multiphase model, including the multiphase version of the InternalEnergy equation.

- Added a diagnostic field called CompressibleContinuityResidual which computes the residual of the compressible multiphase continuity equation. This is similar to the SumVelocityDivergence field for incompressible multiphase flow which computes \sum{div(vfrac*u)}.

- Added two gas-solid shock tube tests to help to validate the model: mphase_rogue_shock_tube_dense_bed_glass and mphase_rogue_shock_tube_dense_bed_nylon.

- Added another gas-solid shock tube test (mphase_dusty_gas_shock_tube) which uses the setup by Miura & Glass (1982).

- Implemented two new correlations for fluid-particle drag. The one by Wen & Yu (1966) can support higher Reynolds numbers (compared to the existing Stokes' law correlation). The one by Ergun (1952) can support dense flows where the volume fraction of the particle phase is greater than 0.2, like in the dense bed shock tube tests (and in the pyroclastic flow tests which will hopefully come later).

- Changed the structure of the schema. Users now explicitly select which interaction terms they wish to include, as well as the drag correlation, under "/multiphase_interaction" in Diamond. These options are stored in the new files multiphase_interaction.rnc and multiphase_interaction.rng.

- Added a test for the Wen & Yu (1966) drag correlation. This is similar to mphase_stokes_law.

- Added a test to check if inlet velocity boundary conditions work with the incompressible multiphase flow model.

- Added a test for the flux boundary condition.

4025. By Christian Jacobs

Moved the compressible multiphase MMS tests to the longtests directory.

4026. By Christian Jacobs

- Updated the tephra_settling example's .flml file to use the new multiphase_interaction schema structure.
- Corrected a small typo in the manual.

4027. By Lawrence Mitchell

Fix DG momentum assembly corner case

Since r3984 we would assemble elements in DG momentum if
element_neighbour_owned returned true. However, for some corner cases
element_neighbour_owned need not return true if the element is owned
(consider the case of a process with one owned element). In this
situation we would not correctly assemble all elements. To fix this,
change the test for element assembly to directly check for element
ownership.

At the same time, update the documentation for element_neighbour_owned
to note this issue.

4028. By Rhodri Davies

Merge relax_halo_verifies branch into trunk. Thanks to dham for review.

4029. By j-bull08 <email address hidden>

Removing duplicate file in 3D BFS example.

4030. By Lawrence Mitchell

Fix CG momentum assembly on intel fortran

This only affects the have_les and .not. dynamic_les code path.

4031. By Rhodri Davies

Merge zoltan configure check for .mod files into trunk. Thanks to steph for help and jon for review.

4032. By Christian Jacobs

Added solvers_converged pass_tests to all multiphase tests.

4033. By Christian Jacobs

Added a solvers_converged pass_test to the tephra_settling example.

4034. By Samuel Parkinson

Updated Momentum_CG.F90 so that upwind stabilisation can be used with partial or full stress form. The issue is that when using partial or full stress form we tend to set all components of viscosity to the same value. This makes the tensor determinant zero and we cannot invert it. This fix simply sets all non-diagonal terms to zero for the purpose of handling stabilisation when using partial or full stress forms. This isn't ideal as it ignores the fact that we may have non-isotropic viscosities, but these aren't handled properly for these stress forms anyway - so this is a bigger issue that may need fixing later.

The same issue arises when we try to calculate the GridReynoldsNumber diagnostic field and the same fix has been applied here.

4035. By Samuel Parkinson

bug fix of SUPG stabilisation in Momentum_CG.F90

Revision history for this message
Tim Greaves (tim-greaves) wrote :

Toshi is keen to get reviews of this and make any corrections needed prior to merging; if anyone with knowledge of this code is able to look through this request and give feedback that would be much appreciated!

Revision history for this message
Jon Hill (jon-hill) wrote :

Also note that Toshi has made a change to the VTK interface which from the code looks like it allows the user to stop the DG-ifiying of output fields. Are the DG fields projected to continuous somewhere?

Are the fluidity.log and .err files meant to be there?

Looks OK to me, but I would prefer someone else cast an eye over it too.

Revision history for this message
Stephan Kramer (s-kramer) wrote :

The DG Neumann bc is being further developed on this branch https://code.launchpad.net/~fluidity-core/fluidity/DG_Neumann. It needed fixing to work with subcycling. It now needs some proper tests.

This merge seems to have all sorts of other unrelated stuff: FLHere(), /io/adhere_to_output_mesh_continuity, /top_position/align_with_geoids, regularise_aspect_ratio, subshelf_hydrostatic_pressure... These all may, or may not be useful additions but should be proposed for a merge seperately.

Please have a look at the diff with the trunk yourself before you put anything up for merge proposal. Reviewing is a lot of work, so we expect things to be cleaned up properly before requesting a merge (no commented out code, etc.)

review: Disapprove
Revision history for this message
Adam Candy (asc) wrote :

The latest commit deleting the ice shelf test cases to get the build machine passing was a retrograde step! These need to be fixed and returned to the merge.

review: Needs Fixing
Revision history for this message
Adam Candy (asc) wrote :

This branch also contains features currently being worked on in other branches, which themselves are under merge review. These features are not required by the ice shelf thermodynamics, which is what this merge concerns and should be removed from the branch.

These include:
lp:~asc/fluidity/extrudeup
lp:~asc/fluidity/regularisespatialaspect
lp:~asc/fluidity/here
lp:~asc/fluidity/dgifychoice

review: Needs Fixing
Revision history for this message
Satoshi Kimura (skimura04) wrote :

Thanks everyone for good thoughts.
I will start from merging in the thermodynamics part first and work the way up.

Toshi

> This branch also contains features currently being worked on in other
> branches, which themselves are under merge review. These features are not
> required by the ice shelf thermodynamics, which is what this merge concerns
> and should be removed from the branch.
>
> These include:
> lp:~asc/fluidity/extrudeup
> lp:~asc/fluidity/regularisespatialaspect
> lp:~asc/fluidity/here
> lp:~asc/fluidity/dgifychoice

lp:~fluidity-core/fluidity/iceshelf updated
4036. By Tim Greaves

One-line fix, setting the correct flml filename in the test xml.

4037. By Adam Candy

Removal of a runtime-generated test file.
jhill - please check!

4038. By Samuel Parkinson

Added options for how the bed shear stress is calculated. Yuo can now either:
- use drag formulation as before
- calculate the shear stress based upon the gradient of the velocity field at the bed
- use the shear stress estimate generated by the wall boundary layer boundary condition algorithm

4039. By Samuel Parkinson

change to lock_exchange example so that, when run as a test, it runs to 30 seconds so that the tests are checked properly

4040. By Christian Jacobs

Merging compressible-multiphase branch revisions r4068 to r4077 (inclusive) into trunk.

Summary of changes:
===================

- Added multiphase support for stress_form, partial_stress_form and isotropic diagonal_viscosity in Momentum_CG.F90. The stress_form will be important in compressible multiphase flow simulations.

- Added an MMS test (mphase_mms_p2p1_stress_form) for the stress_form changes.

4041. By Christian Jacobs

Small edit to the manual to generalise the form of the multiphase stress term.

4042. By Xiaohu Guo

  1. thread matrix assembly part for Advection_Diffusion_DG.F90
  2. MASSLUMPED_RT0 diffusion scheme is not supported, simply set threads back to 1 if MASSLUMPED_RT0 diffusion scheme is being used, and there will be a warning message.
  3. thread matrix assembly part for Advection_Diffusion_FV.F90

4043. By Christian Jacobs

Merging k_epsilon_extend_and_fix branch into trunk.

Changes to k-epsilon model:
===========================
- The code no longer lumps the mass matrix when calculating source terms for the k and epsilon fields, allowing element pairs other than P1-P1 to be used.
- k-epsilon field calculation moved to the beginning of the time-step and calculated with all other diagnostic fields.
- Added source term to momentum equation (-2/3 grad(rho*k)).
- Updated the manual documentation for the k-epsilon model.
- Users must now use partial-stress or full-stress form for the stress term in the momentum equation. This is the correct formulation when using spatially
varying viscosities. The ScalarEddyViscosity from the model is added to all components of the stress tensor to reflect this.
- Fixed calculation of c_eps_3.
- Added lots of debugging options.
- Improved the options_check subroutine.
- Changed how prescribed source terms are implemented for the k and epsilon fields so that multiple non-linear iterations can be carried out
with prescribed source terms.
- Altered calculation of Reynolds Stresses.
- Added a new equation_type (KEpsilon) for the k-epsilon equation. This includes the density in the advection-diffusion equation for the k and epsilon fields. Also made many other changes to the code to take account of density variations. Supports CG and CV discretisations.
- Made a few preliminary steps for adding multiphase support to the k-epsilon model.
- k and epsilon fields are now clipped at Gauss points rather than nodes.
- Moved element assembly to its own subroutine to avoid dynamic memory assignment.
- Changed k and epsilon diffusivity tensor to (nu + nu_t/sigma_*) instead of (nu_t/sigma_*)
- The turbulent buoyancy term is now calculated from the VelocityBuoyancyDensity rather than a summation of scalar fields that have been identified as
being buoyant. This means that any buoyant field will automatically result in a buoyancy term in the k-epsilon model (unless the entire term
is disabled in debugging_options)
- Removed several unused variables.
- Added the buoyancy term to the k-epsilon model.
- Added damping functions for the low_Re k-epsilon model.
- Changed epsilon low_Re boundary condition to d(eps)/dx = 0 as it's simpler than what we had before and still valid (Wilcox 1998).
- Fixes to high-Re boundary conditions.
- Removed length scale limiting.

Changes to tests and examples:
==============================
- The existing k-epsilon MMS test (k-epsilon_mms_cg_p1p1) has been removed and replaced with one that uses a P2-P1 discretisation and runs to steady state.
- Three additional tests have been added for the low_Re boundary condition, and the LinearMomentum Velocity equation_type with CG and CV discretisations.
- Tests include the turbulent buoyancy term and scalar field turbulent diffusion.
- A unit test has been added for the tensor_inner_product function.
- Fixed the backward facing step 2D example based on the changes to the k-epsilon model.

4044. By Christian Jacobs

Moved tests/mms_rans_p2p1_keps_linearmomentum and tests/mms_rans_p2p1_keps_linearmomentum_cv to the longtests directory.

4045. By Christian Jacobs

Buildbot's makefiles-x86_64 queue has been failing since r4042. These updates from make makefiles will hopefully fix this.

4046. By Adam Candy

Wetting and Drying bug fix - the material phase had an assumed name of 'water'.
(It is now just assumed to be the first material phase)

4047. By Satoshi Kimura

Added the meltrate code

4048. By Satoshi Kimura

Fixed the makefile.dependencies

Unmerged revisions

4058. By Satoshi Kimura

Merge the trunk

4057. By Satoshi Kimura

Merge the trunk

4056. By Satoshi Kimura

Merge the recent trunk

4055. By Satoshi Kimura

Changed the min velocity for melting

4054. By Satoshi Kimura

Changed the min velocity for melting

4053. By Satoshi Kimura

Merged the trunk to be updated

4052. By Satoshi Kimura

Fixing the test case, iceshelf

4051. By Satoshi Kimura

Fixed my test case to pass the medium test

4050. By Satoshi Kimura

Deleted some comments in Ice_Melt_Interface.F90

4049. By Satoshi Kimura

Added the test case

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'aclocal.m4'
2--- aclocal.m4 2012-08-16 17:13:12 +0000
3+++ aclocal.m4 2012-09-04 15:00:37 +0000
4@@ -412,6 +412,8 @@
5 AC_MSG_ERROR( [You need to set PETSC_DIR to point at your PETSc installation... exiting] )
6 fi
7
8+
9+
10 PETSC_LINK_LIBS=`make -s -f petsc_makefile getlinklibs`
11 LIBS="$PETSC_LINK_LIBS $LIBS"
12
13@@ -465,7 +467,7 @@
14 # petsc tutorial - using the headers in the same way as we do in the code
15 AC_LINK_IFELSE(
16 [AC_LANG_SOURCE([
17-program test_petsc
18+program test
19 #include "petscversion.h"
20 #ifdef HAVE_PETSC_MODULES
21 use petsc
22@@ -572,13 +574,13 @@
23 call MatDestroy(A,ierr)
24
25 call PetscFinalize(ierr)
26-end program test_petsc
27+end program test
28 ])],
29 [
30 AC_MSG_NOTICE([PETSc program succesfully compiled and linked.])
31 ],
32 [
33-cp conftest.F90 test_petsc.F90
34+cp conftest.F90 test.F90
35 AC_MSG_FAILURE([Failed to compile and link PETSc program.])])
36
37 AC_LANG_RESTORE
38@@ -1004,6 +1006,7 @@
39 echo "note: using $zoltan_INCLUDES_PATH/zoltan.mod"
40 fi
41
42+
43 AC_LANG_SAVE
44 AC_LANG_C
45 AC_CHECK_LIB(
46@@ -1011,28 +1014,7 @@
47 [Zoltan_Initialize],
48 [AC_DEFINE(HAVE_ZOLTAN,1,[Define if you have zoltan library.])],
49 [AC_MSG_ERROR( [Could not link in the zoltan library... exiting] )] )
50-
51-# Small test for zoltan .mod files:
52-AC_LANG(Fortran)
53-ac_ext=F90
54-# In fluidity's makefile we explicitly add CPPFLAGS, temporarily add it to
55-# FCFLAGS here for this zoltan test:
56-tmpFCFLAGS="$FCFLAGS"
57-FCFLAGS="$FCFLAGS $CPPFLAGS"
58-AC_LINK_IFELSE(
59-[AC_LANG_SOURCE([
60-program test_zoltan
61- use zoltan
62-end program test_zoltan
63-])],
64-[
65-AC_MSG_NOTICE([Great success! Zoltan .mod files exist and are usable])
66-],
67-[
68-cp conftest.F90 test_zoltan.F90
69-AC_MSG_FAILURE([Failed to find zoltan.mod files])])
70-# And now revert FCFLAGS
71-FCFLAGS="$tmpFCFLAGS"
72+# Save variables...
73 AC_LANG_RESTORE
74
75 ZOLTAN="yes"
76
77=== modified file 'configure'
78--- configure 2012-08-16 17:13:12 +0000
79+++ configure 2012-09-04 15:00:37 +0000
80@@ -2137,52 +2137,6 @@
81
82 } # ac_fn_f77_try_link
83
84-# ac_fn_fc_try_link LINENO
85-# ------------------------
86-# Try to link conftest.$ac_ext, and return whether this succeeded.
87-ac_fn_fc_try_link ()
88-{
89- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
90- rm -f conftest.$ac_objext conftest$ac_exeext
91- if { { ac_try="$ac_link"
92-case "(($ac_try" in
93- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
94- *) ac_try_echo=$ac_try;;
95-esac
96-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
97-$as_echo "$ac_try_echo"; } >&5
98- (eval "$ac_link") 2>conftest.err
99- ac_status=$?
100- if test -s conftest.err; then
101- grep -v '^ *+' conftest.err >conftest.er1
102- cat conftest.er1 >&5
103- mv -f conftest.er1 conftest.err
104- fi
105- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
106- test $ac_status = 0; } && {
107- test -z "$ac_fc_werror_flag" ||
108- test ! -s conftest.err
109- } && test -s conftest$ac_exeext && {
110- test "$cross_compiling" = yes ||
111- $as_test_x conftest$ac_exeext
112- }; then :
113- ac_retval=0
114-else
115- $as_echo "$as_me: failed program was:" >&5
116-sed 's/^/| /' conftest.$ac_ext >&5
117-
118- ac_retval=1
119-fi
120- # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
121- # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
122- # interfere with the next link command; also delete a directory that is
123- # left behind by Apple's compiler. We do this before executing the actions.
124- rm -rf conftest.dSYM conftest_ipa8_conftest.oo
125- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
126- as_fn_set_status $ac_retval
127-
128-} # ac_fn_fc_try_link
129-
130 # ac_fn_c_check_func LINENO FUNC VAR
131 # ----------------------------------
132 # Tests whether FUNC exists, setting the cache variable VAR accordingly
133@@ -2266,6 +2220,52 @@
134
135 } # ac_fn_c_check_func
136
137+# ac_fn_fc_try_link LINENO
138+# ------------------------
139+# Try to link conftest.$ac_ext, and return whether this succeeded.
140+ac_fn_fc_try_link ()
141+{
142+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
143+ rm -f conftest.$ac_objext conftest$ac_exeext
144+ if { { ac_try="$ac_link"
145+case "(($ac_try" in
146+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
147+ *) ac_try_echo=$ac_try;;
148+esac
149+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
150+$as_echo "$ac_try_echo"; } >&5
151+ (eval "$ac_link") 2>conftest.err
152+ ac_status=$?
153+ if test -s conftest.err; then
154+ grep -v '^ *+' conftest.err >conftest.er1
155+ cat conftest.er1 >&5
156+ mv -f conftest.er1 conftest.err
157+ fi
158+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
159+ test $ac_status = 0; } && {
160+ test -z "$ac_fc_werror_flag" ||
161+ test ! -s conftest.err
162+ } && test -s conftest$ac_exeext && {
163+ test "$cross_compiling" = yes ||
164+ $as_test_x conftest$ac_exeext
165+ }; then :
166+ ac_retval=0
167+else
168+ $as_echo "$as_me: failed program was:" >&5
169+sed 's/^/| /' conftest.$ac_ext >&5
170+
171+ ac_retval=1
172+fi
173+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
174+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
175+ # interfere with the next link command; also delete a directory that is
176+ # left behind by Apple's compiler. We do this before executing the actions.
177+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
178+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
179+ as_fn_set_status $ac_retval
180+
181+} # ac_fn_fc_try_link
182+
183 # ac_fn_fc_try_run LINENO
184 # -----------------------
185 # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
186@@ -11791,6 +11791,7 @@
187 fi
188
189
190+
191 ac_ext=c
192 ac_cpp='$CPP $CPPFLAGS'
193 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
194@@ -11857,42 +11858,7 @@
195 as_fn_error $? "Could not link in the zoltan library... exiting " "$LINENO" 5
196 fi
197
198-
199-# Small test for zoltan .mod files:
200-ac_ext=${ac_fc_srcext-f}
201-ac_compile='$FC -c $FCFLAGS $ac_fcflags_srcext conftest.$ac_ext >&5'
202-ac_link='$FC -o conftest$ac_exeext $FCFLAGS $LDFLAGS $ac_fcflags_srcext conftest.$ac_ext $LIBS >&5'
203-ac_compiler_gnu=$ac_cv_fc_compiler_gnu
204-
205-ac_ext=F90
206-# In fluidity's makefile we explicitly add CPPFLAGS, temporarily add it to
207-# FCFLAGS here for this zoltan test:
208-tmpFCFLAGS="$FCFLAGS"
209-FCFLAGS="$FCFLAGS $CPPFLAGS"
210-cat > conftest.$ac_ext <<_ACEOF
211-
212-program test_zoltan
213- use zoltan
214-end program test_zoltan
215-
216-_ACEOF
217-if ac_fn_fc_try_link "$LINENO"; then :
218-
219-{ $as_echo "$as_me:${as_lineno-$LINENO}: Great success! Zoltan .mod files exist and are usable" >&5
220-$as_echo "$as_me: Great success! Zoltan .mod files exist and are usable" >&6;}
221-
222-else
223-
224-cp conftest.F90 test_zoltan.F90
225-{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
226-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
227-as_fn_error $? "Failed to find zoltan.mod files
228-See \`config.log' for more details" "$LINENO" 5; }
229-fi
230-rm -f core conftest.err conftest.$ac_objext \
231- conftest$ac_exeext conftest.$ac_ext
232-# And now revert FCFLAGS
233-FCFLAGS="$tmpFCFLAGS"
234+# Save variables...
235 ac_ext=c
236 ac_cpp='$CPP $CPPFLAGS'
237 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
238@@ -12935,6 +12901,8 @@
239 as_fn_error $? "You need to set PETSC_DIR to point at your PETSc installation... exiting " "$LINENO" 5
240 fi
241
242+
243+
244 PETSC_LINK_LIBS=`make -s -f petsc_makefile getlinklibs`
245 LIBS="$PETSC_LINK_LIBS $LIBS"
246
247@@ -13004,7 +12972,7 @@
248 # petsc tutorial - using the headers in the same way as we do in the code
249 cat > conftest.$ac_ext <<_ACEOF
250
251-program test_petsc
252+program test
253 #include "petscversion.h"
254 #ifdef HAVE_PETSC_MODULES
255 use petsc
256@@ -13111,7 +13079,7 @@
257 call MatDestroy(A,ierr)
258
259 call PetscFinalize(ierr)
260-end program test_petsc
261+end program test
262
263 _ACEOF
264 if ac_fn_fc_try_link "$LINENO"; then :
265@@ -13121,7 +13089,7 @@
266
267 else
268
269-cp conftest.F90 test_petsc.F90
270+cp conftest.F90 test.F90
271 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
272 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
273 as_fn_error $? "Failed to compile and link PETSc program.
274
275=== added file 'examples/backward_facing_step_3d/Ercoftac-test31-BFS/readme'
276--- examples/backward_facing_step_3d/Ercoftac-test31-BFS/readme 1970-01-01 00:00:00 +0000
277+++ examples/backward_facing_step_3d/Ercoftac-test31-BFS/readme 2012-09-04 15:00:37 +0000
278@@ -0,0 +1,28 @@
279+1) File name convention: filename.nnn
280+ nnn = streamwise nodal (cell center) location
281+
282+ nnn = 181 ---> x/h = -3.0
283+ nnn = 360 ---> x/h = 4.0
284+ nnn = 411 ---> x/h = 6.0
285+ nnn = 513 ---> x/h = 10.0
286+ nnn = 641 ---> x/h = 15.0
287+ nnn = 744 ---> x/h = 19.0
288+
289+2) Files "x.nnn" contain U, V, u'(rms), v'(rms), w'(rms), u'v'.
290+ File header explains the data columns.
291+
292+ All quantities are nomalized to U0, where U0 is the mean inlet
293+ free stream velocity.
294+
295+3) Files "r**.nnn" contain Reynolds stress budgets.
296+
297+ r11.nnn ---> Streamwise component (u'u')
298+ r22.nnn ---> Vertical component (v'v')
299+ r33.nnn ---> Spanwise component (w'w')
300+ r12.nnn ---> Reynolds shear stress component (u'v')
301+ rkk.nnn ---> Turbulent kinetic energy
302+
303+ All quantities are nomalized to U0^3/h.
304+
305+4) File "stat.info" contains additional information at each streamwise
306+ location (u_tau, Cf, etc...).
307
308=== modified file 'examples/backward_facing_step_3d/Makefile'
309--- examples/backward_facing_step_3d/Makefile 2012-07-27 08:10:27 +0000
310+++ examples/backward_facing_step_3d/Makefile 2012-09-04 15:00:37 +0000
311@@ -39,7 +39,7 @@
312 ./postprocessor_3d.py
313
314 input: clean
315- $(MAKE) preprocess NPROCS=8
316+ $(MAKE) preprocess
317
318 clean:
319 @echo **********Cleaning the output from previous fluidity runs:
320
321=== modified file 'examples/hokkaido-nansei-oki_tsunami/hokkaido-nansei-oki_tsunami.xml'
322--- examples/hokkaido-nansei-oki_tsunami/hokkaido-nansei-oki_tsunami.xml 2012-08-22 21:21:08 +0000
323+++ examples/hokkaido-nansei-oki_tsunami/hokkaido-nansei-oki_tsunami.xml 2012-09-04 15:00:37 +0000
324@@ -2,8 +2,8 @@
325 <testproblem>
326 <name>Monai Valley</name>
327 <owner userid="sf1409"/>
328- <problem_definition length="long" nprocs="4">
329- <command_line>fluidity MonaiValley_C_p1p1_nu0.01_kmkstab_drag0.002_butcircularoundisland0.2.flml</command_line>
330+ <problem_definition length="special" nprocs="4">
331+ <command_line>fluidity MonaiValley_C.flml</command_line>
332 </problem_definition>
333 <variables>
334 <variable name="gage_integral_error" language="python">import tools
335
336=== modified file 'examples/restratification_after_oodc/Makefile'
337--- examples/restratification_after_oodc/Makefile 2012-07-25 15:27:53 +0000
338+++ examples/restratification_after_oodc/Makefile 2012-09-04 15:00:37 +0000
339@@ -40,7 +40,7 @@
340 @echo **********No postprocessing needed
341
342 input: clean
343- $(MAKE) preprocess NPROCS=32
344+ $(MAKE) preprocess NPROCS=8
345
346 clean:
347 @echo **********Cleaning the output from previous fluidity runs:
348
349=== modified file 'examples/tephra_settling/tephra_settling.flml'
350--- examples/tephra_settling/tephra_settling.flml 2012-08-09 22:08:12 +0000
351+++ examples/tephra_settling/tephra_settling.flml 2012-09-04 15:00:37 +0000
352@@ -586,9 +586,4 @@
353 </particle_diameter>
354 </multiphase_properties>
355 </material_phase>
356- <multiphase_interaction>
357- <fluid_particle_drag>
358- <drag_correlation name="stokes"/>
359- </fluid_particle_drag>
360- </multiphase_interaction>
361 </fluidity_options>
362
363=== modified file 'examples/tephra_settling/tephra_settling.xml'
364--- examples/tephra_settling/tephra_settling.xml 2012-08-19 00:21:26 +0000
365+++ examples/tephra_settling/tephra_settling.xml 2012-09-04 15:00:37 +0000
366@@ -20,18 +20,11 @@
367 s = stat_parser("tephra_settling.stat")
368 tephra_u_max=s["Tephra"]["Velocity%magnitude"]["max"]
369 </variable>
370-
371 <variable name="time" language="python">
372 from fluidity_tools import stat_parser
373 s = stat_parser("tephra_settling.stat")
374 time=s["ElapsedTime"]["value"]
375 </variable>
376-
377- <variable name="solvers_converged" language="python">
378-import os
379-files = os.listdir("./")
380-solvers_converged = not "matrixdump" in files and not "matrixdump.info" in files
381- </variable>
382 </variables>
383
384 <pass_tests>
385@@ -49,10 +42,6 @@
386 assert max(tephra_u_max[t:]) &lt; 0.05
387 break
388 </test>
389-
390- <test name="Solvers converged" language="python">
391-assert(solvers_converged)
392- </test>
393 </pass_tests>
394
395 <warn_tests>
396
397=== modified file 'examples/top_hat/top_hat.xml'
398--- examples/top_hat/top_hat.xml 2012-07-25 12:38:28 +0000
399+++ examples/top_hat/top_hat.xml 2012-09-04 15:00:37 +0000
400@@ -2,7 +2,7 @@
401 <testproblem>
402 <name>tophat</name>
403 <owner userid="skramer"/>
404- <problem_definition length="long" nprocs="1">
405+ <problem_definition length="medium" nprocs="1">
406 <command_line>fluidity -v2 -l top_hat_cg.flml &amp;&amp;
407 fluidity -v2 -l top_hat_dg.flml &amp;&amp;
408 fluidity -v2 -l top_hat_cv.flml</command_line>
409
410=== modified file 'femtools/Diagnostic_Fields.F90'
411--- femtools/Diagnostic_Fields.F90 2012-08-29 18:09:06 +0000
412+++ femtools/Diagnostic_Fields.F90 2012-09-04 15:00:37 +0000
413@@ -477,7 +477,7 @@
414 type(scalar_field), intent(inout) :: grn
415
416 type(vector_field), pointer :: U, X
417- integer :: ele, gi, stat, a, b
418+ integer :: ele, gi, stat
419 ! Transformed quadrature weights.
420 real, dimension(ele_ngi(GRN, 1)) :: detwei
421 ! Inverse of the local coordinate change matrix.
422@@ -525,24 +525,6 @@
423 ! The matmul is as given by dham
424 GRN_q=ele_val_at_quad(U, ele)
425 vis_q=ele_val_at_quad(viscosity, ele)
426-
427- ! for full and partial stress form we need to set the off diagonal terms of the viscosity tensor to zero
428- ! to be able to invert it
429- if (have_option(trim(U%option_path)//&
430- &"/prognostic/spatial_discretisation/continuous_galerkin"//&
431- &"/stress_terms/stress_form") .or. &
432- have_option(trim(U%option_path)//&
433- &"/prognostic/spatial_discretisation/continuous_galerkin"//&
434- &"/stress_terms/partial_stress_form")) then
435-
436- do a=1,size(vis_q,1)
437- do b=1,size(vis_q,2)
438- if(a.eq.b) cycle
439- vis_q(a,b,:) = 0.0
440- end do
441- end do
442- end if
443-
444 do gi=1, size(detwei)
445 GRN_q(:,gi)=matmul(GRN_q(:,gi), J(:,:,gi))
446 GRN_q(:,gi)=matmul(inverse(vis_q(:,:,gi)), GRN_q(:,gi))
447
448=== modified file 'femtools/Halos_Communications.F90'
449--- femtools/Halos_Communications.F90 2012-08-10 16:03:41 +0000
450+++ femtools/Halos_Communications.F90 2012-09-04 15:00:37 +0000
451@@ -765,8 +765,6 @@
452
453 type(halo_type), intent(in) :: halo
454 real, dimension(:), intent(in) :: real_array
455-
456- real :: epsl
457
458 logical :: verifies
459
460@@ -780,16 +778,14 @@
461
462 call halo_update(halo, lreal_array)
463
464- epsl = spacing( maxval( abs( lreal_array ))) * 10000.
465- call allmax(epsl)
466-
467- verifies = all(abs(real_array - lreal_array) < epsl )
468+ verifies = all(abs(real_array - lreal_array) < 10000.0 * max(spacing(lreal_array), epsilon(lreal_array)))
469 #ifdef DDEBUG
470 if(.not. verifies) then
471 do i = 1, halo_proc_count(halo)
472 do j = 1, halo_receive_count(halo, i)
473 receive = halo_receive(halo, i, j)
474- if(abs(real_array(receive) - lreal_array(receive)) >= epsl) then
475+ if(abs(real_array(receive) - lreal_array(receive)) >= 10000.0&
476+ &*max(spacing(real_array(receive)), epsilon(real_array(receive)))) then
477 ewrite(0, *) "Warning: Halo receive ", receive, " for halo " // halo_name(halo) // " failed verification"
478 ewrite(0, *) "Reference = ", real_array(receive)
479 ewrite(0, *) "Value in verification array = ", lreal_array(receive)
480@@ -798,12 +794,13 @@
481 end do
482
483 do i = 1, size(real_array)
484- if(abs(real_array(i) - lreal_array(i)) >= epsl ) then
485- ewrite(0, *) "Warning: Reference index ", i, " for halo " // halo_name(halo) // " failed verification"
486- ewrite(0, *) "Reference = ", real_array(i)
487- ewrite(0, *) "Value in verification array = ", lreal_array(i)
488- end if
489- end do
490+ if(abs(real_array(i) - lreal_array(i)) >= 10000.0&
491+ &*max(spacing(real_array(i)), epsilon(real_array(i)))) then
492+ ewrite(0, *) "Warning: Reference index ", i, " for halo " // halo_name(halo) // " failed verification"
493+ ewrite(0, *) "Reference = ", real_array(i)
494+ ewrite(0, *) "Value in verification array = ", lreal_array(i)
495+ end if
496+ end do
497 end if
498 #endif
499
500
501=== modified file 'femtools/Makefile.dependencies'
502--- femtools/Makefile.dependencies 2012-05-29 13:43:29 +0000
503+++ femtools/Makefile.dependencies 2012-09-04 15:00:37 +0000
504@@ -1192,18 +1192,6 @@
505
506 Vector_set.o ../include/vector_set.mod: Vector_set.F90
507
508-../include/vertical_extrapolation_module.mod: Vertical_Extrapolation.o
509- @true
510-
511-Vertical_Extrapolation.o ../include/vertical_extrapolation_module.mod: \
512- Vertical_Extrapolation.F90 ../include/boundary_conditions.mod \
513- ../include/dynamic_bin_sort_module.mod ../include/elements.mod \
514- ../include/fdebug.h ../include/fields.mod ../include/fldebug.mod \
515- ../include/global_parameters.mod ../include/integer_set_module.mod \
516- ../include/parallel_tools.mod ../include/pickers.mod \
517- ../include/sparse_tools.mod ../include/spud.mod ../include/state_module.mod \
518- ../include/transform_elements.mod ../include/vtk_interfaces.mod
519-
520 ../include/wandzura_quadrature.mod: Wandzura_Quadrature.o
521 @true
522
523
524=== modified file 'femtools/Makefile.in'
525--- femtools/Makefile.in 2012-06-08 20:28:35 +0000
526+++ femtools/Makefile.in 2012-09-04 15:00:37 +0000
527@@ -104,7 +104,7 @@
528 CGAL_Tools_C.o CGAL_Tools.o \
529 Rotated_Boundary_Conditions.o MPI_Interfaces.o Parallel_Tools.o \
530 Fields_Halos.o Profiler.o Profiler_Fortran.o Streamfunction.o \
531- GMSH_Common.o Read_GMSH.o Write_GMSH.o Mesh_Files.o Vertical_Extrapolation.o
532+ GMSH_Common.o Read_GMSH.o Write_GMSH.o Mesh_Files.o
533
534
535 # objects to be included in libfemtools:
536
537=== modified file 'femtools/Parallel_fields.F90'
538--- femtools/Parallel_fields.F90 2012-08-13 12:39:57 +0000
539+++ femtools/Parallel_fields.F90 2012-09-04 15:00:37 +0000
540@@ -284,11 +284,8 @@
541 !!< Return .true. if ELEMENT_NUMBER has a neighbour in MESH that
542 !!< is owned by this process otherwise .false.
543
544- !! Note, you cannot use this function to compute if
545- !! ELEMENT_NUMBER is owned. Imagine if this is the only owned
546- !! element on a process, then none of the neighbours will be owned.
547- !! You can use this function to compute whether ELEMENT_NUMBER is
548- !! in the L1 element halo.
549+ !! Effectively, this computes whether ELEMENT_NUMBER is in the L1
550+ !! halo.
551 type(mesh_type), intent(in) :: mesh
552 integer, intent(in) :: element_number
553 logical :: owned
554@@ -314,11 +311,8 @@
555 !!< Return .true. if ELEMENT_NUMBER has a neighbour in FIELD that
556 !!< is owned by this process otherwise .false.
557
558- !! Note, you cannot use this function to compute if
559- !! ELEMENT_NUMBER is owned. Imagine if this is the only owned
560- !! element on a process, then none of the neighbours will be owned.
561- !! You can use this function to compute whether ELEMENT_NUMBER is
562- !! in the L1 element halo.
563+ !! Effectively, this computes whether ELEMENT_NUMBER is in the L1
564+ !! halo.
565 type(scalar_field), intent(in) :: field
566 integer, intent(in) :: element_number
567 logical :: owned
568@@ -330,11 +324,8 @@
569 !!< Return .true. if ELEMENT_NUMBER has a neighbour in FIELD that
570 !!< is owned by this process otherwise .false.
571
572- !! Note, you cannot use this function to compute if
573- !! ELEMENT_NUMBER is owned. Imagine if this is the only owned
574- !! element on a process, then none of the neighbours will be owned.
575- !! You can use this function to compute whether ELEMENT_NUMBER is
576- !! in the L1 element halo.
577+ !! Effectively, this computes whether ELEMENT_NUMBER is in the L1
578+ !! halo.
579 type(vector_field), intent(in) :: field
580 integer, intent(in) :: element_number
581 logical :: owned
582@@ -346,11 +337,8 @@
583 !!< Return .true. if ELEMENT_NUMBER has a neighbour in FIELD that
584 !!< is owned by this process otherwise .false.
585
586- !! Note, you cannot use this function to compute if
587- !! ELEMENT_NUMBER is owned. Imagine if this is the only owned
588- !! element on a process, then none of the neighbours will be owned.
589- !! You can use this function to compute whether ELEMENT_NUMBER is
590- !! in the L1 element halo.
591+ !! Effectively, this computes whether ELEMENT_NUMBER is in the L1
592+ !! halo.
593 type(tensor_field), intent(in) :: field
594 integer, intent(in) :: element_number
595 logical :: owned
596
597=== removed file 'femtools/Vertical_Extrapolation.F90'
598--- femtools/Vertical_Extrapolation.F90 2012-05-29 13:43:29 +0000
599+++ femtools/Vertical_Extrapolation.F90 1970-01-01 00:00:00 +0000
600@@ -1,1290 +0,0 @@
601-! Copyright (C) 2007 Imperial College London and others.
602-!
603-! Please see the AUTHORS file in the main source directory for a full list
604-! of copyright holders.
605-!
606-! Prof. C Pain
607-! Applied Modelling and Computation Group
608-! Department of Earth Science and Engineering
609-! Imperial College London
610-!
611-! amcgsoftware@imperial.ac.uk
612-!
613-! This library is free software; you can redistribute it and/or
614-! modify it under the terms of the GNU Lesser General Public
615-! License as published by the Free Software Foundation,
616-! version 2.1 of the License.
617-!
618-! This library is distributed in the hope that it will be useful,
619-! but WITHOUT ANY WARRANTY; without even the implied warranty of
620-! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
621-! Lesser General Public License for more details.
622-!
623-! You should have received a copy of the GNU Lesser General Public
624-! License along with this library; if not, write to the Free Software
625-! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
626-! USA
627-
628-
629-#include "confdefs.h"
630-#include "fdebug.h"
631-
632-module vertical_extrapolation_module
633-!!< Module containing routines for vertical extrapolation on
634-!!< fully unstructured 3D meshes. Also contains routines for updating
635-!!< distance to top and bottom fields.
636-use fldebug
637-use elements
638-use sparse_tools
639-use fields
640-use state_module
641-use transform_elements
642-use boundary_conditions
643-use parallel_tools
644-use global_parameters, only: real_4, real_8
645-use spud
646-use dynamic_bin_sort_module
647-use pickers
648-use integer_set_module
649-use vtk_interfaces
650-implicit none
651-
652-interface VerticalExtrapolation
653- module procedure VerticalExtrapolationScalar, &
654- VerticalExtrapolationMultiple, VerticalExtrapolationVector
655-end interface VerticalExtrapolation
656-
657-interface vertical_integration
658- module procedure vertical_integration_scalar, &
659- vertical_integration_multiple, vertical_integration_vector
660-end interface vertical_integration
661-
662-!! element i is considered to be above j iff the inner product of the face
663-!! normal point from i to j and the gravity normal is bigger than this
664-real, parameter:: VERTICAL_INTEGRATION_EPS=1.0e-8
665-
666-! the two hemispheres are both projected to the unit circle, but the projected southern
667-! hemisphere will have its origin shifted. The projected hemispheres are slightly bigger
668-! than the unit circle, as a bit of overlap with the opposite hemisphere is included around
669-! the equator. So this shift needs to be big enough to not make both projected hemispheres
670-! overlap
671-real, parameter:: HEMISPHERE_SHIFT=10.0
672-
673-private
674-
675-public CalculateTopBottomDistance
676-public VerticalExtrapolation, vertical_integration
677-public vertical_element_ordering
678-public VerticalProlongationOperator
679-public vertical_extrapolation_module_check_options
680-public compute_face_normal_gravity
681-
682-contains
683-
684-subroutine UpdateDistanceField(state, name, vertical_coordinate)
685- ! This sub calculates the vertical distance to the free surface
686- ! and bottom of the ocean to all nodes. The results are stored
687- ! in the 'DistanceToBottom/FreeSurface' fields from state.
688- type(state_type), intent(inout):: state
689- character(len=*), intent(in):: name
690- type(scalar_field), intent(in):: vertical_coordinate
691-
692- ! Local variables
693- type(vector_field), pointer:: positions, vertical_normal
694- type(scalar_field), pointer:: distance
695-
696- integer, pointer, dimension(:):: surface_element_list
697-
698- ! the distance field to compute:
699- distance => extract_scalar_field(state, name)
700- ! its first boundary condition is on the related top or bottom mesh
701- call get_boundary_condition(distance, 1, &
702- surface_element_list=surface_element_list)
703- positions => extract_vector_field(state, "Coordinate")
704- vertical_normal => extract_vector_field(state, "GravityDirection")
705-
706- ! in each node of the mesh, set "distance" to the vertical coordinate
707- ! of this node projected to the above/below surface mesh
708- call VerticalExtrapolation(vertical_coordinate, distance, positions, &
709- vertical_normal, surface_element_list, surface_name=name)
710-
711- ! the distance is then calculated by subtracting its own vertical coordinate
712- call addto(distance, vertical_coordinate, scale=-1.0)
713-
714- if (name=="DistanceToBottom") then
715- ! make distance to bottom positive
716- call scale(distance, -1.0)
717- end if
718-
719-end subroutine UpdateDistanceField
720-
721-subroutine CalculateTopBottomDistance(state)
722- !! This sub calculates the vertical distance to the free surface
723- !! and bottom of the ocean to all nodes. The results are stored
724- !! in the 'DistanceToBottom/Top' fields from state.
725- type(state_type), intent(inout):: state
726-
727- type(mesh_type), pointer:: xmesh
728- type(scalar_field):: vertical_coordinate
729-
730- xmesh => extract_mesh(state, "CoordinateMesh")
731- call allocate(vertical_coordinate, xmesh, "VerticalCoordinate")
732- call calculate_vertical_coordinate(state, vertical_coordinate)
733- call UpdateDistanceField(state, "DistanceToTop", vertical_coordinate)
734- call UpdateDistanceField(state, "DistanceToBottom", vertical_coordinate)
735- call deallocate(vertical_coordinate)
736-
737-end subroutine CalculateTopBottomDistance
738-
739-subroutine calculate_vertical_coordinate(state, vertical_coordinate)
740- !! Computes a vertical coordinate, i.e. a scalar field such that
741- !! for each 2 nodes above each other, the difference of the field
742- !! in these nodes gives the distance between them.
743- type(state_type), intent(inout):: state
744- type(scalar_field), intent(inout):: vertical_coordinate
745-
746- type(vector_field), pointer:: positions, gravity_normal
747- type(scalar_field):: positions_magnitude
748-
749- positions => extract_vector_field(state, "Coordinate")
750- if(have_option('/geometry/spherical_earth')) then
751- ! use the radius as vertical coordinate
752- ! that is, the l2-norm of the coordinate field
753- positions_magnitude=magnitude(positions)
754- call set(vertical_coordinate, positions_magnitude)
755- call deallocate(positions_magnitude)
756- else
757- gravity_normal => extract_vector_field(state, "GravityDirection")
758- assert(gravity_normal%field_type==FIELD_TYPE_CONSTANT)
759- call inner_product(vertical_coordinate, gravity_normal, positions)
760- ! gravity points down, we want a vertical coordinate that increases upward
761- call scale(vertical_coordinate, -1.0)
762- end if
763-
764-end subroutine calculate_vertical_coordinate
765-
766-subroutine VerticalExtrapolationScalar(from_field, to_field, &
767- positions, vertical_normal, surface_element_list, surface_name)
768- !!< This sub extrapolates the values on a horizontal 2D surface
769- !!< in the vertical direction to 3D fields
770- !! The from_fields should be 3D fields of which only the values on the
771- !! 2D horizontal surface are used.
772- type(scalar_field), intent(in):: from_field
773- !! Resulting extrapolated field. May be the same field or a field on
774- !! a different mesh (different degree).
775- type(scalar_field), intent(inout):: to_field
776- !! positions, and upward normal vector on the whole domain
777- type(vector_field), target, intent(inout):: positions
778- type(vector_field), target, intent(in):: vertical_normal
779- !! the surface elements (faces numbers) that make up the surface
780- integer, dimension(:), intent(in):: surface_element_list
781- !! If provided the projected surface mesh onto horizontal coordinates
782- !! and its associated rtree/pickers are cached under this name and
783- !! attached to the 'positions'. In this case when called again with
784- !! the same 'positions' and the same surface_name,
785- !! the same surface_element_list should again be provided.
786- character(len=*), optional, intent(in):: surface_name
787-
788- type(scalar_field), dimension(1):: to_fields
789-
790- to_fields=(/ to_field /)
791- call VerticalExtrapolationMultiple( (/ from_field /) , to_fields, &
792- positions, vertical_normal, surface_element_list, &
793- surface_name=surface_name)
794-
795-end subroutine VerticalExtrapolationScalar
796-
797-subroutine VerticalExtrapolationVector(from_field, to_field, &
798- positions, vertical_normal, surface_element_list, surface_name)
799- !!< This sub extrapolates the values on a horizontal 2D surface
800- !!< in the vertical direction to 3D fields
801- !! The from_fields should be 3D fields of which only the values on the
802- !! 2D horizontal surface are used.
803- type(vector_field), intent(in):: from_field
804- !! Resulting extrapolated field. May be the same field or a field on
805- !! a different mesh (different degree).
806- type(vector_field), intent(inout):: to_field
807- !! positions, and upward normal vector on the whole domain
808- type(vector_field), target, intent(inout):: positions
809- type(vector_field), target, intent(in):: vertical_normal
810- !! the surface elements (faces numbers) that make up the surface
811- integer, dimension(:), intent(in):: surface_element_list
812- !! If provided the projected surface mesh onto horizontal coordinates
813- !! and its associated rtree/pickers are cached under this name and
814- !! attached to the 'positions'. In this case when called again with
815- !! the same 'positions' and the same surface_name,
816- !! the same surface_element_list should again be provided.
817- character(len=*), optional, intent(in):: surface_name
818-
819- type(scalar_field), dimension(from_field%dim):: from_field_components, to_field_components
820- integer i
821-
822- assert(from_field%dim==to_field%dim)
823-
824- do i=1, from_field%dim
825- from_field_components(i)=extract_scalar_field(from_field, i)
826- to_field_components(i)=extract_scalar_field(to_field, i)
827- end do
828-
829- call VerticalExtrapolationMultiple( from_field_components, to_field_components, &
830- positions, vertical_normal, surface_element_list, &
831- surface_name=surface_name)
832-
833-end subroutine VerticalExtrapolationVector
834-
835-subroutine VerticalExtrapolationMultiple(from_fields, to_fields, &
836- positions, vertical_normal, surface_element_list, surface_name)
837- !!< This sub extrapolates the values on a horizontal 2D surface
838- !!< in the vertical direction to 3D fields
839- !! The from_fields should be 3D fields of which only the values on the
840- !! 2D horizontal surface are used.
841- !! This version takes multiple from_fields at the same time and extrapolates
842- !! to to_fields, such that the surface search only has to be done once. This
843- !! will only work if all the from_fields are on the same mesh, and on all the
844- !! to_field are on the same (possibly a different) mesh.
845- type(scalar_field), dimension(:), intent(in):: from_fields
846- !! Resulting extrapolated field. May be the same field or a field on
847- !! a different mesh (different degree).
848- type(scalar_field), dimension(:), intent(inout):: to_fields
849- !! positions, and upward normal vector on the whole domain
850- type(vector_field), target, intent(inout):: positions
851- type(vector_field), target, intent(in):: vertical_normal
852-
853- !! the surface elements (faces numbers) that make up the surface
854- integer, dimension(:), intent(in):: surface_element_list
855- !! If provided the projected surface mesh onto horizontal coordinates
856- !! and its associated rtree/pickers are cached under this name and
857- !! attached to the 'positions'. In this case when called again with
858- !! the same 'positions' and the same surface_name,
859- !! the same surface_element_list should again be provided.
860- character(len=*), optional, intent(in):: surface_name
861-
862- character(len=FIELD_NAME_LEN):: lsurface_name
863- real, dimension(:,:), allocatable:: loc_coords
864- integer, dimension(:), allocatable:: seles
865- integer i, j, to_nodes, face
866-
867- assert(size(from_fields)==size(to_fields))
868- assert(element_count(from_fields(1))==element_count(to_fields(1)))
869- do i=2, size(to_fields)
870- assert(to_fields(1)%mesh==to_fields(i)%mesh)
871- assert(from_fields(1)%mesh==from_fields(i)%mesh)
872- end do
873-
874- to_nodes=nowned_nodes(to_fields(1))
875- ! local coordinates is one more than horizontal coordinate dim
876- allocate( seles(to_nodes), loc_coords(1:positions%dim, 1:to_nodes) )
877-
878- if (present(surface_name)) then
879- lsurface_name=surface_name
880- else
881- lsurface_name="TempSurfaceName"
882- end if
883-
884- ! project the positions of to_fields(1)%mesh into the horizontal plane
885- ! and returns 'seles' (indices in surface_element_list) and loc_coords
886- ! to tell where these projected nodes are found in the surface mesh
887- call horizontal_picker(to_fields(1)%mesh, positions, vertical_normal, &
888- surface_element_list, lsurface_name, &
889- seles, loc_coords)
890-
891- ! interpolate using the returned faces and loc_coords
892- do i=1, size(to_fields)
893- do j=1, to_nodes
894- face=surface_element_list(seles(j))
895- call set(to_fields(i), j, &
896- dot_product( eval_shape( face_shape(from_fields(i), face), loc_coords(:,j) ), &
897- face_val( from_fields(i), face ) ))
898- end do
899- end do
900-
901- if (IsParallel()) then
902- do i=1, size(to_fields)
903- call halo_update(to_fields(i))
904- end do
905- end if
906-
907- if (.not. present(surface_name)) then
908- call remove_boundary_condition(positions, "TempSurfaceName")
909- end if
910-
911-end subroutine VerticalExtrapolationMultiple
912-
913-subroutine horizontal_picker(mesh, positions, vertical_normal, &
914- surface_element_list, surface_name, &
915- seles, loc_coords)
916- !! Searches the nodes of 'mesh' in the surface mesh above.
917- !! Returns the surface elements 'seles' that each node lies under
918- !! and the loc_coords in this element of this node projected
919- !! upward (radially on the sphere) onto the surface mesh
920-
921- !! mesh
922- type(mesh_type), intent(in):: mesh
923- !! a valid positions field for the whole domain, not necessarily on 'mesh'
924- !! for instance in a periodic domain, mesh is periodic and positions should not be
925- type(vector_field), target, intent(inout):: positions
926- !! upward normal vector on the whole domain
927- type(vector_field), target, intent(in):: vertical_normal
928- !! the surface elements (faces numbers) that make up the surface
929- integer, dimension(:), intent(in):: surface_element_list
930- !! The projected surface mesh onto horizontal coordinates
931- !! and its associated rtree/pickers are cached under this name and
932- !! attached to the 'positions'. When called again with
933- !! the same 'positions' and the same surface_name,
934- !! the same surface_element_list should again be provided.
935- character(len=*), intent(in):: surface_name
936-
937- !! returned surface elements (face numbers in 'mesh')
938- !! and loc coords each node has been found in
939- !! size(seles)==size(loc_coords,2)==nowned_nodes(mesh)
940- integer, dimension(:), intent(out):: seles
941- real, dimension(:,:), intent(out):: loc_coords
942-
943- type(vector_field):: mesh_positions
944- type(vector_field), pointer:: horizontal_positions
945- integer, dimension(:), pointer:: horizontal_mesh_list
946- real, dimension(:,:), allocatable:: horizontal_coordinate
947- real, dimension(vertical_normal%dim):: normal_vector
948- real, dimension(positions%dim):: xyz
949- integer:: i, stat, nodes
950-
951- assert(.not. mesh_periodic(positions))
952-
953- ! search only the first owned nodes
954- nodes=nowned_nodes(mesh)
955-
956- assert( size(seles)==nodes )
957- assert(size(loc_coords,1)==positions%dim)
958- assert( size(loc_coords,2)==nodes )
959-
960- if (mesh==positions%mesh) then
961- mesh_positions=positions
962- ! make mesh_positions indep. ref. of the field, so we can deallocate it
963- ! safely without destroying positions
964- call incref(mesh_positions)
965- else
966- call allocate(mesh_positions, positions%dim, mesh, &
967- name='ToPositions_VerticalExtrapolation')
968- call remap_field(positions, mesh_positions, stat)
969- if (stat/=0 .and. stat/=REMAP_ERR_HIGHER_LOWER_CONTINUOUS .and. &
970- stat/=REMAP_ERR_UNPERIODIC_PERIODIC) then
971- ! Mapping from higher order to lower order is allowed for coordinates
972- ! (well depends on how the higher order is derived from the lower order)
973- !
974- ! Mapping to periodic coordinates is ok in this case as we only need
975- ! locations for the nodes individually (i.e. we don't care about elements
976- ! in 'mesh') - the created horizontal_positions will be non-periodic
977- ! So that we should be able to find nodes on the periodic boundary on
978- ! either side. Using this to interpolate is consistent as long as
979- ! the interpolated from field is indeed periodic.
980- FLAbort("Unknown error in remmaping positions in horizontal_picker.")
981- end if
982- end if
983-
984-
985- ! create an array of the horizontally projected coordinates of the 'mesh' nodes
986- allocate( horizontal_coordinate(1:positions%dim-1, 1:nodes) )
987- if (have_option('/geometry/spherical_earth')) then
988- assert(mesh_positions%dim==3)
989- do i=1, nodes
990- xyz=node_val(mesh_positions, i)
991- if (xyz(3)>0.0) then
992- horizontal_coordinate(:,i) = &
993- map2horizontal_sphere( node_val(mesh_positions, i), +1.0)
994- else
995- horizontal_coordinate(:,i) = &
996- map2horizontal_sphere( node_val(mesh_positions, i), -1.0)
997- horizontal_coordinate(1,i)=horizontal_coordinate(1,i)+HEMISPHERE_SHIFT
998- end if
999- end do
1000- else
1001- assert( vertical_normal%field_type==FIELD_TYPE_CONSTANT )
1002- normal_vector=node_val(vertical_normal,1)
1003-
1004- do i=1, nodes
1005- horizontal_coordinate(:,i) = &
1006- map2horizontal( node_val(mesh_positions, i), normal_vector )
1007- end do
1008- end if
1009-
1010- call get_horizontal_positions(positions, surface_element_list, &
1011- vertical_normal, surface_name, &
1012- horizontal_positions, horizontal_mesh_list)
1013-
1014- call picker_inquire( horizontal_positions, horizontal_coordinate, &
1015- seles, loc_coords, global=.false. )
1016-
1017- ! in the spherical case some of the surface elements may be duplicated
1018- ! within horizontal positions, the returned seles should refer to entries
1019- ! in surface_element_list however - also check for nodes not found
1020- do i=1, size(seles)
1021- if (seles(i)>0) then
1022- seles(i)=horizontal_mesh_list(seles(i))
1023- else
1024- ewrite(-1,*) "For node with coordinate", node_val(mesh_positions, i)
1025- ewrite(-1,*) "no top surface node was found."
1026- FLAbort("Something wrong with the geometry.")
1027- end if
1028- end do
1029-
1030- call deallocate(mesh_positions)
1031- deallocate(horizontal_mesh_list)
1032-
1033-end subroutine horizontal_picker
1034-
1035-subroutine get_horizontal_positions(positions, surface_element_list, vertical_normal, surface_name, &
1036- horizontal_positions, horizontal_mesh_list)
1037-! returns a horizontal positions field over the surface mesh indicated by
1038-! 'surface_element_list'. This field will be created and cached on 'positions'
1039-! as a surface field attached to a dummy boundary condition under the name surface_name
1040- type(vector_field), intent(inout):: positions
1041- type(vector_field), intent(in):: vertical_normal
1042- integer, dimension(:), intent(in):: surface_element_list
1043- character(len=*), intent(in):: surface_name
1044-
1045- ! Returns the horizontal positions on a horizontal mesh, this mesh
1046- ! may have some of the faces in surface_element_list duplicated -
1047- ! this is only in the spherical case where the horizontal mesh
1048- ! consists of two disjoint regions representing the two hemispheres
1049- ! and surface elements near the equator may be represented in both.
1050- ! Therefore we also return a map between elements in the horizontal positions mesh
1051- ! and entries in surface_element_list. If ele is an element in the horizontal
1052- ! position mesh, then surface_element_list(horizontal_mesh(ele))
1053- ! is the face number in the full mesh
1054- ! horizontal_positions is a borrowed reference, don't allocate
1055- ! horizontal_mesh_list does need to be deallocated
1056- type(vector_field), pointer:: horizontal_positions
1057- integer, dimension(:), pointer:: horizontal_mesh_list
1058-
1059- integer, dimension(:), pointer:: horizontal_sele_list
1060- integer:: i, j, k, sele
1061-
1062- if (.not. has_boundary_condition_name(positions, surface_name)) then
1063- if (have_option('/geometry/spherical_earth')) then
1064- call create_horizontal_positions_sphere(positions, &
1065- surface_element_list, surface_name)
1066- else
1067- call create_horizontal_positions_flat(positions, &
1068- surface_element_list, vertical_normal, surface_name)
1069- end if
1070- end if
1071-
1072- horizontal_positions => extract_surface_field(positions, surface_name, &
1073- trim(surface_name)//"HorizontalCoordinate")
1074-
1075-! call vtk_write_fields('horizontal_mesh', 0, horizontal_positions, &
1076-! horizontal_positions%mesh)
1077-
1078-
1079- allocate( horizontal_mesh_list(1:element_count(horizontal_positions)) )
1080- if (have_option('/geometry/spherical_earth')) then
1081- call get_boundary_condition(positions, surface_name, &
1082- surface_element_list=horizontal_sele_list)
1083- ! by construction the map can be obtained by running
1084- ! through surface_element_list twice (for each hemisphere)
1085- i=1 ! position in horizontal_sele_list
1086- sele=horizontal_sele_list(i) ! face we're looking for
1087-outer_loop: &
1088- do j=1, 2
1089- do k=1, size(surface_element_list)
1090- if (surface_element_list(k)==sele) then
1091- ! found matching position in surface_element_list
1092- horizontal_mesh_list(i)=k
1093- ! next one to search
1094- i=i+1
1095- if (i>size(horizontal_sele_list)) exit outer_loop
1096- sele=horizontal_sele_list(i)
1097- end if
1098- end do
1099- end do outer_loop
1100-
1101- if (i<=size(horizontal_sele_list)) then
1102- ! not all were found in 2 loops through surface_element_list
1103- ! something's wrong
1104- FLAbort("Internal error in horizontal mesh administration")
1105- end if
1106-
1107- else
1108- ! no duplication: horizontal_mesh_list is simply the identity map
1109- do i=1, size(horizontal_mesh_list)
1110- horizontal_mesh_list(i)=i
1111- end do
1112- end if
1113-
1114-end subroutine get_horizontal_positions
1115-
1116-subroutine create_horizontal_positions_flat(positions, surface_element_list, vertical_normal, surface_name)
1117-! adds a "boundary condition" to 'positions' with an associated vector surface field containing a dim-1
1118-! horizontal coordinate field that can be used to map from the surface mesh specified
1119-! by 'surface_element_list'. This "boundary condition" will be stored under the name 'surface_name'
1120-! The horizontal coordinates are created by projecting out the component
1121-! in the direction of 'vertical_normal' and then throwing out the x, y or z
1122-! coordinate that is most aligned with 'vertical_normal'
1123- type(vector_field), intent(inout):: positions
1124- type(vector_field), intent(in):: vertical_normal
1125- integer, dimension(:), intent(in):: surface_element_list
1126- character(len=*), intent(in):: surface_name
1127-
1128- type(mesh_type), pointer:: surface_mesh
1129- type(vector_field):: horizontal_positions
1130- integer, dimension(:), pointer:: surface_node_list
1131- real, dimension(vertical_normal%dim):: normal_vector
1132- integer:: i, node
1133-
1134- assert(vertical_normal%field_type==FIELD_TYPE_CONSTANT)
1135- normal_vector=node_val(vertical_normal, 1)
1136-
1137- call add_boundary_condition_surface_elements(positions, &
1138- name=surface_name, type="verticalextrapolation", &
1139- surface_element_list=surface_element_list)
1140- ! now get back the created surface mesh
1141- ! and surface_node_list a mapping between node nos in the projected mesh and node nos in the original 'positions' mesh
1142- call get_boundary_condition(positions, name=surface_name, &
1143- surface_mesh=surface_mesh, surface_node_list=surface_node_list)
1144- call allocate( horizontal_positions, dim=positions%dim-1, mesh=surface_mesh, &
1145- name=trim(surface_name)//"HorizontalCoordinate" )
1146-
1147- call insert_surface_field(positions, name=surface_name, &
1148- surface_field=horizontal_positions)
1149-
1150- do i=1, size(surface_node_list)
1151- node=surface_node_list(i)
1152- call set(horizontal_positions, i, &
1153- map2horizontal(node_val(positions, node), normal_vector))
1154- end do
1155-
1156- call deallocate( horizontal_positions )
1157-
1158-end subroutine create_horizontal_positions_flat
1159-
1160-subroutine create_horizontal_positions_sphere(positions, surface_element_list, surface_name)
1161-! adds a "boundary condition" to 'positions' with an associated vector surface field containing a dim-1
1162-! horizontal coordinate field that can be used to map from the surface mesh specified
1163-! by 'surface_element_list'. This "boundary condition" will be stored under the name 'surface_name'
1164-! The horizontal coordinates are created by a stereographic projection from the unit sphere to the
1165-! xy-plane. The northern hemisphere is projected to the unit-circle (including some extra surface
1166-! elements on the southern hemisphere around the equator). The southern hemisphere (including some
1167-! northern surface elements near the equator) is also projected to a unit circle but translated
1168-! away from the origin to not overlap with the projected northern hemisphere. Thus the created
1169-! projected horizontal positions field consists of two disjoint areas in the plane corresponding
1170-! to both hemispheres, and elements in the original surface mesh (near the equator) may appear
1171-! twice in the projected field.
1172- type(vector_field), intent(inout):: positions
1173- integer, dimension(:), intent(in):: surface_element_list
1174- character(len=*), intent(in):: surface_name
1175-
1176- type(vector_field), pointer:: horizontal_positions_north, horizontal_positions_south
1177- type(vector_field):: horizontal_positions
1178- type(mesh_type), pointer:: surface_mesh_north, surface_mesh_south
1179- type(mesh_type):: surface_mesh
1180- integer, dimension(:), pointer:: surface_element_list_north, surface_element_list_south
1181- integer, dimension(:), allocatable:: surface_element_list_combined
1182- integer:: nodes_north, elements_south, elements_north
1183- integer:: i
1184-
1185- ! first create 2 separate horizontal coordinate fields
1186- ! for each hemisphere
1187-
1188- call create_horizontal_positions_hemisphere(positions, &
1189- surface_element_list, trim(surface_name)//'North', +1.0)
1190-
1191- call create_horizontal_positions_hemisphere(positions, &
1192- surface_element_list, trim(surface_name)//'South', -1.0)
1193-
1194- ! these are stored as bcs under the positions
1195- ! retrieve this information back:
1196-
1197- call get_boundary_condition(positions, name=trim(surface_name)//'North', &
1198- surface_mesh=surface_mesh_north, &
1199- surface_element_list=surface_element_list_north)
1200- call get_boundary_condition(positions, name=trim(surface_name)//'South', &
1201- surface_mesh=surface_mesh_south, &
1202- surface_element_list=surface_element_list_south)
1203-
1204- horizontal_positions_north => extract_surface_field(positions, &
1205- trim(surface_name)//'North', trim(surface_name)//"NorthHorizontalCoordinate")
1206- horizontal_positions_south => extract_surface_field(positions, &
1207- trim(surface_name)//'South', trim(surface_name)//"SouthHorizontalCoordinate")
1208-
1209- ! merge these 2 meshes
1210- surface_mesh=merge_meshes( (/ surface_mesh_north, surface_mesh_south /) )
1211-
1212- ! and merge the positions field
1213- call allocate( horizontal_positions, positions%dim-1, surface_mesh, &
1214- trim(surface_name)//"HorizontalCoordinate" )
1215-
1216- nodes_north=node_count(surface_mesh_north)
1217- do i=1, nodes_north
1218- call set( horizontal_positions, i, &
1219- node_val(horizontal_positions_north, i))
1220- end do
1221- ! but translate the projected southern hemisphere to the left
1222- ! to not overlap it with the northern hemisphere
1223- do i=1, node_count(surface_mesh_south)
1224- call set( horizontal_positions, nodes_north+i, &
1225- node_val(horizontal_positions_south, i)+(/ HEMISPHERE_SHIFT, 0.0 /) )
1226- end do
1227-
1228- ! merge the surface element lists
1229- ! (note that this is different than the incoming surface_element_list
1230- ! as it will have some duplicate equatorial elements)
1231- elements_north=size(surface_element_list_north)
1232- elements_south=size(surface_element_list_south)
1233- allocate(surface_element_list_combined(1:elements_north+elements_south))
1234- surface_element_list_combined(1:elements_north)=surface_element_list_north
1235- surface_element_list_combined(elements_north+1:)=surface_element_list_south
1236-
1237- ! finally the bc for the combined surface mesh:
1238- ! (note that this creates a new surface mesh different than
1239- ! the merged mesh, that we won't be using)
1240- call add_boundary_condition_surface_elements( &
1241- positions, name=surface_name, type="verticalextrapolation", &
1242- surface_element_list=surface_element_list_combined)
1243-
1244- ! insert the horizontal positions under this bc
1245- call insert_surface_field( positions, name=surface_name, &
1246- surface_field=horizontal_positions)
1247-
1248- ! everything is safely stored, so we can deallocate our references
1249- call deallocate(horizontal_positions)
1250- call deallocate(surface_mesh)
1251- deallocate(surface_element_list_combined)
1252-
1253- ! also we won't need the 2 hemisphere bcs anymore
1254- call remove_boundary_condition( positions, trim(surface_name)//'North')
1255- call remove_boundary_condition( positions, trim(surface_name)//'South')
1256-
1257-end subroutine create_horizontal_positions_sphere
1258-
1259-subroutine create_horizontal_positions_hemisphere(positions, &
1260- surface_element_list, surface_name, hemi_sign)
1261- type(vector_field), intent(inout):: positions
1262- integer, dimension(:), intent(in):: surface_element_list
1263- character(len=*), intent(in):: surface_name
1264- real, intent(in):: hemi_sign
1265-
1266- type(vector_field):: horizontal_positions
1267- type(mesh_type), pointer:: surface_mesh
1268- real, dimension(2):: xy
1269- integer, dimension(:), pointer:: nodes, surface_node_list
1270- integer:: i, j, sele, node
1271-
1272- type(integer_set):: surface_element_set
1273-
1274- call allocate(surface_element_set)
1275-
1276- do i=1, size(surface_element_list)
1277- sele=surface_element_list(i)
1278- if (sele_is_on_hemisphere(sele)) then
1279- call insert(surface_element_set, sele)
1280- end if
1281- end do
1282-
1283- call add_boundary_condition_surface_elements(positions, &
1284- name=surface_name, type="verticalextrapolation", &
1285- surface_element_list=set2vector(surface_element_set))
1286- call deallocate(surface_element_set)
1287-
1288- call get_boundary_condition(positions, name=surface_name, &
1289- surface_mesh=surface_mesh, surface_node_list=surface_node_list)
1290-
1291- call allocate( horizontal_positions, dim=2, mesh=surface_mesh, &
1292- name=trim(surface_name)//"HorizontalCoordinate" )
1293-
1294- call insert_surface_field(positions, name=surface_name, &
1295- surface_field=horizontal_positions)
1296-
1297- do i=1, element_count(horizontal_positions)
1298- nodes => ele_nodes(horizontal_positions, i)
1299- do j=1, size(nodes)
1300- node=surface_node_list( nodes(j) )
1301- xy=map2horizontal_sphere(node_val(positions, node), hemi_sign)
1302- assert(abs(xy(1))<HEMISPHERE_SHIFT/2.0)
1303- call set( horizontal_positions, nodes(j), xy)
1304- end do
1305- end do
1306-
1307- call deallocate(horizontal_positions)
1308-
1309- contains
1310-
1311- logical function sele_is_on_hemisphere(sele)
1312- integer, intent(in):: sele
1313-
1314- real, dimension(face_loc(positions, sele)):: znodes
1315-
1316- znodes=face_val(positions, 3, sele)
1317- ! if any of the nodes is on the hemisphere or *very* close to the equator
1318- ! (relative to the other nodes)
1319- sele_is_on_hemisphere = any(hemi_sign * znodes > -sum(abs(znodes))*1e-6)
1320-
1321- end function sele_is_on_hemisphere
1322-
1323-end subroutine create_horizontal_positions_hemisphere
1324-
1325-function map2horizontal(xyz, normal_vector)
1326-real, dimension(:), intent(in):: xyz, normal_vector
1327-real, dimension(size(xyz)-1):: map2horizontal
1328-
1329- real, dimension(size(xyz)):: hxyz
1330- integer:: i, c, takeout
1331-
1332- ! first subtract of the vertical component
1333- hxyz=xyz-dot_product(xyz, normal_vector)*normal_vector
1334-
1335- ! then leave out the "most vertical" coordinate
1336- takeout=maxloc(abs(normal_vector), dim=1)
1337-
1338- c=1
1339- do i=1, size(xyz)
1340- if (i==takeout) cycle
1341- map2horizontal(c)=hxyz(i)
1342- c=c+1
1343- end do
1344-
1345-end function map2horizontal
1346-
1347-function map2horizontal_sphere(xyz, hemi_sign)
1348-real, dimension(3), intent(in):: xyz
1349-real, intent(in):: hemi_sign
1350-real, dimension(2):: map2horizontal_sphere
1351-
1352- real:: r
1353-
1354- r=sqrt(sum(xyz**2))
1355- map2horizontal_sphere=xyz(1:2)/(r+hemi_sign*xyz(3))
1356-
1357-end function map2horizontal_sphere
1358-
1359-function VerticalProlongationOperator(mesh, positions, vertical_normal, &
1360- surface_element_list, surface_mesh)
1361- !! creates a prolongation operator that prolongates values on
1362- !! a surface mesh to a full mesh below using the same interpolation
1363- !! as the vertical extrapolation code above. The transpose of this prolongation
1364- !! operator can be used as a restriction/clustering operator
1365- !! from the full mesh to the surface.
1366- type(csr_matrix) :: VerticalProlongationOperator
1367- !! the mesh to which to prolongate, its nodes are only considered as
1368- !! a set of loose points
1369- type(mesh_type), target, intent(in):: mesh
1370- !! positions on the whole domain (doesn't have to be the same mesh)
1371- type(vector_field), intent(inout):: positions
1372- !! upward normal vector on the whole domain
1373- type(vector_field), target, intent(in):: vertical_normal
1374- !! the face numbers of the surface mesh
1375- integer, dimension(:), intent(in):: surface_element_list
1376- !! Optionally a surface mesh may be provided that represents the nodes
1377- !! /from/ which to interpolate (may be of different signature than 'mesh')
1378- !! Each column in the prolongator will correspond to a node in this surface_mesh.
1379- !! If not provided, the "from mesh" is considered to consist of faces
1380- !! given by surface_element_list (and same cont. and order as 'mesh').
1381- !! In this case however empty columns, i.e. surface nodes from which no
1382- !! value in the full mesh is interpolated, are removed and there is no
1383- !! necessary relation between column numbering and surface node numbering.
1384- type(mesh_type), intent(in), target, optional:: surface_mesh
1385-
1386- type(csr_sparsity):: sparsity
1387- type(mesh_type), pointer:: lsurface_mesh
1388- real, dimension(:,:), allocatable:: loc_coords
1389- real, dimension(:), allocatable:: mat
1390- real:: coef
1391- integer, dimension(:), pointer:: snodes
1392- integer, dimension(:), allocatable:: seles, colm, findrm, snod2used_snod
1393- integer i, j, k, rows, entries, count, snod, sele
1394- ! coefficient have to be at least this otherwise they're negligable in an interpolation
1395- real, parameter :: COEF_EPS=1d-10
1396-
1397- ! only assemble the rows associted with nodes we own
1398- rows=nowned_nodes(mesh)
1399-
1400- ! local coordinates is one more than horizontal coordinate dim
1401- allocate( seles(rows), loc_coords(1:positions%dim, 1:rows) )
1402-
1403- ! project the positions of to_fields(1)%mesh into the horizontal plane
1404- ! and returns 'seles' (indices in surface_element_list) and loc_coords
1405- ! to tell where these projected nodes are found in the surface mesh
1406- call horizontal_picker(mesh, positions, vertical_normal, &
1407- surface_element_list, "TempSurfaceName", &
1408- seles, loc_coords)
1409-
1410- ! count upper estimate for n/o entries for sparsity
1411- entries=0
1412- do i=1, rows
1413- entries=entries+face_loc(mesh, seles(i))
1414- end do
1415- ! preliminary matrix:
1416- allocate( mat(1:entries), findrm(1:rows+1), &
1417- colm(1:entries) )
1418-
1419- if (.not. present(surface_mesh)) then
1420- ! We use the entire surface mesh of 'mesh'
1421- lsurface_mesh => mesh%faces%surface_mesh
1422- ! Not all surface nodes may be used (i.e. interpolated from) - even
1423- ! within surface elements that /are/ in surface_element-list.
1424- ! We need a map between global surface node numbering and
1425- ! a consecutive numbering of used surface nodes.
1426- ! (this will be the column numbering)
1427- allocate(snod2used_snod(1:node_count(lsurface_mesh)))
1428- snod2used_snod=0
1429- count=0 ! counts number of used surface nodes
1430- else
1431- lsurface_mesh => surface_mesh
1432- end if
1433-
1434- entries=0 ! this time only count nonzero entries
1435- do i=1, rows
1436-
1437- ! beginning of each row in mat
1438- findrm(i)=entries+1
1439-
1440- if (present(surface_mesh)) then
1441- ! element number within surface_mesh
1442- sele=seles(i)
1443- else
1444- ! face number in 'mesh', i.e. element number within entire surface_mesh
1445- sele=surface_element_list(seles(i))
1446- end if
1447-
1448- snodes => ele_nodes(lsurface_mesh, sele)
1449-
1450- do j=1, size(snodes)
1451- coef=eval_shape(ele_shape(lsurface_mesh, sele), j, loc_coords(:,i))
1452- snod=snodes(j)
1453- if (abs(coef)>COEF_EPS) then
1454- if (.not. present(surface_mesh)) then
1455- if (snod2used_snod(snod)==0) then
1456- ! as of yet unused surface node
1457- count=count+1
1458- snod2used_snod(snod)=count
1459- end if
1460- ! this is the column index we're gonna use instead
1461- snod=snod2used_snod(snod)
1462- end if
1463- entries=entries+1
1464- colm(entries)=snod
1465- mat(entries)=coef
1466- end if
1467- end do
1468-
1469- end do
1470- findrm(i)=entries+1
1471-
1472- if (present(surface_mesh)) then
1473- ! we haven't counted used surface nodes, instead we're using all
1474- ! nodes of surface mesh as columns
1475- count=node_count(surface_mesh)
1476- end if
1477-
1478- call allocate(sparsity, rows, count, &
1479- entries, diag=.false., name="VerticalProlongationSparsity")
1480- sparsity%findrm=findrm
1481- sparsity%colm=colm(1:entries)
1482- ! for lots of applications it's good to have sorted rows
1483- call sparsity_sort(sparsity)
1484-
1485- call allocate(VerticalProlongationOperator, sparsity, &
1486- name="VerticalProlongationOperator")
1487- call deallocate(sparsity)
1488-
1489- ! as the sparsity has been sorted the ordering of mat(:) no longer
1490- ! matches that of sparsity%colm, however it still matches the original
1491- ! unsorted colm(:)
1492- do i=1, rows
1493- do k=findrm(i), findrm(i+1)-1
1494- j=colm(k)
1495- call set(VerticalProlongationOperator, i, j, mat(k))
1496- end do
1497- end do
1498-
1499- if (.not. present(surface_mesh)) then
1500- deallocate( snod2used_snod )
1501- end if
1502- deallocate( findrm, colm, mat )
1503- deallocate( seles, loc_coords )
1504-
1505- call remove_boundary_condition(positions, "TempSurfaceName")
1506-
1507-end function VerticalProlongationOperator
1508-
1509-subroutine vertical_element_ordering(ordered_elements, face_normal_gravity, optimal_ordering)
1510-!!< Calculates an element ordering such that each element is
1511-!!< is preceded by all elements above it.
1512-integer, dimension(:), intent(out):: ordered_elements
1513-!! need to supply face_normal_gravity matrix,
1514-!! created by compute_face_normal_gravity() subroutine below
1515-type(csr_matrix), intent(in):: face_normal_gravity
1516-!! returns .true. if an optimal ordering is found, i.e there are no
1517-!! cycles, i.o.w. elements that are (indirectly) above and below each other
1518-!! at the same time (deck of cards problem).
1519-logical, optional, intent(out):: optimal_ordering
1520-
1521- type(dynamic_bin_type) dbin
1522- real, dimension(:), pointer:: inn
1523- integer, dimension(:), pointer:: neigh
1524- integer, dimension(:), allocatable:: bin_list
1525- integer i, j, elm, bin_no
1526- logical warning
1527-
1528- assert( size(ordered_elements)==size(face_normal_gravity,1) )
1529-
1530- ! create binlist, i.e. assign each element to a bin, according to
1531- ! the number of elements above it
1532- allocate(bin_list(1:size(ordered_elements)))
1533- do i=1, size(ordered_elements)
1534- neigh => row_m_ptr(face_normal_gravity, i)
1535- inn => row_val_ptr(face_normal_gravity, i)
1536- ! elements with no element above it go in bin 1
1537- ! elements with n elements above it go in bin n+1
1538- ! neigh>0 so we don't count exterior boundary faces
1539- bin_list(i)=count( inn<-VERTICAL_INTEGRATION_EPS .and. neigh>0 )+1
1540- end do
1541-
1542- call allocate(dbin, bin_list)
1543-
1544- warning=.false.
1545- do i=1, size(ordered_elements)
1546- ! pull an element from the first non-empty bin
1547- ! (hopefully an element with no unprocessed elements above)
1548- call pull_element(dbin, elm, bin_no)
1549- ordered_elements(i)=elm
1550- ! if this is bin one then it is indeed an element with no unprocessed
1551- ! elements above, otherwise issue a warning
1552- if (bin_no>1) warning=.true.
1553-
1554- ! update elements below:
1555-
1556- ! adjacent elements:
1557- neigh => row_m_ptr(face_normal_gravity, elm)
1558- inn => row_val_ptr(face_normal_gravity, elm)
1559- do j=1, size(neigh)
1560- if (inn(j)>VERTICAL_INTEGRATION_EPS .and. neigh(j)>0) then
1561- ! element neigh(j) is below element i, therefore now has one
1562- ! less unprocessed element above it, so can be moved to
1563- ! lower bin.
1564- if (.not. element_pulled(dbin, neigh(j))) then
1565- ! but only if neigh(j) itself hasn't been selected yet
1566- ! (which might happen for imperfect vertical orderings)
1567- call move_element(dbin, neigh(j), bin_list(neigh(j))-1)
1568- end if
1569- end if
1570- end do
1571- end do
1572-
1573- if (warning) then
1574- ! this warning may be reduced (in verbosity level) if it occurs frequently:
1575- ewrite(-1,*) "Warning: vertical_element_ordering has detected a cycle."
1576- ewrite(-1,*) "(deck of cards problem). This may reduce the efficiency"
1577- ewrite(-1,*) "of your vertically sweeping solve."
1578- end if
1579-
1580- if (present(optimal_ordering)) then
1581- optimal_ordering=.not. warning
1582- end if
1583-
1584- call deallocate(dbin)
1585-
1586-end subroutine vertical_element_ordering
1587-
1588-subroutine compute_face_normal_gravity(face_normal_gravity, &
1589- positions, vertical_normal)
1590-!!< Returns a matrix where A_ij is the inner product of the face normal
1591-!!< and the gravity normal vector of the face between element i and j.
1592-type(csr_matrix), intent(out):: face_normal_gravity
1593-type(vector_field), target, intent(in):: positions, vertical_normal
1594-
1595- type(mesh_type), pointer:: mesh
1596- real, dimension(:), pointer:: face_normal_gravity_val
1597- real, dimension(:), allocatable:: detwei_f
1598- real, dimension(:,:), allocatable:: face_normal, gravity_normal
1599- integer, dimension(:), pointer:: neigh, faces
1600- real inn, area
1601- integer sngi, nloc, i, k
1602-
1603- mesh => positions%mesh
1604- call allocate(face_normal_gravity, mesh%faces%face_list%sparsity)
1605- call zero(face_normal_gravity)
1606-
1607- sngi=face_ngi(mesh, 1)
1608- nloc=ele_loc(mesh,1)
1609- allocate( detwei_f(1:sngi), &
1610- face_normal(1:positions%dim, 1:sngi), &
1611- gravity_normal(1:positions%dim, 1:sngi))
1612-
1613- do i=1, element_count(mesh)
1614- ! elements adjacent to element i
1615- ! this is a row (column indices) in the mesh%faces%face_list matrix
1616- neigh => ele_neigh(mesh, i)
1617- ! the surrounding faces
1618- ! this is a row (integer values) in the mesh%faces%face_list matrix
1619- faces => ele_faces(mesh, i)
1620- do k=1, size(neigh)
1621- if (neigh(k)>i .or. neigh(k)<=0) then
1622- ! only handling neigh(k)>i to ensure anti-symmetry of the matrix
1623- ! (and more efficient of course)
1624- call transform_facet_to_physical(positions, faces(k), &
1625- detwei_f=detwei_f, &
1626- normal=face_normal)
1627- gravity_normal=face_val_at_quad(vertical_normal, faces(k))
1628- area=sum(detwei_f)
1629- ! inner product of face normal and vertical normal
1630- ! integrated over face
1631- inn=sum(matmul(face_normal*gravity_normal, detwei_f))/area
1632- if (neigh(k)>0) then
1633- call set(face_normal_gravity, i, neigh(k), inn)
1634- call set(face_normal_gravity, neigh(k), i, -inn)
1635- else
1636- ! exterior surface: matrix entry does not have valid
1637- ! column index, still want to store its value, so we
1638- ! use a pointer
1639- face_normal_gravity_val => row_val_ptr(face_normal_gravity, i)
1640- face_normal_gravity_val(k)=inn
1641- end if
1642- end if
1643- end do
1644-
1645- end do
1646-
1647-end subroutine compute_face_normal_gravity
1648-
1649-subroutine vertical_integration_scalar(from_field, to_field, &
1650- positions, vertical_normal, surface_element_list, rhs)
1651-!!< See description vertical_integration_multiple
1652-type(scalar_field), intent(in):: from_field
1653-type(scalar_field), intent(in):: to_field
1654-type(vector_field), intent(in):: positions, vertical_normal
1655-integer, dimension(:), intent(in):: surface_element_list
1656-type(scalar_field), optional, intent(in):: rhs
1657-
1658- type(scalar_field) to_fields(1)
1659-
1660- to_fields=(/ to_field /)
1661- if (present(rhs)) then
1662- call vertical_integration_multiple( (/ from_field /), to_fields, &
1663- positions, vertical_normal, surface_element_list, rhs=(/ rhs /) )
1664- else
1665- call vertical_integration_multiple( (/ from_field /), to_fields, &
1666- positions, vertical_normal, surface_element_list)
1667- end if
1668-
1669-end subroutine vertical_integration_scalar
1670-
1671-subroutine vertical_integration_vector(from_field, to_field, &
1672- positions, vertical_normal, surface_element_list, rhs)
1673-!!< See description vertical_integration_multiple
1674-type(vector_field), intent(in):: from_field
1675-type(vector_field), intent(in):: to_field
1676-type(vector_field), intent(in):: positions, vertical_normal
1677-integer, dimension(:), intent(in):: surface_element_list
1678-type(vector_field), optional, intent(in):: rhs
1679-
1680- type(scalar_field), dimension(from_field%dim):: from_field_components, &
1681- to_field_components, rhs_components
1682- integer i
1683-
1684- assert(from_field%dim==to_field%dim)
1685-
1686- do i=1, from_field%dim
1687- from_field_components(i)=extract_scalar_field(from_field, i)
1688- to_field_components(i)=extract_scalar_field(to_field, i)
1689- if (present(rhs)) then
1690- rhs_components(i)=extract_scalar_field(rhs, i)
1691- end if
1692- end do
1693-
1694- if (present(rhs)) then
1695- call vertical_integration_multiple( from_field_components, &
1696- to_field_components, positions, vertical_normal, &
1697- surface_element_list, rhs=rhs_components)
1698- else
1699- call vertical_integration_multiple( from_field_components, &
1700- to_field_components, positions, vertical_normal, &
1701- surface_element_list)
1702- end if
1703-
1704-end subroutine vertical_integration_vector
1705-
1706-subroutine vertical_integration_multiple(from_fields, to_fields, &
1707- positions, vertical_normal, surface_element_list, rhs)
1708-!!< This subroutine solves: dP/dz=rhs using DG
1709-!!< It can be used for vertical integration downwards (dP/dz=0) as a drop
1710-!!< in replacement of VerticalExtrapolation hence its similar interface.
1711-!!< The field P is the to_field. A boundary condition is given by the
1712-!!< from_field. Again completely similar to VerticalExtrapolation, it may
1713-!!< be defined as a surface field on surface elements given by
1714-!!< surface_element_list or it may be a field on the complete mesh
1715-!!< in which case only its values on these surface elements are used.
1716-!!< vertical_normal specifies the direction in which to integrate (usually downwards)
1717-!!< If not specified rhs is assumed zero.
1718-!!<
1719-!!< This version accepts multiple from_fields, to_fields and rhs
1720-type(scalar_field), dimension(:), intent(in):: from_fields
1721-type(scalar_field), dimension(:), intent(inout):: to_fields
1722-type(vector_field), intent(in):: positions, vertical_normal
1723-integer, dimension(:), intent(in):: surface_element_list
1724-type(scalar_field), dimension(:), optional, intent(in):: rhs
1725-
1726- type(csr_matrix) face_normal_gravity
1727- type(element_type), pointer:: ele_shp, face_shp, x_face_shp
1728- real, dimension(:), pointer:: inn
1729- real, dimension(:,:,:), allocatable:: surface_rhs, dele_shp
1730- real, dimension(:,:), allocatable:: ele_mat, face_mat, ele_rhs
1731- real, dimension(:), allocatable:: detwei, detwei_f
1732- integer, dimension(:), pointer:: neigh, ele_nds, faces, face_lnds
1733- integer, dimension(:), allocatable:: ordered_elements, face_nds, face_nds2
1734- integer nloc, snloc, ngi, sngi
1735- integer i, j, k, f, f2, elm, it, noit
1736- logical optimal_ordering, from_surface_fields
1737-
1738- assert( size(from_fields)==size(to_fields) )
1739-
1740- ! computes inner product of face normal and gravity (see above)
1741- call compute_face_normal_gravity(face_normal_gravity, &
1742- positions, vertical_normal)
1743-
1744- ! determine an ordering for the elements based on this
1745- allocate( ordered_elements(1:element_count(positions)) )
1746- call vertical_element_ordering(ordered_elements, face_normal_gravity, &
1747- optimal_ordering)
1748-
1749- ! General initalisation
1750- !-----------------------
1751- ! various grid numbers
1752- nloc=ele_loc(to_fields(1), 1)
1753- snloc=face_loc(to_fields(1), 1)
1754- ngi=ele_ngi(positions, 1)
1755- sngi=face_ngi(positions, 1)
1756- ! shape functions
1757- ele_shp => ele_shape(to_fields(1), 1)
1758- face_shp => face_shape(to_fields(1), 1)
1759- x_face_shp => face_shape(positions, 1)
1760- ! various allocations:
1761- allocate( &
1762- surface_rhs(1:snloc, 1:size(to_fields), 1:surface_element_count(positions)), &
1763- ele_mat(1:nloc, 1:nloc), ele_rhs(1:nloc,1:size(to_fields)), &
1764- dele_shp(1:nloc, 1:ngi, 1:positions%dim), &
1765- face_mat(1:snloc, 1:snloc), face_nds(1:snloc), face_nds2(1:snloc), &
1766- detwei(1:ngi), detwei_f(1:sngi))
1767-
1768- if (element_count(from_fields(1))==size(surface_element_list)) then
1769- ! from_fields are fields over the surface mesh only
1770- ! so we're using all of its values:
1771- from_surface_fields=.true.
1772- ! check the other fields as well:
1773- do k=2, size(from_fields)
1774- assert( element_count(from_fields(k))==size(surface_element_list) )
1775- end do
1776- else
1777- ! from_fields are on the full mesh and we only extract its values
1778- ! at the specified surface_elements
1779-
1780- from_surface_fields=.false.
1781- end if
1782-
1783- surface_rhs=0
1784- ! Compute contribution of exterior surface integral (boundary condition) to rhs
1785- !-----------------------
1786- do i=1, size(surface_element_list)
1787- f=surface_element_list(i)
1788- call transform_facet_to_physical(positions, f, detwei_f)
1789- face_mat=-shape_shape(face_shp, face_shp, detwei_f)
1790- do k=1, size(from_fields)
1791- if (from_surface_fields) then
1792- ! we need to use ele_val, where i is the element number in the surface_mesh
1793- surface_rhs(:,k,f)=surface_rhs(:,k,f)+ &
1794- matmul(face_mat, ele_val(from_fields(k), i))
1795- else
1796- ! we can simply use face_val with face number f
1797- surface_rhs(:,k,f)=surface_rhs(:,k,f)+ &
1798- matmul(face_mat, face_val(from_fields(k), f))
1799- end if
1800- end do
1801- end do
1802-
1803- ! Solution loop
1804- !-----------------------
1805- if (optimal_ordering) then
1806- noit=1
1807- else
1808- noit=10
1809- end if
1810-
1811- do it=1, noit
1812- do i=1, element_count(positions)
1813-
1814- elm=ordered_elements(i)
1815-
1816- ! construct diagonal matrix block for this element
1817- call transform_to_physical(positions, elm, &
1818- shape=ele_shp, dshape=dele_shp, detwei=detwei)
1819- ele_mat=shape_vector_dot_dshape(ele_shp, &
1820- ele_val_at_quad(vertical_normal,elm), &
1821- dele_shp, detwei)
1822-
1823- ! initialise rhs
1824- if (present(rhs)) then
1825- do k=1, size(to_fields)
1826- ele_rhs(:,k)=shape_rhs(ele_shp, detwei*ele_val_at_quad(rhs(k), elm))
1827- end do
1828- else
1829- ele_rhs=0.0
1830- end if
1831-
1832-
1833- ! then add contribution of surface integrals of incoming
1834- ! faces to the rhs and matrix
1835- neigh => row_m_ptr(face_normal_gravity, elm)
1836- inn => row_val_ptr(face_normal_gravity, elm)
1837- faces => ele_faces(positions, elm)
1838- do j=1, size(neigh)
1839- if (inn(j)<-VERTICAL_INTEGRATION_EPS) then
1840- call transform_facet_to_physical(positions, faces(j), &
1841- detwei_f)
1842- face_mat=-shape_shape(face_shp, face_shp, detwei_f)*inn(j)
1843-
1844- face_nds=face_global_nodes(to_fields(1), faces(j))
1845- face_lnds => face_local_nodes(to_fields(1)%mesh, faces(j))
1846- ele_mat(face_lnds,face_lnds)=ele_mat(face_lnds,face_lnds)+face_mat
1847-
1848- if (neigh(j)>0) then
1849- ! face of element neigh(j), facing elm:
1850- f2=ele_face(positions, neigh(j), elm)
1851- face_nds2=face_global_nodes(to_fields(1), f2)
1852-
1853- do k=1, size(to_fields)
1854- ele_rhs(face_lnds,k)=ele_rhs(face_lnds,k)+ &
1855- matmul(face_mat, node_val(to_fields(k), face_nds2))
1856- end do
1857- else
1858- ! note that we've already multiplied with face_mat above, but not with inn(j)
1859- ele_rhs(face_lnds,:)=ele_rhs(face_lnds,:)+surface_rhs(:,:,faces(j))*inn(j)
1860- end if
1861- end if
1862- end do
1863-
1864- call invert(ele_mat)
1865-
1866- ! compute values for the to_fields:
1867- ele_nds => ele_nodes(to_fields(1), elm)
1868- do k=1, size(to_fields)
1869- call set( to_fields(k), ele_nds, matmul(ele_mat, ele_rhs(:,k)) )
1870- end do
1871- end do
1872- end do
1873-
1874- call deallocate(face_normal_gravity)
1875-
1876-end subroutine vertical_integration_multiple
1877-
1878-subroutine vertical_extrapolation_module_check_options
1879-
1880- if (have_option("/geometry/ocean_boundaries")) then
1881- if (.not. have_option("/physical_parameters/gravity")) then
1882- ewrite(-1,*) "If you select /geometry/ocean_boundaries, you also need to "//&
1883- &"set /physical_parameters/gravity"
1884- FLExit("Missing gravity!")
1885- end if
1886- end if
1887-
1888-end subroutine vertical_extrapolation_module_check_options
1889-
1890-end module vertical_extrapolation_module
1891
1892=== modified file 'main/Fluids.F90'
1893--- main/Fluids.F90 2012-09-03 14:31:15 +0000
1894+++ main/Fluids.F90 2012-09-04 15:00:37 +0000
1895@@ -84,7 +84,7 @@
1896 use discrete_properties_module
1897 use gls
1898 use k_epsilon
1899- use iceshelf_meltrate_surf_normal
1900+ use ice_melt_interface
1901 use halos
1902 use memory_diagnostics
1903 use free_surface_module
1904@@ -406,15 +406,9 @@
1905
1906 call initialise_diagnostics(filename, state)
1907
1908- ! Initialise ice_meltrate, read constatns, allocate surface, and calculate melt rate
1909+ ! Initialise melt interface paramerisation
1910 if (have_option("/ocean_forcing/iceshelf_meltrate/Holland08")) then
1911- call melt_surf_init(state(1))
1912- call melt_allocate_surface(state(1))
1913- call melt_surf_calc(state(1))
1914- !BC for ice melt
1915- if (have_option('/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries')) then
1916- call melt_bc(state(1))
1917- endif
1918+ call melt_interface_initialisation(state(1))
1919 end if
1920
1921 ! Checkpoint at start
1922@@ -636,12 +630,13 @@
1923 end if
1924 end if
1925
1926- ! Calculate the meltrate
1927- if(have_option("/ocean_forcing/iceshelf_meltrate/Holland08/") ) then
1928- if( (trim(field_name_list(it))=="MeltRate")) then
1929- call melt_surf_calc(state(1))
1930+ ! Calculate the (ice) interface meltrate using the parameterisation described in Holland et al. 2008 doi:10.1175/2007JCLI1909.1
1931+ if (have_option("/ocean_forcing/iceshelf_meltrate/Holland08")) then
1932+ if ((trim(field_name_list(it)) == "MeltRate")) then
1933+ call melt_interface_calculate(state(1))
1934 endif
1935- end if
1936+ end if
1937+
1938
1939 call get_option(trim(field_optionpath_list(it))//&
1940 '/prognostic/equation[0]/name', &
1941@@ -713,9 +708,9 @@
1942 call keps_eddyvisc(state(i))
1943 end do
1944
1945- !BC for ice melt
1946+ ! Boundary conditions for the (ice) melt interface parameterisation
1947 if (have_option('/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries')) then
1948- call melt_bc(state(1))
1949+ call melt_interface_boundary_condition(state(1))
1950 endif
1951
1952 if(option_count("/material_phase/scalar_field/prognostic/spatial_discretisation/coupled_cv")>0) then
1953
1954=== modified file 'main/Makefile.dependencies'
1955--- main/Makefile.dependencies 2012-03-02 00:25:33 +0000
1956+++ main/Makefile.dependencies 2012-09-04 15:00:37 +0000
1957@@ -45,7 +45,7 @@
1958 ../include/fldebug.mod ../include/foam_flow_module.mod \
1959 ../include/free_surface_module.mod ../include/global_parameters.mod \
1960 ../include/gls.mod ../include/goals.mod ../include/halos.mod \
1961- ../include/iceshelf_meltrate_surf_normal.mod ../include/implicit_solids.mod \
1962+ ../include/ice_melt_interface.mod ../include/implicit_solids.mod \
1963 ../include/k_epsilon.mod ../include/memory_diagnostics.mod \
1964 ../include/meshdiagnostics.mod ../include/meshmovement.mod \
1965 ../include/momentum_equation.mod ../include/multimaterial_module.mod \
1966
1967=== modified file 'manual/embedded_models.tex'
1968--- manual/embedded_models.tex 2011-11-24 14:46:24 +0000
1969+++ manual/embedded_models.tex 2012-09-04 15:00:37 +0000
1970@@ -361,13 +361,6 @@
1971 therefore requires a single boundary condition: the amount of light incident
1972 on the water surface. This is a Dirichlet condition on $I$.
1973
1974-As the PAR equation is relatively trivial to solve, the following options
1975-are recommended:
1976-\begin{itemize}
1977-\item Discontinuous discretisation
1978-\item Solver: gmres iterative method, with no preconditioner.
1979-\end{itemize}
1980-
1981 \subsection{Detritus falling velocity}\label{sec:detritus}
1982
1983 Phytoplankton, zooplankton and nutrient are assumed to be neutrally buoyant
1984
1985=== modified file 'manual/model_equations.tex'
1986--- manual/model_equations.tex 2012-08-31 13:12:30 +0000
1987+++ manual/model_equations.tex 2012-09-04 15:00:37 +0000
1988@@ -10,7 +10,7 @@
1989 problem & underlying equations & boundary conditions & other considerations\\
1990 \hline
1991 scalar advection & \ref{sec:MP-AdvdifEqn-eqns}, \ref{sec:MP-AdvdifEqn-adv} & \ref{sec:bc_scalar_dirichlet}, \ref{sec:bc_scalar_neumann} & × \\
1992-scalar advection-diffusion & \ref{sec:MP-AdvdifEqn-eqns}, \ref{sec:MP-AdvdifEqn-diff} & \ref{sec:bc_scalar_dirichlet}, \ref{sec:bc_scalar_neumann}, \ref{sec:bc_scalar_robin} × & ×\\
1993+scalar advection-diffusion & \ref{sec:MP-AdvdifEqn-eqns}, \ref{sec:MP-AdvdifEqn-diff} & × & ×\\
1994 momentum equation & \ref{sec:MP-MomEqn} & \ref{sec:bc_vector_dirichlet}, \ref{sec:bc_vector_stress}, \ref{sec:bc_vector_traction}, \ref{sec:FS} & \ref{sec:eqn_extensions} \\
1995 \hline
1996 \end{tabular}
1997@@ -119,21 +119,6 @@
1998 and substituting in this surface integral to the discretised equation. Note that $q$ is often termed a flux.
1999
2000
2001-\subsubsection{Robin condition for a scalar field}\label{sec:bc_scalar_robin}
2002-\index{boundary conditions!Neumann}
2003-
2004-Taking the weak form (applying Green's theorem to the diffusion term) of the advection-diffusion equation \eqref{eq:general_scalar_eqn} leads to a surface integral of the form
2005-\begin{equation*}
2006-\int_{\partial\Omega} \phi (\kaptens\nabla c)\cdot\bmn \;d\Gamma,
2007-\end{equation*}
2008-where $\phi$ is a test function (see section \ref{chap:numerical_discretisation}).
2009-The Robin condition is specified by relating the surface diffusive flux $(\kaptens\nabla c)\cdot\bmn$ to an ambient field value $c_{a}$, with an associated surface transfer coefficient $h$. This is given by the relationship
2010-\begin{equation*}
2011--(\kaptens\nabla c)\cdot\bmn = h (c - c_{a}),\quad \textrm{on}\quad \partial\Omega,
2012-\end{equation*}
2013-where in general $h$ and $c_{a}$ can vary spatially and temporally. This is substituted into the discretised equation to give a surface absorption term given by $hc$ and a surface source given by $-hc_{a}$.
2014-
2015-
2016 \section{Fluid equations}\label{sec:MP-MomEqn}
2017
2018 \index{conservation!equation}
2019@@ -727,7 +712,7 @@
2020
2021 \subsection{Multi-material simulations}
2022 \index{multi-material flow}
2023-The ability to differentiate between regions with distinct material properties is of fundamental importance in the modelling of many physical systems. Two different approaches exist for achieving this: the multi-material approach and the multiphase approach. The multi-material approach is implemented within \fluidity, and the multiphase approach (discussed in the next subsection) is currently under development.
2024+The ability to differentiate between regions with distinct material properties is of fundamental importance in the modelling of many physical systems. Two different approaches exist for achieving this: the multi-material approach and the multi-phase approach. The multi-material approach is implemented within \fluidity, and the multi-phase approach (discussed in the next subsection) is currently under development.
2025 In situations where the model can resolve physical mixing of immiscible materials, or where there is no mixing, only one velocity field (and hence one momentum equation) is required to describe the flow of all materials. The \emph{multi-material} approach, considers all materials to be immiscible materials separated by a sharp interface.
2026
2027 In a multi-material approach, the various forms of the conservation equations can be solved for multiple material flows if the point-wise mass density $\rho$ in the equations is defined as the bulk density at each point. If the flow comprises $n$ materials and the volume fraction of the $i^{th}$ material is denoted $\phi_i$ then the bulk density is given by:
2028@@ -749,17 +734,14 @@
2029 \end{equation}
2030 where the volume fraction field at time zero must be specified.
2031
2032-\subsection{Multiphase simulations}
2033+\subsection{Multi-phase simulations}
2034 \label{sec:multiphase_equations}
2035-\index{multiphase flow}
2036-Multiphase flows are defined by \cite{prosperettiEtAl2007} to be flows in which two or more phases of matter (solid, liquid, gas, etc) are simultaneously present and are allowed to inter-penetrate. Simple examples include the flow of a fizzy drink which is composed of a liquid and a finite number of gas bubbles, the transportation of solid sediment particles in a river, and the flow of blood cells around the human body.
2037+\index{multi-phase flow}
2038+Multi-phase flows are defined by \cite{prosperettiEtAl2007} to be flows in which two or more phases of matter (solid, liquid, gas, etc) are simultaneously present and are allowed to inter-penetrate. Simple examples include the flow of a fizzy drink which is composed of a liquid and a finite number of gas bubbles, the transportation of solid sediment particles in a river, and the flow of blood cells around the human body.
2039
2040 Further to the above definition, each phase is classed as either \textit{continuous} or \textit{dispersed}, where a continuous phase is a connected liquid or gas substance in which dispersed phases (comprising a finite number of solid particles, liquid droplets and/or gas bubbles) may be immersed \citep{croweEtAl1998}.
2041
2042-To enable the mixing and inter-penetration of phases, a separate velocity field (and hence a separate momentum equation) is assigned to each one and solved for. Extra terms are then included to account for inter-phase interactions. Furthermore, the model currently assumes no mass transfer between phases, incompressible flow, and a common pressure field $p$ so that only one continuity equation is used. The form of the governing equations depends on whether we are dealing with incompressible or compressible flow.
2043-
2044-\subsubsection{Incompressible flow}
2045-For an incompressible multiphase flow, the continuity equation and momentum equation for phase $i$ (based on the derivation in \cite{ishii1975}, written in non-conservative form) are:
2046+To enable the mixing and inter-penetration of phases, a separate velocity field (and hence a separate momentum equation) is assigned to each one and solved for. Extra terms are then included to account for inter-phase interactions. Furthermore, the model currently assumes no mass transfer between phases, incompressible flow, and a common pressure field $p$ so that only one continuity equation is used. Thus, the continuity equation and momentum equation for phase $i$ (based on the derivation in \cite{ishii1975}, written in non-conservative form) are:
2047 \begin{equation}
2048 \sum_{i=1}^N\nabla\cdot\left(\alpha_i\mathbf{u}_i\right) = 0,
2049 \end{equation}
2050@@ -772,19 +754,6 @@
2051 \end{equation}
2052 where $\mathbf{u}_i$, $\rho_i$, $\tautens_i$ and $\alpha_i$ are the velocity, density, viscous stress tensor and volume fraction of phase $i$ respectively, and $\mathbf{F}_i$ represents the forces imposed on phase $i$ by the other $N-1$ phases. Details of momentum transfer terms are given in Chapter \ref{chap:configuration}.
2053
2054-\subsubsection{Compressible flow}
2055-Fluidity currently only supports the case where the continuous phase is compressible. All other phases (the dispersed phases) are incompressible. The momentum equation is the same as the one given above, but the continuity equation becomes:
2056-\begin{equation}
2057-\alpha_1\frac{\partial\rho_1}{\partial t} + \nabla\cdot\left(\alpha_1\rho_1\mathbf{u}_1\right) + \rho_1\left(\sum_{i=2}^N\nabla\cdot\left(\alpha_i\mathbf{u}_i\right)\right) = 0,
2058-\end{equation}
2059-where the compressible phase has an index of $i=1$ here.
2060-
2061-An internal energy equation may also need to be solved depending on the equation of state used for the compressible phase. This is given by:
2062-\begin{equation}
2063-\alpha_i\rho_i\frac{\partial e_i}{\partial t} + \alpha_i\rho_i\mathbf{u}_i\cdot\nabla e_i = -p\nabla\cdot\left(\alpha_i\mathbf{u}_i\right) + Q_i
2064-\end{equation}
2065-where $e_i$ is the internal energy and $Q_i$ is an inter-phase energy transfer term described in Chapter \ref{chap:configuration}.
2066-
2067 \subsection{Porous Media Darcy Flow}
2068 \label{sec:porous_media_darcy_flow_equations}
2069 \index{porous media darcy flow}
2070
2071=== modified file 'manual/numerical_discretisation.tex'
2072--- manual/numerical_discretisation.tex 2012-07-26 15:38:42 +0000
2073+++ manual/numerical_discretisation.tex 2012-09-04 15:00:37 +0000
2074@@ -9,12 +9,11 @@
2075 $\Omega$ is defined as $\dOmega$
2076 %(NB. $\Gamma$ is another common symbol for a boundary)
2077 and can be split into different sections, e.g.
2078-$\partial\Omega = \dOmega^{N}\cup \dOmega^{R}\cup \dOmega^{D}$
2079+$\partial\Omega = \dOmega^{N}\cup \dOmega^{D}$
2080 where $\dOmega^{N}$ is that part of the boundary over
2081-which Neumann conditions are applied, $\dOmega^{R}$ is the part of the
2082-boundary over which Robin conditions are applied and $\dOmega^{D}$ is that part of the boundary over
2083+which Neumann conditions are applied and $\dOmega^{D}$ is that part of the boundary over
2084 which Dirichlet conditions are applied. For clarity a subscript showing the field in question will
2085-be given on the $N$, $R$ and $D$ characters.
2086+be given on the $N$ and $D$ characters.
2087
2088 The unit vector \vec{n} is always assumed to be the
2089 outward facing normal vector to the domain.
2090@@ -31,11 +30,10 @@
2091 \end{equation}
2092
2093 We now define a partition of the boundary such that
2094-$\partial\Omega = \dOmega^{N}\cup \dOmega^{R}\cup \dOmega^{D}$, and impose boundary conditions on $c$:
2095+$\partial\Omega = \dOmega^{N}\cup \dOmega^{D}$, and impose boundary conditions on $c$:
2096 \begin{gather}
2097 \label{eq:cboundary}
2098 \vec{n}\cdot\tensor{\kappa}\cdot\grad c=g_{N_c} \quad\textrm{on}\quad \dOmega^{N_c},\\
2099- -\vec{n}\cdot\tensor{\kappa}\cdot\grad c=h(c-c_{a})\quad\textrm{on}\quad \dOmega^{R_c},\\
2100 c=g_{D_c} \quad\textrm{on}\quad \dOmega^{D_c}.
2101 \end{gather}
2102
2103@@ -371,7 +369,7 @@
2104
2105 The SUPG implementation in \fluidity\ does not modify the test function derivatives
2106 or the face test functions. Hence the SUPG implementation is only consistent
2107-for degree one elements with no non-zero Neumann, Robin or weak Dirichlet boundary
2108+for degree one elements with no non-zero Neumann or weak Dirichlet boundary
2109 conditions.
2110
2111 SUPG is considered ready for production use for scalar advection-diffusion
2112@@ -435,25 +433,6 @@
2113 boundary term \eqref{eq:missing_boundary_term} with an integral of $\phi~g_N$
2114 over the boundary.
2115
2116-The Robin boundary condition
2117-\begin{equation*}
2118- -\vec{n}\cdot\tensor{\kappa}\cdot\grad c=h(c-c_{a}),
2119-\end{equation*}
2120-where $h$ and $c_{a}$ can be any prescribed function on the boundary $\partial\Omega$, is also imposed
2121-weakly. As for the Neumann boundary condition, weighting the Robin boundary condition with a test function
2122-and integrating over the relevant domain boundary gives the weak form:
2123-\begin{equation}\label{eq:weak_robin}
2124- - \int_{\partial\Omega} \phi~\vec{n}\cdot\tensor{\kappa}\cdot\grad c=
2125- \int_{\partial\Omega} \phi~h(c-c_{a}), \quad \text{for all }\phi.
2126-\end{equation}
2127-Thus \eqref{eq:weak_robin} can be used to replace the missing
2128-boundary term \eqref{eq:missing_boundary_term} with an integral of $\phi~h(c-c_{a})$
2129-over the relevant part of the boundary. The two terms within the integral can be treated slightly differently.
2130-The term $\phi~hc_{a}$ is always included in the right hand side of the linear system.
2131-The term $\phi~hc$ is effectively a surface absorption term where any implicit contributions
2132-are included in the left had side matrix, while any explicit contributions are included into the
2133-right hand side of the linear system.
2134-
2135 \index{boundary conditions!Dirichlet!weakly imposed}
2136 In a similar way, a weakly imposed Dirichlet boundary condition can be related to an
2137 integration by parts of the advection term. Let us consider a pure advection problem
2138
2139=== modified file 'manual/parameterisations.tex'
2140--- manual/parameterisations.tex 2012-08-30 14:23:11 +0000
2141+++ manual/parameterisations.tex 2012-09-04 15:00:37 +0000
2142@@ -83,18 +83,6 @@
2143 \end{equation}
2144 where $c_\mu^0$ is a model constant used to make $\Psi$ identifiable with any of the transitional
2145 models, e.g. $kl$, $\epsilon$, and $\omega$, by adopting the values shown in Table \ref{tab:glsparams} \citep{umlauf2003}.
2146-
2147-There is also the option to add an extra term to account for various
2148-oceanic parameters, such an internal waves breaking. This is based on the NEMO
2149-ocean model and takes a user-defined percentage of the surface $k$ and adds it down-depth
2150-using an exponential profile:
2151-\begin{equation}
2152- k_z = {k_z}_o + p*k_{\mathrm{sur}} * \exp{\left( -z / l_k \right)}
2153-\end{equation}
2154-where $k_z$ is the new turbulent kinetic energy value at depth, $z$, ${k_z}_o$ is the original TKE, $k_{\mathrm{sur}}$
2155-is the surface TKE, $p$ is a constant, and $l_k$ is a lengthscale. The options for this can be found in
2156-\option{\ldots/subgridscale\_parameterisations/gls/ocean\_parameterisation}.
2157-
2158 The second equation is:
2159 \begin{equation}
2160 \frac{\partial \Psi}{\partial t} + \bmu_i\frac{\partial \Psi}{\partial x_i} =
2161
2162=== modified file 'manual/visualisation_and_diagnostics.tex'
2163--- manual/visualisation_and_diagnostics.tex 2012-08-09 22:08:12 +0000
2164+++ manual/visualisation_and_diagnostics.tex 2012-09-04 15:00:37 +0000
2165@@ -66,7 +66,6 @@
2166 Limitations: Requires the diagnostic \option{scalar\_field::IsopycnalCoordinate} and (therefore) is not parallelised.
2167 \item[BulkMaterialPressure:]Calculates the bulk material pressure based on the MaterialDensity and MaterialVolumeFraction (and MaterialInternalEnergy if appropriate) for the equation of state of all materials.
2168 \item[CFLNumber:]CFLNumber as defined on the co-ordinate mesh. It is calculated as $\triangle t \vec{u} \mat{J}^{-1}$ where $\triangle t$ is the timestep, $\vec{u}$ the velocity and $\mat{J}$ the Jacobian.
2169-\item[CompressibleContinuityResidual:]Computes the residual of the compressible multiphase continuity equation. Used only in compressible multiphase flow simulations.
2170 \item[ControlVolumeCFLNumber:]CFL Number as defined on a control volume mesh. It is calculated as $\triangle t \frac{1}{V} \int _{cv_{face}} \vec{u}$ where $\triangle t$ is the timestep and $\vec{u}$ the velocity. The integral is taken over the faces of the control volume and $V$ is the volume of the control volume.
2171 \item[ControlVolumeDivergence:]Divergence of the velocity field where the divergence operator is defined using the control volume $\mathrm{C}^\mathrm{T}$ matrix. This assumes that the test space is discontinuous control volumes.
2172 \item[CVMaterialDensityCFLNumber:]Courant Number as defined on a control volume mesh and incorporating the MaterialDensity. Requires a MaterialDensity field!
2173
2174=== modified file 'parameterisation/Gls_vertical_turbulence_model.F90'
2175--- parameterisation/Gls_vertical_turbulence_model.F90 2012-06-08 20:26:43 +0000
2176+++ parameterisation/Gls_vertical_turbulence_model.F90 2012-09-04 15:00:37 +0000
2177@@ -42,7 +42,6 @@
2178 use Coordinates
2179 use FLDebug
2180 use fefields
2181- use vertical_extrapolation_module
2182
2183 implicit none
2184
2185@@ -355,13 +354,10 @@
2186 type(tensor_field), pointer :: tke_diff, background_diff
2187 type(scalar_field), pointer :: tke
2188 real :: prod, buoyan, diss
2189- integer :: i, stat, ele
2190+ integer :: i, stat
2191 character(len=FIELD_NAME_LEN) :: bc_type
2192 type(scalar_field), pointer :: scalar_surface
2193 type(vector_field), pointer :: positions
2194- type(scalar_field), pointer :: lumped_mass
2195- type(scalar_field) :: inverse_lumped_mass
2196-
2197
2198 ! Temporary tensor to hold rotated values if on the sphere (note: must be a 3x3 mat)
2199 real, dimension(3,3) :: K_M_sphere_node
2200@@ -371,10 +367,6 @@
2201 ! Get N^2 and M^2 -> NN2 and MM2
2202 call gls_buoyancy(state)
2203
2204- do i=1,NNodes_sur
2205- call set(NN2,top_surface_nodes(i),0.0)
2206- end do
2207-
2208 ! calculate stability function
2209 call gls_stability_function(state)
2210
2211@@ -384,35 +376,26 @@
2212 tke_diff => extract_tensor_field(state, "GLSTurbulentKineticEnergyDiffusivity")
2213 positions => extract_vector_field(state, "Coordinate")
2214
2215- ! Create a local_tke in which we can mess about with the surface values
2216- ! for creating some of the diagnostic values later
2217+ ! swap state tke for our local one - the state tke has had the upper and
2218+ ! lower boundaries ammended with the Dirichlet condition as in Warner et al
2219+ ! 2005. The local TKE is the non-amended version. This ensures a
2220+ ! non-ill-posed problem.
2221 call set(tke,local_tke)
2222- ! Assembly loop
2223- call zero(P)
2224- call zero(B)
2225- call allocate(inverse_lumped_mass, P%mesh, "InverseLumpedMass")
2226- lumped_mass => get_lumped_mass(state, P%mesh)
2227- call invert(lumped_mass, inverse_lumped_mass)
2228- ! create production terms, P, B
2229- do ele=1, ele_count(P)
2230- call assemble_tke_prodcution_terms(ele, P, B, mesh_dim(P))
2231- end do
2232- call scale(P,inverse_lumped_mass)
2233- call scale(B,inverse_lumped_mass)
2234- call deallocate(inverse_lumped_mass)
2235
2236- call zero(source)
2237- call zero(absorption)
2238- do ele = 1, ele_count(tke)
2239- call assemble_kk_src_abs(ele,tke, mesh_dim(tke))
2240- end do
2241- call allocate(inverse_lumped_mass, tke%mesh, "InverseLumpedMass")
2242- lumped_mass => get_lumped_mass(state, tke%mesh)
2243- call invert(lumped_mass, inverse_lumped_mass)
2244- ! source and absorption terms are set, apart from the / by lumped mass
2245- call scale(source,inverse_lumped_mass)
2246- call scale(absorption,inverse_lumped_mass)
2247- call deallocate(inverse_lumped_mass)
2248+ do i=1,nNodes
2249+ prod = node_val(K_M,i)*node_val(MM2,i)
2250+ buoyan = -1.*node_val(K_H,i)*node_val(NN2,i)
2251+ diss = node_val(eps,i)
2252+ if (prod+buoyan.gt.0) then
2253+ call set(source,i, prod + buoyan)
2254+ call set(absorption,i, diss / node_val(tke,i))
2255+ else
2256+ call set(source,i, prod)
2257+ call set(absorption,i, (diss-buoyan)/node_val(tke,i))
2258+ end if
2259+ call set(P, i, prod)
2260+ call set(B, i, buoyan)
2261+ enddo
2262
2263 ! set diffusivity for tke
2264 call zero(tke_diff)
2265@@ -457,66 +440,6 @@
2266 call set(scalarField,absorption)
2267 end if
2268
2269- contains
2270-
2271- subroutine assemble_tke_prodcution_terms(ele, P, B, dim)
2272-
2273- integer, intent(in) :: ele, dim
2274- type(scalar_field), intent(inout) :: P, B
2275-
2276- real, dimension(ele_loc(P,ele),ele_ngi(P,ele),dim) :: dshape_P
2277- real, dimension(ele_ngi(P,ele)) :: detwei
2278- real, dimension(ele_loc(P,ele)) :: rhs_addto_vel, rhs_addto_buoy
2279- type(element_type), pointer :: shape_p
2280- integer, pointer, dimension(:) :: nodes_p
2281-
2282- nodes_p => ele_nodes(p, ele)
2283- shape_p => ele_shape(p, ele)
2284- call transform_to_physical( positions, ele, shape_p, dshape=dshape_p, detwei=detwei )
2285-
2286- ! Shear production term:
2287- rhs_addto_vel = shape_rhs(shape_p, detwei*ele_val_at_quad(K_M,ele)*ele_val_at_quad(MM2,ele))
2288- ! Buoyancy production term:
2289- rhs_addto_buoy = shape_rhs(shape_p, -detwei*ele_val_at_quad(K_H,ele)*ele_val_at_quad(NN2,ele))
2290-
2291- call addto(P, nodes_p, rhs_addto_vel)
2292- call addto(B, nodes_p, rhs_addto_buoy)
2293-
2294- end subroutine assemble_tke_prodcution_terms
2295-
2296- subroutine assemble_kk_src_abs(ele, kk, dim)
2297-
2298- integer, intent(in) :: ele, dim
2299- type(scalar_field), intent(in) :: kk
2300-
2301- real, dimension(ele_loc(kk,ele),ele_ngi(kk,ele),dim) :: dshape_kk
2302- real, dimension(ele_ngi(kk,ele)) :: detwei
2303- real, dimension(ele_loc(kk,ele)) :: rhs_addto_disip, rhs_addto_src
2304- type(element_type), pointer :: shape_kk
2305- integer, pointer, dimension(:) :: nodes_kk
2306-
2307- nodes_kk => ele_nodes(kk, ele)
2308- shape_kk => ele_shape(kk, ele)
2309- call transform_to_physical( positions, ele, shape_kk, dshape=dshape_kk, detwei=detwei )
2310-
2311- ! ROMS and GOTM hide the absorption term in the source if the
2312- ! total is > 0. Kinda hard to do that over an element (*what* should
2313- ! be > 0?). So we don't pull this trick. Doesn't seem to make a
2314- ! difference to anything.
2315- rhs_addto_src = shape_rhs(shape_kk, detwei * (&
2316- (ele_val_at_quad(P,ele))) &
2317- )
2318- rhs_addto_disip = shape_rhs(shape_kk, detwei * ( &
2319- (ele_val_at_quad(eps,ele) - &
2320- ele_val_at_quad(B,ele)) / &
2321- ele_val_at_quad(tke,ele)) &
2322- )
2323-
2324- call addto(source, nodes_kk, rhs_addto_src)
2325- call addto(absorption, nodes_kk, rhs_addto_disip)
2326-
2327-
2328- end subroutine assemble_kk_src_abs
2329
2330 end subroutine gls_tke
2331
2332@@ -532,19 +455,9 @@
2333 type(tensor_field), pointer :: psi_diff, background_diff
2334 real :: prod, buoyan,diss,PsiOverTke
2335 character(len=FIELD_NAME_LEN) :: bc_type
2336- integer :: i, stat, ele
2337+ integer :: i, stat
2338 type(scalar_field), pointer :: scalar_surface
2339 type(vector_field), pointer :: positions
2340- type(scalar_field), pointer :: lumped_mass
2341- type(scalar_field) :: inverse_lumped_mass, vel_prod, buoy_prod
2342- ! variables for the ocean parameterisation
2343- type(csr_matrix) :: face_normal_gravity
2344- integer, dimension(:), allocatable :: ordered_elements
2345- logical, dimension(:), allocatable :: node_list
2346- logical :: got_surface
2347- real :: lengthscale, percentage, tke_surface
2348- type(scalar_field), pointer :: distanceToTop, distanceToBottom
2349- type(vector_field), pointer :: vertical_normal
2350
2351 ! Temporary tensor to hold rotated values (note: must be a 3x3 mat)
2352 real, dimension(3,3) :: psi_sphere_node
2353@@ -558,16 +471,26 @@
2354 psi => extract_scalar_field(state, "GLSGenericSecondQuantity")
2355 psi_diff => extract_tensor_field(state, "GLSGenericSecondQuantityDiffusivity")
2356 positions => extract_vector_field(state, "Coordinate")
2357- vertical_normal => extract_vector_field(state, "GravityDirection")
2358- call allocate(vel_prod, psi%mesh, "_vel_prod_psi")
2359- call allocate(buoy_prod, psi%mesh, "_buoy_prod_psi")
2360+
2361 ewrite(2,*) "In gls_psi: setting up"
2362
2363 ! store the tke in an internal field and then
2364 ! add the dirichlet conditions to the upper and lower surfaces. This
2365 ! helps stabilise the diffusivity (e.g. rapid heating cooling of the surface
2366- ! can destabilise the run)
2367+ ! can destabilise the run) but also is then done for output so we match
2368+ ! other models which also play this trick, c.f. Warner et al 2005.
2369 call set(local_tke,tke)
2370+ ! Call the bc code, but specify we want dirichlet
2371+ call gls_tke_bc(state, 'dirichlet')
2372+ ! copy the values onto the mesh using the global node id
2373+ do i=1,NNodes_sur
2374+ call set(tke,top_surface_nodes(i),node_val(top_surface_values,i))
2375+ call set(tke_old,top_surface_nodes(i),node_val(top_surface_values,i))
2376+ end do
2377+ do i=1,NNodes_bot
2378+ call set(tke,bottom_surface_nodes(i),node_val(bottom_surface_values,i))
2379+ call set(tke_old,bottom_surface_nodes(i),node_val(bottom_surface_values,i))
2380+ end do
2381
2382 ! clip at k_min
2383 do i=1,nNodes
2384@@ -576,76 +499,41 @@
2385 call set(local_tke,i, max(node_val(local_tke,i),k_min))
2386 end do
2387
2388- ! This is the extra term meant to add in internal wave breaking and the
2389- ! like. Based on a similar term in NEMO
2390- if (have_option("/material_phase[0]/subgridscale_parameterisations/GLS/ocean_parameterisation")) then
2391-
2392- distanceToTop => extract_scalar_field(state, "DistanceToTop")
2393- distanceToBottom => extract_scalar_field(state, "DistanceToBottom")
2394-
2395- call get_option("/material_phase[0]/subgridscale_parameterisations/GLS/ocean_parameterisation/lengthscale",lengthscale)
2396- call get_option("/material_phase[0]/subgridscale_parameterisations/GLS/ocean_parameterisation/percentage",percentage)
2397- ewrite(2,*) "Computing extra ocean parameterisation"
2398- allocate(node_list(NNodes))
2399- node_list = .false.
2400- ! create gravity face normal
2401- call compute_face_normal_gravity(face_normal_gravity, positions, vertical_normal)
2402- allocate(ordered_elements(size(face_normal_gravity,1)))
2403-
2404- ! create an element ordering from the mesh that moves vertically downwards
2405- call vertical_element_ordering(ordered_elements, face_normal_gravity)
2406- ! I assume the above fails gracefully if the mesh isn't suitable, hence
2407- ! no options checks are carried out
2408-
2409- got_surface = .false.
2410- do i=1, size(ordered_elements)
2411- if (.not. got_surface) then
2412- ! First time around grab, the surface TKE
2413- ! for this column
2414- tke_surface = maxval(ele_val(tke,i))
2415- got_surface = .true.
2416- end if
2417- if (got_surface) then
2418- call ocean_tke(i,tke,distanceToTop,lengthscale,percentage,tke_surface, node_list)
2419- end if
2420- if (minval(ele_val(distanceToBottom,i)) < 1e-6) then
2421- got_surface = .false.
2422- end if
2423- end do
2424-
2425- deallocate(ordered_elements)
2426- call deallocate(face_normal_gravity)
2427- deallocate(node_list)
2428-
2429- end if
2430-
2431- call allocate(inverse_lumped_mass, psi%mesh, "InverseLumpedMass")
2432- lumped_mass => get_lumped_mass(state, psi%mesh)
2433- call invert(lumped_mass, inverse_lumped_mass)
2434- ! Set Psi from previous timestep
2435+ ! re-construct psi at "old" timestep
2436 do i=1,nNodes
2437 call set(psi,i, cm0**gls_p * node_val(tke_old,i)**gls_m * node_val(ll,i)**gls_n)
2438 call set(psi,i,max(node_val(psi,i),psi_min))
2439 end do
2440
2441 ewrite(2,*) "In gls_psi: computing RHS"
2442- call zero(vel_prod)
2443- call zero(buoy_prod)
2444- call zero(source)
2445- call zero(absorption)
2446- do ele = 1, ele_count(psi)
2447- call assemble_production_terms_psi(ele, vel_prod, buoy_prod, psi, mesh_dim(psi))
2448- end do
2449- call scale(vel_prod,inverse_lumped_mass)
2450- call scale(buoy_prod,inverse_lumped_mass)
2451-
2452- do ele = 1, ele_count(psi)
2453- call assemble_psi_src_abs(ele, psi, tke_old, mesh_dim(psi))
2454- end do
2455- call scale(source,inverse_lumped_mass)
2456- call scale(absorption,inverse_lumped_mass)
2457- call deallocate(inverse_lumped_mass)
2458-
2459+ ! compute RHS
2460+ do i=1,nNodes
2461+
2462+ ! compute production terms in psi-equation
2463+ if (node_val(B,i).gt.0) then ! note that we have already set B when setting up the RHS for TKE
2464+ cPsi3=cPsi3_plus ! unstable strat
2465+ else
2466+ cPsi3=cPsi3_minus ! stable strat
2467+ end if
2468+
2469+ ! compute production terms in psi-equation
2470+ PsiOverTke = node_val(psi,i)/node_val(tke_old,i)
2471+ prod = cPsi1*PsiOverTke*node_val(P,i)
2472+ buoyan = cPsi3*PsiOverTke*node_val(B,i)
2473+ diss = cPsi2*PsiOverTke*node_val(eps,i)*node_val(Fwall,i)
2474+ if (prod+buoyan.gt.0) then
2475+ src = prod+buoyan
2476+ absn = diss/node_val(psi,i)
2477+ call set(source,i, src)
2478+ call set(absorption,i, absn)
2479+ else
2480+ src = prod
2481+ absn = (diss-buoyan)/node_val(psi,i)
2482+ call set(source,i, src)
2483+ call set(absorption,i, absn)
2484+ end if
2485+ end do
2486+
2487 ewrite(2,*) "In gls_psi: setting diffusivity"
2488 ! Set diffusivity for Psi
2489 call zero(psi_diff)
2490@@ -689,150 +577,6 @@
2491 call set(scalarField,absorption)
2492 end if
2493
2494- call deallocate(vel_prod)
2495- call deallocate(buoy_prod)
2496-
2497-
2498- contains
2499-
2500- subroutine ocean_tke(ele, tke, distanceToTop, lengthscale, percentage, tke_surface, nodes_done)
2501- type(scalar_field),pointer, intent(in) :: distanceToTop
2502- type(scalar_field),pointer, intent(out) :: tke
2503- real, intent(in) :: lengthscale, percentage, tke_surface
2504- integer, intent(in) :: ele
2505- logical, dimension(:), intent(inout) :: nodes_done
2506-
2507- integer, dimension(:), pointer :: element_nodes
2508- integer :: i, node
2509- real :: current_TKE, depth
2510-
2511- element_nodes => ele_nodes(tke, ele)
2512-
2513-
2514- ! smooth out TKE according to length scale
2515- do i = 1, size(element_nodes)
2516- node = element_nodes(i)
2517- depth = node_val(distanceToTop,node)
2518- current_TKE = node_val(tke,node)
2519- if (nodes_done(node)) then
2520- cycle
2521- end if
2522- current_TKE = current_TKE + &
2523- & percentage*TKE_surface * EXP( -depth / lengthscale )
2524- call set(tke,node,current_TKE)
2525-
2526- nodes_done(node) = .true.
2527- end do
2528-
2529- end subroutine ocean_tke
2530-
2531- subroutine reconstruct_psi(ele, psi, dim)
2532-
2533- integer, intent(in) :: ele, dim
2534- type(scalar_field), intent(inout) :: psi
2535-
2536- real, dimension(ele_loc(psi,ele),ele_ngi(psi,ele),dim) :: dshape_psi
2537- real, dimension(ele_ngi(psi,ele)) :: detwei
2538- real, dimension(ele_loc(psi,ele)) :: rhs_addto
2539- type(element_type), pointer :: shape_psi
2540- integer, pointer, dimension(:) :: nodes_psi
2541-
2542- nodes_psi => ele_nodes(psi, ele)
2543- shape_psi => ele_shape(psi, ele)
2544- call transform_to_physical( positions, ele, shape_psi, dshape=dshape_psi, detwei=detwei )
2545-
2546- rhs_addto = shape_rhs(shape_psi, detwei* &
2547- (cm0**gls_p) * &
2548- ele_val_at_quad(tke_old,ele)**gls_m * &
2549- ele_val_at_quad(ll,ele)**gls_n)
2550-
2551- call addto(psi, nodes_psi, rhs_addto)
2552-
2553- end subroutine reconstruct_psi
2554-
2555- subroutine assemble_production_terms_psi(ele, vel_prod, buoy_prod, psi, dim)
2556-
2557-
2558- integer, intent(in) :: ele, dim
2559- type(scalar_field), intent(inout) :: psi, vel_prod, buoy_prod
2560-
2561- real, dimension(ele_loc(psi,ele),ele_ngi(psi,ele),dim) :: dshape_psi
2562- real, dimension(ele_ngi(psi,ele)) :: detwei
2563- real, dimension(ele_loc(psi,ele)) :: rhs_addto_vel, rhs_addto_buoy
2564- real, dimension(ele_ngi(psi,ele)) :: cPsi3
2565- type(element_type), pointer :: shape_psi
2566- integer, pointer, dimension(:) :: nodes_psi
2567-
2568- nodes_psi => ele_nodes(psi, ele)
2569- shape_psi => ele_shape(psi, ele)
2570- call transform_to_physical( positions, ele, shape_psi, dshape=dshape_psi, detwei=detwei )
2571-
2572- ! Buoyancy production term:
2573- ! First we need to work out if cPsi3 is for stable or unstable
2574- ! stratification
2575- where(ele_val_at_quad(B,ele) .gt. 0.0)
2576- cPsi3 = cPsi3_plus ! unstable strat
2577- elsewhere
2578- cPsi3 = cPsi3_minus ! stable strat
2579- end where
2580- rhs_addto_buoy = shape_rhs(shape_psi, detwei*(cPsi3*ele_val_at_quad(B,ele)*&
2581- (ele_val_at_quad(psi, ele)/ele_val_at_quad(local_tke,ele))))
2582-
2583- ! shear production term:
2584- rhs_addto_vel = shape_rhs(shape_psi, detwei*(cPsi1*ele_val_at_quad(P,ele)*&
2585- (ele_val_at_quad(psi, ele)/ele_val_at_quad(local_tke,ele))))
2586-
2587- call addto(vel_prod, nodes_psi, rhs_addto_vel)
2588- call addto(buoy_prod, nodes_psi, rhs_addto_buoy)
2589-
2590-
2591- end subroutine assemble_production_terms_psi
2592-
2593- subroutine assemble_psi_src_abs(ele, psi, tke, dim)
2594-
2595- integer, intent(in) :: ele, dim
2596- type(scalar_field), intent(in) :: psi, tke
2597-
2598- real, dimension(ele_loc(psi,ele),ele_ngi(psi,ele),dim) :: dshape_psi
2599- real, dimension(ele_ngi(psi,ele)) :: detwei
2600- real, dimension(ele_loc(psi,ele)) :: rhs_addto_src, rhs_addto_disip
2601- type(element_type), pointer :: shape_psi
2602- integer, pointer, dimension(:) :: nodes_psi
2603-
2604- nodes_psi => ele_nodes(psi, ele)
2605- shape_psi => ele_shape(psi, ele)
2606- call transform_to_physical( positions, ele, shape_psi, dshape=dshape_psi, detwei=detwei )
2607-
2608- where (ele_val_at_quad(vel_prod,ele) + ele_val_at_quad(buoy_prod,ele) .gt. 0)
2609- rhs_addto_src = shape_rhs(shape_psi, detwei* ( &
2610- (ele_val_at_quad(vel_prod,ele)) + &
2611- (ele_val_at_quad(buoy_prod,ele)) &
2612- ) & !detwei
2613- ) ! shape_rhs
2614- rhs_addto_disip = shape_rhs(shape_psi, detwei* (&
2615- (cPsi2*ele_val_at_quad(eps,ele) * &
2616- (ele_val_at_quad(Fwall,ele)*ele_val_at_quad(psi, ele)/ele_val_at_quad(tke,ele))) / &
2617- ele_val_at_quad(psi,ele) &
2618- ) & ! detwei
2619- ) !shape_rhs
2620- elsewhere
2621- rhs_addto_src = shape_rhs(shape_psi, detwei * ( &
2622- (ele_val_at_quad(vel_prod,ele)) &
2623- ) & !detwei
2624- ) !shape_rhs
2625- rhs_addto_disip = shape_rhs(shape_psi, detwei * (&
2626- ((cPsi2*ele_val_at_quad(eps,ele) * &
2627- (ele_val_at_quad(Fwall,ele)*ele_val_at_quad(psi, ele)/ele_val_at_quad(tke,ele))) - &! disipation term
2628- (ele_val_at_quad(buoy_prod,ele)))/ & ! buoyancy term
2629- ele_val_at_quad(psi,ele)&
2630- ) & !detwei
2631- ) !shape_rhs
2632- end where
2633- call addto(source, nodes_psi, rhs_addto_src)
2634- call addto(absorption, nodes_psi, rhs_addto_disip)
2635-
2636- end subroutine assemble_psi_src_abs
2637-
2638 end subroutine gls_psi
2639
2640
2641@@ -872,15 +616,27 @@
2642 velocity => extract_vector_field(state, "Velocity")
2643
2644 call allocate(tke, tke_state%mesh, name="MyLocalTKE")
2645- !if (gls_n > 0) then
2646+ if (gls_n > 0) then
2647 ! set the TKE to use below to the unaltered TKE
2648- ! with no changes to the upper/lower surfaces
2649+ ! with no chnages tothe upper/lower surfaces
2650 ! Applies to k-kl model only
2651- ! call set (tke, local_tke)
2652- !else
2653+ call set (tke, local_tke)
2654+ else
2655 ! Use the altered TKE to get the surface diffusivity correct
2656 call set (tke, tke_state)
2657- !end if
2658+ end if
2659+
2660+ ! call the bc code, but specify we want dirichlet
2661+ if (gls_n < 0) then
2662+ call gls_psi_bc(state, 'dirichlet')
2663+ ! copy the values onto the mesh using the global node id
2664+ do i=1,NNodes_sur
2665+ call set(psi,top_surface_nodes(i),node_val(top_surface_values,i))
2666+ end do
2667+ do i=1,NNodes_bot
2668+ call set(psi,bottom_surface_nodes(i),node_val(bottom_surface_values,i))
2669+ end do
2670+ end if
2671
2672 exp1 = 3.0 + gls_p/gls_n
2673 exp2 = 1.5 + gls_m/gls_n
2674@@ -913,12 +669,12 @@
2675
2676 ! compute dissipative scale
2677 call set(ll,i,cde*sqrt(tke_cur**3.)/node_val(eps,i))
2678- !if (gls_n > 0) then
2679- ! if (node_val(NN2,i) > 0) then
2680- ! limit = sqrt(0.56 * tke_cur / node_val(NN2,i))
2681- ! call set(ll,i,min(limit,node_val(ll,i)))
2682- ! end if
2683- !end if
2684+ if (gls_n > 0) then
2685+ if (node_val(NN2,i) > 0) then
2686+ limit = sqrt(0.56 * tke_cur / node_val(NN2,i))
2687+ call set(ll,i,min(limit,node_val(ll,i)))
2688+ end if
2689+ end if
2690
2691 end do
2692
2693@@ -1684,24 +1440,20 @@
2694 case("neumann")
2695 do i=1,NNodes_sur
2696 ! GOTM Boundary
2697- value = -(gls_n*(cm0**(gls_p+1.))*(kappa**(gls_n+1.)))/sigma_psi &
2698- *node_val(top_surface_tke_values,i)**(gls_m+0.5)*(z0s(i))**gls_n
2699- ! Warner 2005 - left here for posterity and debugging
2700- !value = -gls_n*(cm0**(gls_p))*(node_val(top_surface_tke_values,i)**gls_m)* &
2701- ! (kappa**gls_n)*(z0s(i)**(gls_n-1))*((node_val(top_surface_km_values,i)/sigma_psi))
2702+ !value = -(gls_n*(cm0**(gls_p+1.))*(kappa**(gls_n+1.)))/sigma_psi &
2703+ ! *node_val(top_surface_values,i)**(gls_m+0.5)*(z0s(i))**gls_n
2704+ ! Warner 2005
2705+ value = -gls_n*(cm0**(gls_p))*(node_val(top_surface_tke_values,i)**gls_m)* &
2706+ (kappa**gls_n)*(z0s(i)**(gls_n-1))*((node_val(top_surface_km_values,i)/sigma_psi))
2707 call set(top_surface_values,i,value)
2708 end do
2709 do i=1,NNodes_bot
2710- if (u_taub_squared(i) < 1e-16) then
2711- value = 0.0
2712- else
2713- ! GOTM Boundary
2714- value = - gls_n*cm0**(gls_p+1.)*(kappa**(gls_n+1.)/sigma_psi) &
2715- *node_val(bottom_surface_tke_values,i)**(gls_m+0.5)*(z0b(i))**gls_n
2716- ! Warner 2005 - as above
2717- !value = gls_n*cm0**(gls_p)*node_val(bottom_surface_tke_values,i)**(gls_m)* &
2718- ! kappa**gls_n*(z0b(i)**(gls_n-1))*(node_val(bottom_surface_km_values,i)/sigma_psi)
2719- end if
2720+ ! GOTM Boundary
2721+ !value = - gls_n*cm0**(gls_p+1.)*(kappa**(gls_n+1.)/sigma_psi) &
2722+ ! *node_val(bottom_surface_tke_values,i)**(gls_m+0.5)*(z0b(i))**gls_n
2723+ ! Warner 2005
2724+ value = gls_n*cm0**(gls_p)*node_val(bottom_surface_tke_values,i)**(gls_m)* &
2725+ kappa**gls_n*(z0b(i)**(gls_n-1))*(node_val(bottom_surface_km_values,i)/sigma_psi)
2726 call set(bottom_surface_values,i,value)
2727 end do
2728 case("dirichlet")
2729@@ -1741,7 +1493,7 @@
2730 integer :: nobcs
2731 integer :: i,ii, MaxIter
2732 real :: rr
2733- real :: charnock_val=18500.
2734+ real :: charnock_val=1400.
2735 character(len=OPTION_PATH_LEN) :: bctype
2736 type(vector_field), pointer :: wind_surface_field, positions, velocity
2737 type(scalar_field), pointer :: tke
2738@@ -1835,15 +1587,12 @@
2739
2740 do i=1,NNodes_bot
2741 temp_vector_3D = node_val(bottom_velocity,i)
2742- u_taub = sqrt(temp_vector_3D(1)**2+temp_vector_3D(2)**2+temp_vector_3D(3)**2)
2743- if (u_taub <= 1e-12) then
2744- z0b(i) = z0s_min
2745- else
2746+ u_taub = max(1e-12,sqrt(temp_vector_3D(1)**2+temp_vector_3D(2)**2+temp_vector_3D(3)**2))
2747
2748
2749 ! iterate bottom roughness length MaxIter times
2750 do ii=1,MaxIter
2751- z0b(i)=(1e-7/max(1e-6,u_taub)+0.03*0.1)
2752+ z0b(i)=1e-7/max(1e-6,u_taub)+0.03*0.1
2753
2754 ! compute the factor r
2755 rr=kappa/log(z0b(i))
2756@@ -1852,7 +1601,6 @@
2757 u_taub = rr*sqrt(temp_vector_3D(1)**2+temp_vector_3D(2)**2+temp_vector_3D(3)**2)
2758
2759 end do
2760- end if
2761
2762 u_taub_squared(i) = u_taub**2
2763 end do
2764@@ -1861,7 +1609,7 @@
2765 if (surface_allocated) then
2766 do i=1,NNodes_sur
2767 temp_vector_1D = node_val(surface_forcing,i)
2768- u_taus_squared(i) = max(1e-12,abs(temp_vector_1D(1)))
2769+ u_taus_squared(i) = max(1e-12,temp_vector_1D(1))
2770 ! use the Charnock formula to compute the surface roughness
2771 z0s(i)=charnock_val*u_taus_squared(i)/gravity_magnitude
2772 if (z0s(i).lt.z0s_min) z0s(i)=z0s_min
2773@@ -1874,11 +1622,11 @@
2774
2775 do i=1,NNodes_bot
2776 temp_vector_2D = node_val(bottom_velocity,i)
2777- u_taub = sqrt(temp_vector_2D(1)**2+temp_vector_2D(2)**2)
2778+ u_taub = max(1e-12,sqrt(temp_vector_2D(1)**2+temp_vector_2D(2)**2))
2779
2780 ! iterate bottom roughness length MaxIter times
2781 do ii=1,MaxIter
2782- z0b(i)=(1e-7/(max(1e-6,u_taub)+0.03*0.1))
2783+ z0b(i)=1e-7/(max(1e-6,u_taub)+0.03*0.1)
2784 rr=kappa/log(z0b(i))
2785
2786 ! compute the friction velocity at the bottom
2787
2788=== added file 'parameterisation/Ice_Melt_Interface.F90'
2789--- parameterisation/Ice_Melt_Interface.F90 1970-01-01 00:00:00 +0000
2790+++ parameterisation/Ice_Melt_Interface.F90 2012-09-04 15:00:37 +0000
2791@@ -0,0 +1,950 @@
2792+! Copyright (C) 2006 Imperial College London and others.
2793+!
2794+! Please see the AUTHORS file in the main source directory for a full list
2795+! of copyright holders.
2796+!
2797+! Prof. C Pain
2798+! Applied Modelling and Computation Group
2799+! Department of Earth Science and Engineering
2800+! Imperial College London
2801+!
2802+! amcgsoftware@imperial.ac.uk
2803+!
2804+! This library is free software; you can redistribute it and/or
2805+! modify it under the terms of the GNU Lesser General Public
2806+! License as published by the Free Software Foundation,
2807+! version 2.1 of the License.
2808+!
2809+! This library is distributed in the hope that it will be useful,
2810+! but WITHOUT ANY WARRANTY; without even the implied arranty of
2811+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2812+! Lesser General Public License for more details.
2813+!
2814+! You should have received a copy of the GNU Lesser General Public
2815+! License along with this library; if not, write to the Free Software
2816+! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2817+! USA
2818+
2819+#include "fdebug.h"
2820+
2821+module ice_melt_interface
2822+ use quadrature
2823+ use elements
2824+ use field_derivatives
2825+ use fields
2826+ use fields_base
2827+ use field_options
2828+ use fields_allocates
2829+ use state_module
2830+ use spud
2831+ use global_parameters, only: FIELD_NAME_LEN, OPTION_PATH_LEN
2832+ use state_fields_module
2833+ use boundary_conditions
2834+ use fields_manipulation
2835+ use surface_integrals
2836+ use fetools
2837+ use vector_tools
2838+ use FLDebug
2839+ use integer_set_module
2840+ use pickers_inquire
2841+ use transform_elements
2842+ use state_fields_module
2843+ use global_parameters, only: PYTHON_FUNC_LEN
2844+ use embed_python
2845+implicit none
2846+
2847+ private
2848+ integer, save :: nnodes, dimen
2849+ type(vector_field), save :: surface_positions
2850+ type(vector_field), save :: funky_positions
2851+ ! Normals to the iceshelf interface surface
2852+ type(vector_field), save :: unit_normal_vectors
2853+ integer, dimension(:), pointer, save :: sf_nodes_point
2854+
2855+ ! Fields and variables for the interface surface
2856+ type(scalar_field), save :: ice_surfaceT, ice_surfaceS! these are used to populate the bcs
2857+
2858+ public :: melt_interface_initialisation, populate_iceshelf_boundary_conditions, melt_interface_calculate, melt_interface_boundary_condition, melt_interface_cleanup, melt_interface_allocate_surface
2859+
2860+contains
2861+
2862+ ! Calculation of the (ice) interface meltrate using the parameterisation described in Holland et al. 2008 doi:10.1175/2007JCLI1909.1
2863+
2864+ !----------
2865+ ! initialise parameters based on options
2866+ !----------
2867+
2868+ subroutine melt_interface_initialisation(state)
2869+
2870+ type(state_type), intent(inout) :: state
2871+ type(scalar_field), pointer :: T, S
2872+ ! For the case when Dirichlet boundary conditions are used
2873+ character(len=FIELD_NAME_LEN) :: bc_type
2874+ type(integer_set) :: surface_ids
2875+ integer, dimension(:),allocatable :: interface_surface_id
2876+ integer, dimension(2) :: shape_option
2877+ type(mesh_type), pointer :: mesh
2878+ type(mesh_type) :: surface_mesh
2879+ ! Allocated and returned by create_surface_mesh
2880+ integer, dimension(:), pointer :: surface_nodes
2881+ integer, dimension(:), allocatable :: surface_element_list
2882+ integer :: i,the_node
2883+ ! For input of the hydrostatic pressure
2884+ character(len=PYTHON_FUNC_LEN) :: subshelf_hydrostatic_pressure_function
2885+ real :: current_time
2886+ type(vector_field), pointer :: positions
2887+ type(scalar_field), pointer :: subshelf_hydrostatic_pressure
2888+ real :: c0, cI, L, TI, a, b, gammaT, gammaS, farfield_distance
2889+ real :: T_steady, S_steady
2890+ logical :: calculate_boundaries_T, calculate_boundaries_S
2891+ logical :: calculate_boundary_temperature, calculate_boundary_salinity
2892+ character(len=*), parameter :: option_path = '/ocean_forcing/iceshelf_meltrate/Holland08'
2893+
2894+ ewrite(1,*) 'Melt interface initialisation begins'
2895+
2896+ call melt_interface_read_coefficients(c0=c0, cI=cI, L=L, TI=TI, a=a, b=b, gammaT=gammaT, gammaS=gammaS, farfield_distance=farfield_distance, T_steady=T_steady, S_steady=S_steady)
2897+
2898+ calculate_boundary_temperature=have_option(trim(option_path)//'/calculate_boundaries/bc_value_temperature')
2899+ calculate_boundary_salinity=have_option(trim(option_path)//'/calculate_boundaries/bc_value_salinity')
2900+ if (calculate_boundary_temperature) write(3,*) "Melt interface, initialised T_steady", T_steady
2901+ if (calculate_boundary_salinity) write(3,*) "Melt interface, initialised S_steady", S_steady
2902+
2903+ ! bc= Dirichlet initialize T and S at the ice-ocean interface
2904+ ! This change with bc type
2905+ if (have_option(trim(option_path)//'/calculate_boundaries')) then
2906+ call get_option(trim(option_path)//'/calculate_boundaries', bc_type)
2907+ select case(bc_type)
2908+ case('neumann')
2909+ ewrite(3,*) 'Calculating steady Neumann boundary condition - nothing to be done here.'
2910+ case('dirichlet')
2911+ ewrite(3,*) 'Calculating steady Dirichlet boundary condition - nothing to be done here.'
2912+ ! Define values of temperature and salinity at ice-ocean interface
2913+ ! Get the surface_id of the ice-ocean interface
2914+ ! Note this code needs working through - Neumann is definitely the best choice here!
2915+ shape_option = option_shape(trim(option_path)//"/melt_surfaceID")
2916+ allocate(interface_surface_id(1:shape_option(1)))
2917+ call get_option(trim(option_path)//'/melt_surfaceID', interface_surface_id)
2918+ call allocate(surface_ids)
2919+ call insert(surface_ids,interface_surface_id)
2920+ mesh => extract_mesh(state,"VelocityMesh")
2921+ ! Obtain surface_mesh, surface_element_list, from mesh and surface_id
2922+ call melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
2923+
2924+ T => extract_scalar_field(state,"Temperature")
2925+ S => extract_scalar_field(state,"Salinity")
2926+ do i=1, size(surface_nodes)
2927+ the_node = surface_nodes(i)
2928+ call set(T,the_node,T_steady)
2929+ call set(S,the_node,S_steady)
2930+ enddo
2931+ case default
2932+ FLAbort('Unknown boundary type for ice-ocean interface.')
2933+ end select
2934+ else
2935+
2936+ endif
2937+
2938+ ! Read the hydrostatic pressure
2939+ if(have_option(trim(option_path)//'/subshelf_hydrostatic_pressure')) then
2940+ call get_option(trim(option_path)//'/subshelf_hydrostatic_pressure/python', subshelf_hydrostatic_pressure_function)
2941+ ! Get current time
2942+ call get_option("/timestepping/current_time", current_time)
2943+ ! Set initial condition from python function
2944+ ! TODO: This field should be generated automatically and is not needed in the schema
2945+ subshelf_hydrostatic_pressure => extract_scalar_field(state,"subshelf_hydrostatic_pressure")
2946+ positions => extract_vector_field(state,"Coordinate")
2947+ call set_from_python_function(subshelf_hydrostatic_pressure, trim(subshelf_hydrostatic_pressure_function), positions, current_time)
2948+ ewrite(1,*) "Melt interface initialisation, found hydrostatic pressure field"
2949+ endif
2950+
2951+ ! Additional initialisation routines
2952+ call melt_interface_allocate_surface(state)
2953+ call melt_interface_calculate(state)
2954+ ! Boundary conditions for the (ice) melt interface parameterisation
2955+ if (have_option(trim(option_path)//'/calculate_boundaries')) then
2956+ call melt_interface_boundary_condition(state)
2957+ endif
2958+
2959+ ewrite(1,*) "Melt interface initialisation end"
2960+
2961+ end subroutine melt_interface_initialisation
2962+
2963+ subroutine populate_iceshelf_boundary_conditions(state)
2964+ type(state_type), intent(in) :: state
2965+ type(scalar_field), pointer :: T,S
2966+ character(len=FIELD_NAME_LEN) :: bc_type
2967+ integer, dimension(:), allocatable :: surf_id
2968+ integer, dimension(2) :: shape_option
2969+ type(integer_set) :: surface_ids
2970+ !!
2971+ type(mesh_type), pointer :: surface_mesh
2972+ type(scalar_field) :: scalar_surface_field
2973+
2974+ ewrite(1,*) "-----*** Begin iceshelf BC-----"
2975+ ! Get vector of surface ids
2976+ shape_option=option_shape("/ocean_forcing/iceshelf_meltrate/Holland08/melt_surfaceID")
2977+ allocate(surf_id(1:shape_option(1)))
2978+ call get_option("/ocean_forcing/iceshelf_meltrate/Holland08/melt_surfaceID", surf_id)
2979+ ewrite(1,*) "surf_id", surf_id
2980+ call get_option("/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries", bc_type)
2981+ call allocate(surface_ids)
2982+ call insert(surface_ids,surf_id)
2983+ ewrite(1,*) "set2vector(surface_ids)", set2vector(surface_ids)
2984+
2985+ ewrite(1,*) "bc_type: ", bc_type
2986+ ! Add boundary condition
2987+ T => extract_scalar_field(state,"Temperature")
2988+ S => extract_scalar_field(state,"Salinity")
2989+! do i=1,node_count(T)
2990+! ewrite(1,*) "line2100,i,node_val(T,i): ",i, node_val(T,i)
2991+! enddo
2992+ call add_boundary_condition(T, 'temperature_iceshelf_BC', bc_type, surf_id)
2993+ call add_boundary_condition(S, 'salinity_iceshelf_BC', bc_type, surf_id)
2994+ deallocate(surf_id)
2995+
2996+ ! mesh of only the part of the surface where this b.c. applies for temperature
2997+ call get_boundary_condition(T, 'temperature_iceshelf_BC', surface_mesh=surface_mesh)
2998+ call allocate(scalar_surface_field, surface_mesh, name="value")
2999+ call insert_surface_field(T, 'temperature_iceshelf_BC', scalar_surface_field)
3000+ call deallocate(scalar_surface_field)
3001+
3002+ ! mesh of only the part of the surface where this b.c. applies for salinity
3003+ call get_boundary_condition(S, 'salinity_iceshelf_BC', surface_mesh=surface_mesh)
3004+ call allocate(scalar_surface_field, surface_mesh, name="value")
3005+ call insert_surface_field(S, 'salinity_iceshelf_BC', scalar_surface_field)
3006+ call deallocate(scalar_surface_field)
3007+
3008+ ewrite(1,*) "-----*** End iceshelf BC-----"
3009+ end subroutine populate_iceshelf_boundary_conditions
3010+
3011+ subroutine melt_interface_calculate(state)
3012+ ! Calculate the melt rate
3013+
3014+ type(state_type), intent(inout) :: state
3015+ type(scalar_field), pointer :: Tb, Sb,MeltRate,Heat_flux,Salt_flux
3016+ type(scalar_field), pointer :: scalarfield
3017+ type(vector_field), pointer :: velocity,positions
3018+ ! Debugging purpose pointer
3019+ type(scalar_field), pointer :: T_loc,S_loc,P_loc
3020+ type(vector_field), pointer :: V_loc,Location,Location_org
3021+ real, dimension(:), allocatable :: vel
3022+ integer :: i
3023+ ! Some internal variables
3024+ real :: speed, T,S,P,Aa,Bb,Cc,topo
3025+ real :: c0, cI, L, TI, a, b, gammaT, gammaS, farfield_distance
3026+ real ::loc_Tb,loc_Sb,loc_meltrate,loc_heatflux,loc_saltflux
3027+ ! Aa*Sb^2+Bv*Sb+Cc
3028+ ! Sink mesh part
3029+ integer :: ele,stat,the_node
3030+ real, dimension(:), allocatable :: local_coord,coord
3031+ integer, dimension(:), allocatable :: surface_node_list
3032+ type(scalar_field) :: re_pressure,re_DistanceToTop
3033+ type(scalar_field), pointer :: temperature, salinity
3034+
3035+ ewrite(1,*) "Melt interface calculation begins"
3036+
3037+ call melt_interface_read_coefficients(c0=c0, cI=cI, L=L, TI=TI, a=a, b=b, gammaT=gammaT, gammaS=gammaS, farfield_distance=farfield_distance)
3038+
3039+ !! All the variable under /ocean_forcing/iceshelf_meltrate/Holland08 should be in coordinate mesh
3040+ !! coordinate mesh = continous mesh
3041+ MeltRate => extract_scalar_field(state,"MeltRate")
3042+ call set(MeltRate,0.0)
3043+ Tb => extract_scalar_field(state,"Tb")
3044+ Sb => extract_scalar_field(state,"Sb")
3045+ ! call set(MeltRate,setnan(arg))
3046+ call set(Tb,0.0)
3047+ call set(Sb,-1.0)
3048+ Heat_flux => extract_scalar_field(state,"Heat_flux")
3049+ Salt_flux => extract_scalar_field(state,"Salt_flux")
3050+ call set(Heat_flux,0.0)
3051+ call set(Salt_flux,0.0)
3052+
3053+ ! Local far field values
3054+ T_loc => extract_scalar_field(state,"Tloc")
3055+ S_loc => extract_scalar_field(state,"Sloc")
3056+ P_loc => extract_scalar_field(state,"Ploc")
3057+ V_loc => extract_vector_field(state,"Vloc")
3058+ Location => extract_vector_field(state,"Location")
3059+ Location_org => extract_vector_field(state,"Location_org")
3060+ call set(T_loc,0.0)
3061+ call set(S_loc,-1.0)
3062+ call set(P_loc,-1.0)
3063+ allocate(vel(V_loc%dim))
3064+ vel = 0.0
3065+ call set(V_loc,vel)
3066+ call set(Location,vel)
3067+ call set(Location_org,vel)
3068+
3069+ positions => extract_vector_field(state,"Coordinate")
3070+
3071+ ! Surface node list
3072+ allocate(surface_node_list(size(sf_nodes_point)))
3073+ ! sf_nodes is calculated in "melt_allocate_surface"
3074+ surface_node_list=sf_nodes_point
3075+
3076+ ! Pressure read the hydrostatic pressure, specified at schema
3077+ scalarfield => extract_scalar_field(state,"subshelf_hydrostatic_pressure")
3078+ call allocate(re_pressure,positions%mesh, name="RePressure")
3079+ call remap_field(scalarfield,re_pressure,stat)
3080+
3081+ if (have_option("/geometry/ocean_boundaries/scalar_field::DistanceToBottom")) then
3082+ scalarfield => extract_scalar_field(state,"DistanceToBottom")
3083+ call allocate(re_DistanceToTop,positions%mesh, name="ReDistanceToBottom")
3084+ call remap_field(scalarfield,re_DistanceToTop,stat)
3085+ endif
3086+
3087+ allocate(local_coord(positions%dim+1))
3088+ allocate(coord(positions%dim))
3089+
3090+ temperature => extract_scalar_field(state,"Temperature")
3091+ salinity => extract_scalar_field(state,"Salinity")
3092+ velocity => extract_vector_field(state,"Velocity")
3093+
3094+ ! Loop over the surface nodes to calculate melt rate
3095+ do i=1,size(surface_node_list)
3096+ the_node = surface_node_list(i)
3097+ ! Interpolating
3098+ coord = node_val(funky_positions,the_node)
3099+ call picker_inquire(positions, coord, ele, local_coord,global=.false.)
3100+
3101+ !! If sum(local_coord) is not equal to 1,
3102+ !! we know that this coord (funky position) does not exist in the domain.
3103+ if (sum(local_coord) .gt. 2.0) then
3104+ ewrite(0,*) "Melt interface, funky coordinate: ", node_val(funky_positions,the_node)
3105+ !! node_val(surface_positions,the_node) = node_val(positions,the_node)
3106+ ewrite(0,*) "Melt interface, original element: ",ele
3107+ ewrite(0,*) "Melt interface, sum of local_coord: ", sum(local_coord)
3108+ FLExit("Melt interface, your funky_positions is out of the domain. Change melt_LayerLength.")
3109+ endif
3110+
3111+ !Number of nodes per element for temperature
3112+ ! Find T at a particular element given the field,element,
3113+ !INPUT: field, element number
3114+ !Output: real value
3115+
3116+ call scalar_finder_ele(temperature,ele,positions%dim+1,local_coord,T)
3117+ call scalar_finder_ele(salinity,ele,positions%dim+1,local_coord,S)
3118+ call vector_finder_ele(velocity,ele,positions%dim+1,local_coord,vel)
3119+
3120+ ! Pressure from the schema
3121+ P = node_val(re_pressure,the_node)
3122+ speed = sqrt(sum(vel**2))
3123+
3124+ if (speed .lt. 0.001) then
3125+ speed = 0.001
3126+ !TODO: Use a maxval here instead
3127+ endif
3128+ ! constant = -7.53e-8 [C Pa^(-1)] comes from Holland and Jenkins Table 1
3129+ ! TODO: Define as a constant explicitly, i.e. real, parameter :: topo = -7.53e-8
3130+ topo = -7.53e-8*P
3131+
3132+ ! Define Aa,Bb,Cc
3133+ ! Aa*Sb**2 + Bb*Sb + Cc = 0.0
3134+ Aa = -gammaS * speed * cI * a + a * c0 * gammaT * speed
3135+
3136+ Bb = -gammaS * speed * L + gammaS * speed * S * cI * a
3137+ Bb = Bb - gammaS * speed * cI * (b + topo) + gammaS * speed * cI * TI
3138+ Bb = Bb - c0 * gammaT * speed * T + c0 * gammaT * speed * (b + topo)
3139+
3140+ Cc = gammaS * speed * S * L + gammaS * speed * S * cI * (b + topo) + gammaS * speed * S * (-cI * TI)
3141+
3142+ ! This could be a linear equation if Aa=0
3143+ if (Aa .eq. 0.0) then
3144+ loc_Sb = -Cc/Bb
3145+ else
3146+ ! Calculate for the 2nd oewrite(1,*) "size(surface_element_list)"rder polynomial.
3147+ ! We have two solutions.
3148+ loc_Sb = (-Bb + sqrt(Bb**2 - 4.0*Aa*Cc))/(2.0*Aa)
3149+ ! loc_Sb has to be larger than 0; since the salinity in the ocean is positive definite.
3150+ if (loc_Sb .lt. 0.0) then
3151+ loc_Sb = (-Bb - sqrt(Bb**2 - 4.0*Aa*Cc))/(2.0*Aa)
3152+ endif
3153+ if (loc_Sb .lt. 0.0) then
3154+ ewrite(0,*) "Melt interface, loc_Sb: ", loc_Sb
3155+ ewrite(0,*) "Melt interface, Aa: ", Aa
3156+ ewrite(0,*) "Melt interface, Bb: ", Bb
3157+ ewrite(0,*) "Melt interface, Cc: ", Cc
3158+ ewrite(0,*) "Melt interface, T: ", T
3159+ ewrite(0,*) "Melt interface, S: ", S
3160+ ewrite(0,*) "Melt interface, P: ", P
3161+ ewrite(0,*) "Melt interface, speed: ", speed
3162+ FLExit("Melt interface, Sb is negative. The range of Salinity is not right.")
3163+ endif
3164+ endif
3165+
3166+ loc_Tb = a*loc_Sb + b + topo
3167+ loc_meltrate = gammaS*speed*(S-loc_Sb)/loc_Sb
3168+ !! Heat flux to the ocean
3169+ loc_heatflux = (gammaT*speed+ loc_meltrate)*(loc_Tb - T) ! or loc_meltrate*L + loc_meltrate*cI*(loc_Tb-TI)
3170+ !! Salt flux to the ocean
3171+ loc_saltflux = (gammaS*speed+loc_meltrate)*(loc_Sb - S)
3172+
3173+ !! These are needed to implement BCs.
3174+ call set(MeltRate, the_node, loc_meltrate)
3175+ call set(Tb, the_node, loc_Tb)
3176+ call set(Sb, the_node, loc_Sb)
3177+ call set(Heat_flux, the_node,loc_heatflux)
3178+ call set(Salt_flux, the_node,loc_saltflux)
3179+
3180+ !!Far field values
3181+ call set(T_loc,the_node,T)
3182+ call set(S_loc,the_node,S)
3183+ call set(P_loc,the_node,P)
3184+ call set(V_loc,the_node,vel)
3185+ call set(Location,the_node,node_val(funky_positions,the_node))
3186+ call set(Location_org,the_node,node_val(positions,the_node))
3187+
3188+ enddo
3189+
3190+ deallocate(local_coord)
3191+ deallocate(coord)
3192+ call deallocate(re_pressure)
3193+ ! TODO: check this below
3194+ if (have_option("/geometry/ocean_boundaries/scalar_field::DistanceToBottom")) then
3195+ call deallocate(re_DistanceToTop)
3196+ endif
3197+
3198+ ! Remap meltrate_p heat_flux_p salt_flux_p onto MeltRate Heat_flux Salt_flux
3199+ ! Mappting continous mesh to discontinous mesh
3200+
3201+ ewrite(1,*) "Melt interface calculation end"
3202+
3203+ end subroutine melt_interface_calculate
3204+
3205+ subroutine melt_interface_boundary_condition(state)
3206+ ! See preprocessor/Boundary_Conditions_From_options.F90 populate_iceshelf_boundary_conditions(states(1)) as well
3207+ type(state_type), intent(inout) :: state
3208+ ! Boundary conditions
3209+ type(scalar_field), pointer :: TT,SS
3210+ type(scalar_field), pointer :: scalar_surface, scalar_surface2
3211+ type(mesh_type), pointer :: ice_mesh
3212+ character(len=FIELD_NAME_LEN) :: bc_type
3213+ type(scalar_field) :: T_bc,S_bc
3214+ type(scalar_field), pointer :: Tflux,Sflux
3215+ type(scalar_field), pointer :: Tb,Sb
3216+ integer :: i, the_node
3217+ real :: Tz,Sz
3218+ type(mesh_type), pointer :: mesh
3219+ type(mesh_type) :: surface_mesh
3220+ integer, dimension(:), pointer :: surface_nodes ! allocated and returned by create_surface_mesh
3221+ integer, dimension(:), allocatable :: surface_element_list
3222+ type(integer_set) :: surface_ids
3223+ integer, dimension(:),allocatable :: surf_id
3224+ integer, dimension(2) :: shape_option
3225+ type(vector_field) :: unit_normal_vectors_vel
3226+ type(scalar_field) :: heat_flux_vel,salt_flux_vel
3227+ type(vector_field), pointer :: velocity
3228+ integer :: stat
3229+ real, dimension(:), allocatable :: vel
3230+ real :: T_steady, S_steady
3231+ logical :: calculate_boundary_temperature, calculate_boundary_salinity
3232+ character(len=*), parameter :: option_path = '/ocean_forcing/iceshelf_meltrate/Holland08'
3233+ character(len=*), parameter :: option_path_bc = '/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries'
3234+
3235+ ewrite(1,*) "Melt interface boundary condition begins"
3236+ call melt_interface_read_coefficients(T_steady=T_steady, S_steady=S_steady)
3237+ calculate_boundary_temperature=have_option(trim(option_path)//'/calculate_boundaries/bc_value_temperature')
3238+ calculate_boundary_salinity=have_option(trim(option_path)//'/calculate_boundaries/bc_value_salinity')
3239+
3240+!! See ./preprocessor/Boundary_Conditions_From_options.F90 populate_iceshelf_boundary_conditions(states(1)) as well
3241+ ! Get the surface_id of the ice-ocean interface
3242+ shape_option=option_shape(trim(option_path)//"/melt_surfaceID")
3243+ allocate(surf_id(1:shape_option(1)))
3244+ call get_option(trim(option_path)//'/melt_surfaceID',surf_id)
3245+ call allocate(surface_ids)
3246+ call insert(surface_ids,surf_id)
3247+
3248+ TT=> extract_scalar_field(state,"Temperature")
3249+ SS=> extract_scalar_field(state,"Salinity")
3250+ velocity => extract_vector_field(state,"Velocity")
3251+
3252+ if (node_count(TT) .eq. node_count(velocity)) then
3253+ mesh => extract_mesh(state,"VelocityMesh")
3254+ else
3255+ mesh => extract_mesh(state,"CoordinateMesh")
3256+ endif
3257+ call melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
3258+
3259+!! Remapt the contious mesh to discon, unit_normal vecotr
3260+
3261+ call allocate(unit_normal_vectors_vel,velocity%dim,TT%mesh,"MyVelocitySurfaceMesh")
3262+
3263+ allocate(vel(velocity%dim))
3264+ vel = 0.0
3265+ call set(unit_normal_vectors_vel,vel)
3266+ deallocate(vel)
3267+
3268+ call remap_field(unit_normal_vectors,unit_normal_vectors_vel,stat)
3269+
3270+ call allocate(T_bc,TT%mesh, name="T_boundary")
3271+ call allocate(S_bc,SS%mesh, name="S_boundary")
3272+
3273+ ! Surface node list
3274+ ! This change with bc type
3275+ call get_option(trim(option_path_bc), bc_type)
3276+
3277+ select case(bc_type)
3278+ case("neumann")
3279+ Tflux => extract_scalar_field(state,"Heat_flux")
3280+ Sflux => extract_scalar_field(state,"Salt_flux")
3281+
3282+ !! Remap the heat flux
3283+ call allocate(heat_flux_vel,TT%mesh,"MyHeatFluxSurfaceMesh")
3284+ call set(heat_flux_vel,0.0)
3285+ call remap_field(Tflux,heat_flux_vel,stat)
3286+
3287+ !! Remap the salt flux
3288+ call allocate(salt_flux_vel,SS%mesh,"MySaltFluxSurfaceMesh")
3289+ call set(salt_flux_vel,0.0)
3290+ call remap_field(Sflux,salt_flux_vel,stat)
3291+
3292+ ! create a surface mesh to place values onto. This is for the top surface
3293+ call get_boundary_condition(TT, 'temperature_iceshelf_BC', surface_mesh=ice_mesh)
3294+ call allocate(ice_surfaceT, ice_mesh, name="ice_surfaceT")
3295+ !! Temperature
3296+ scalar_surface => extract_surface_field(TT, 'temperature_iceshelf_BC', "value")
3297+
3298+ do i=1,node_count(ice_surfaceT)
3299+ the_node = surface_nodes(i)
3300+ ! Kt = (sum(node_val(unit_normal_vectors_vel,the_node)*Kt_ar))
3301+ ! Tz = node_val(heat_flux_vel,the_node)/(-Kt*c0)
3302+ Tz = node_val(heat_flux_vel,the_node)
3303+ call set(scalar_surface,i,Tz)
3304+ enddo
3305+
3306+ ! Salinity
3307+ call get_boundary_condition(SS, 'salinity_iceshelf_BC', surface_mesh=ice_mesh)
3308+ call allocate(ice_surfaceS, ice_mesh, name="ice_surfaceS")
3309+ !! Salinity
3310+ scalar_surface2 => extract_surface_field(SS, 'salinity_iceshelf_BC', "value")
3311+ do i=1,node_count(ice_surfaceS)
3312+ the_node = surface_nodes(i)
3313+ ! Ks = abs(sum(node_val(unit_normal_vectors_vel,the_node)*Ks_ar))
3314+ ! Sz = node_val(salt_flux_vel,the_node)/(-Ks)
3315+ Sz = node_val(salt_flux_vel,the_node)
3316+ !call set(ice_surfaceS,i,Sz)
3317+ call set(scalar_surface2,i,Sz)
3318+ enddo
3319+ ! Deallocate the heat_flux and salt_flux on velocity mesh
3320+ call deallocate(heat_flux_vel)
3321+ call deallocate(salt_flux_vel)
3322+
3323+ ! If the fluxes were constant
3324+ if (calculate_boundary_temperature) then
3325+ call set(T_bc,T_steady)
3326+ ! create a surface mesh to place values onto. This is for the top surface
3327+ call get_boundary_condition(TT, 'temperature_iceshelf_BC', surface_mesh=ice_mesh)
3328+ call allocate(ice_surfaceT, ice_mesh, name="ice_surfaceT")
3329+ do i=1,node_count(ice_surfaceT)
3330+ the_node = surface_nodes(i)
3331+ call set(ice_surfaceT,i,node_val(T_bc,the_node))
3332+ enddo
3333+ ! Temperature
3334+ scalar_surface => extract_surface_field(TT, 'temperature_iceshelf_BC', "value")
3335+ call remap_field(ice_surfaceT, scalar_surface)
3336+ endif
3337+
3338+ if (have_option(trim(option_path_bc)//'/bc_value_salinity')) then
3339+ call set(S_bc,S_steady)
3340+ ! Salinity
3341+ call get_boundary_condition(SS, 'salinity_iceshelf_BC', surface_mesh=ice_mesh)
3342+ call allocate(ice_surfaceS, ice_mesh, name="ice_surfaceS")
3343+ do i=1,node_count(ice_surfaceS)
3344+ the_node = surface_nodes(i)
3345+ call set(ice_surfaceS,i,node_val(S_bc,the_node))
3346+ enddo
3347+ !! Salinity
3348+ scalar_surface => extract_surface_field(SS, 'salinity_iceshelf_BC', "value")
3349+ call remap_field(ice_surfaceS, scalar_surface)
3350+ endif
3351+
3352+ case("dirichlet")
3353+ Tb => extract_scalar_field(state,"Tb")
3354+ Sb => extract_scalar_field(state,"Sb")
3355+ do i=1,node_count(T_bc)
3356+ call set(T_bc,i,node_val(Tb,i))
3357+ call set(S_bc,i,node_val(Sb,i))
3358+ enddo
3359+ ! When testing BC implementation
3360+ if (calculate_boundary_temperature) then
3361+ call set(T_bc,T_steady)
3362+ ewrite(3,*) "Melt interface, initialised T_steady", T_steady
3363+ endif
3364+ if (calculate_boundary_salinity) then
3365+ call set(S_bc,S_steady)
3366+ ewrite(3,*) "Melt interface, initialised S_steady", S_steady
3367+ endif
3368+
3369+ case default
3370+ FLAbort('Unknown boundary condition for ice-ocean interface')
3371+ end select
3372+
3373+ ! create a surface mesh to place values onto. This is for the top surface
3374+ !call get_boundary_condition(TT, 'temperature_iceshelf_BC', surface_mesh=ice_mesh)
3375+ !call allocate(ice_surfaceT, ice_mesh, name="ice_surfaceT")
3376+ ! Define ice_surfaceT according to Heat_flux?
3377+
3378+ !do i=1,node_count(ice_surfaceT)
3379+ ! the_node = surface_node_list(i)
3380+ ! call set(ice_surfaceT,i,node_val(T_bc,the_node))
3381+ !enddo
3382+
3383+ ! Salinity
3384+ !call get_boundary_condition(SS, 'salinity_iceshelf_BC', surface_mesh=ice_mesh)
3385+ !call allocate(ice_surfaceS, ice_mesh, name="ice_surfaceS")
3386+ !do i=1,node_count(ice_surfaceS)
3387+ ! the_node = surface_node_list(i)
3388+ ! call set(ice_surfaceS,i,node_val(S_bc,the_node))
3389+ !enddo
3390+
3391+ !!! Temperature
3392+ !scalar_surface => extract_surface_field(TT, 'temperature_iceshelf_BC', "value")
3393+ !call remap_field(ice_surfaceT, scalar_surface)
3394+ !!! Salinity
3395+ !scalar_surface => extract_surface_field(SS, 'salinity_iceshelf_BC', "value")
3396+ !call remap_field(ice_surfaceS, scalar_surface)
3397+
3398+ call deallocate(T_bc)
3399+ call deallocate(S_bc)
3400+ call deallocate(ice_surfaceT)
3401+ call deallocate(ice_surfaceS)
3402+ call deallocate(unit_normal_vectors_vel)
3403+ call deallocate(surface_mesh)
3404+
3405+ ewrite(1,*) "Melt interface boundary condition end"
3406+
3407+ end subroutine melt_interface_boundary_condition
3408+
3409+
3410+ subroutine melt_interface_cleanup()
3411+ ewrite(1,*) "Melt interface, cleaning up melt variables"
3412+ call deallocate(surface_positions)
3413+ call deallocate(funky_positions)
3414+ call deallocate(unit_normal_vectors)
3415+
3416+ deallocate(sf_nodes_point)
3417+ end subroutine melt_interface_cleanup
3418+
3419+
3420+ !------------------------------------------------------------------!
3421+ ! Private subroutines !
3422+ !------------------------------------------------------------------!
3423+
3424+ subroutine melt_interface_allocate_surface(state)
3425+ type(state_type), intent(inout) :: state
3426+ type(vector_field), pointer :: positions
3427+ type(mesh_type), pointer :: mesh
3428+ type(mesh_type) :: surface_mesh
3429+ ! Allocated and returned by create_surface_mesh
3430+ integer, dimension(:), pointer :: surface_nodes
3431+ integer, dimension(:), allocatable :: surface_element_list
3432+ integer :: face,i,j,k
3433+ real, dimension(:), allocatable :: coord
3434+ ! For transform_facet_to_physical
3435+ real, dimension(:,:), allocatable :: normal
3436+ real, dimension(:), allocatable :: av_normal !Average of normal
3437+ type(element_type), pointer :: x_shape_f
3438+ real, dimension(:), allocatable :: xyz !New location of surface_mesh, farfield_distance away from the boundary
3439+ type(integer_set) :: surface_ids
3440+ integer, dimension(:),allocatable :: interface_surface_id
3441+
3442+ ! For the vector averaging schem, taking the area of the surface element etc
3443+ real, dimension(:,:), allocatable :: table
3444+ integer, dimension(:,:), allocatable :: node_occupants
3445+ integer :: st,en,node,dim_vec
3446+ real :: area_sum
3447+ integer, dimension(2) :: shape_option
3448+ real, dimension(:), allocatable :: local_coord
3449+ integer :: ele, counter,Nit
3450+ real, dimension(:), allocatable :: vel
3451+ real :: c0, cI, L, TI, a, b, gammaT, gammaS, farfield_distance
3452+ character(len=*), parameter :: option_path = '/ocean_forcing/iceshelf_meltrate/Holland08/melt_surfaceID'
3453+
3454+ ewrite(1,*) "Melt interface allocation of surface parameters"
3455+
3456+ call melt_interface_read_coefficients(c0=c0, cI=cI, L=L, TI=TI, a=a, b=b, gammaT=gammaT, gammaS=gammaS, farfield_distance=farfield_distance)
3457+
3458+ ! Get the surface_id of the ice-ocean interface
3459+ shape_option=option_shape(trim(option_path))
3460+ allocate(interface_surface_id(1:shape_option(1)))
3461+ call get_option(trim(option_path), interface_surface_id)
3462+ call allocate(surface_ids)
3463+ call insert(surface_ids,interface_surface_id)
3464+
3465+ mesh => extract_mesh(state,"CoordinateMesh")
3466+ ! Input, mesh, surface_id
3467+ ! Output surface_mesh, surface_element_list
3468+ call melt_surf_mesh(mesh, surface_ids, surface_mesh, surface_nodes, surface_element_list)
3469+
3470+ allocate(sf_nodes_point(size(surface_nodes)))
3471+ sf_nodes_point = surface_nodes
3472+ positions => extract_vector_field(state,"Coordinate")
3473+ ! Sink the surface_mesh to the 'far field'
3474+ call allocate(surface_positions,positions%dim,mesh,"MySurfacePosition")
3475+ call allocate(funky_positions,surface_positions%dim, surface_positions%mesh,"MyFunkyPosition")
3476+ call allocate(unit_normal_vectors,surface_positions%dim, surface_positions%mesh,"MyFunkyUnitVec")
3477+ allocate(vel(positions%dim))
3478+ vel = 0.0
3479+ call set(unit_normal_vectors,vel)
3480+ deallocate(vel)
3481+ ! Check above
3482+
3483+ ! Remap the positions vector to the surface mesh, surface_positions
3484+ call remap_field_to_surface(positions, surface_positions, surface_element_list)
3485+
3486+ ! Loop over # of surface elements
3487+ !nf = size(surface_element_list)
3488+ !2 comes because there are 2 nodes per a surface_element (assumption!)
3489+ ! this number could be three for 3D problem, room for change
3490+ ! dim_vec is the dimension of the positions, either 2 or 3.
3491+ ! node_occupants(1,1:dim_vec*nf) = nodes that occupies the surface elements
3492+ ! node_occupants(2,:) = surface elements
3493+ ! table(1,:) = area of the face
3494+ ! table(2,:) = normal_x
3495+ ! table(3,:) = normal_y
3496+ ! table(4,:) = normal_z
3497+ dim_vec = positions%dim
3498+ allocate(coord(dim_vec))
3499+ allocate(node_occupants(2,dim_vec*size(surface_element_list)))
3500+ allocate(table(dim_vec+1,dim_vec*size(surface_element_list)))
3501+ allocate(av_normal(dim_vec))
3502+ allocate(xyz(dim_vec))
3503+ allocate(local_coord(positions%dim+1))
3504+
3505+ do i=1,size(surface_element_list)
3506+ st = 1+dim_vec*(i-1)
3507+ en = dim_vec+dim_vec*(i-1)
3508+ face = surface_element_list(i)
3509+ node_occupants(2,st:en) = face
3510+ node_occupants(1,st:en) = face_global_nodes(positions, face)
3511+ ! Calculate the area of the surface element
3512+ ! For 2D, the area is the length
3513+ if (dim_vec .eq. 2) then
3514+ table(1,st:en) = calc_area2(positions,node_occupants(1,st:en))
3515+ endif
3516+
3517+ ! For 3D, the area become the area of the triangle (assumption here).
3518+ ! Subroutine calc_area3 uses Heron's formula.
3519+ if (dim_vec .eq. 3) then
3520+ table(1,st:en) = calc_area3(positions,node_occupants(1,st:en))
3521+ endif
3522+ ! Compute the normal vector at the surface element.
3523+ x_shape_f=>face_shape(positions,face)
3524+ allocate(normal(dim_vec,x_shape_f%ngi)) !(dim x x_shape_f%ngi)
3525+ call transform_facet_to_physical(positions, face, normal=normal)
3526+ av_normal = sum(normal,2)
3527+ !"normal" should be the same; since the normal vector on the quadrature point does not change.
3528+ av_normal = av_normal/(sum(av_normal*av_normal))**(0.5) ! normalize the vector
3529+ deallocate(normal)
3530+ ! Since av_normal is outward to the boundary, we will put -sign. We want the vector into the boundary
3531+ av_normal = -av_normal
3532+ ! Store av_normal, the normal vector averaged over quadrature points, in table
3533+ do j=1,dim_vec
3534+ table(j+1,st:en)=av_normal(j)
3535+ enddo
3536+ enddo
3537+ ! Now loop over surface_nodes
3538+ ! ewrite(1,*) "table(1,1:3): ", table(1,1:3)
3539+ ! ewrite(1,*) "table(2,1:3),normal_x: ", table(2,1:3)
3540+ ! ewrite(1,*) "table(3,1:3),normal_y: ", table(3,1:3)
3541+ ! ewrite(1,*) "table(4,1:3),normal_z: ", table(4,1:3)
3542+ ! In this loop, we will average adjacent normal vectors, using the area of the surface elements.
3543+
3544+
3545+ do i=1,size(node_occupants(1,:))
3546+ node = node_occupants(1,i)
3547+ av_normal(:) = 0.0
3548+ area_sum = 0.0
3549+ ! This loop average the normal vector based on the areas of surface elements
3550+ ! which share the "node". Using hash table may speed up this process
3551+ do j=1,size(node_occupants(1,:))
3552+ !Pick the surface elements that sheare the "node"
3553+ if (node_occupants(1,j) .eq. node) then
3554+ do k=1,dim_vec
3555+ !table(1,j) = area of the surface element
3556+ av_normal(k) = av_normal(k) + table(k+1,j)*table(1,j)
3557+ enddo
3558+ !The total areas of the surface elements that occupies the "node"
3559+ area_sum = area_sum + table(1,j)
3560+ endif
3561+ enddo
3562+
3563+ av_normal = av_normal / area_sum
3564+ ! normalize
3565+ av_normal = av_normal/(sum(av_normal*av_normal))**(0.5)
3566+
3567+ farfield_distance = abs(farfield_distance)
3568+ ! Shift the location of the surface nodes.
3569+ !The coordinate of the surface node.
3570+
3571+ coord = node_val(positions,node)
3572+ ! farfield_distance = ||xyz - coord|| xyz and coord are vectors
3573+ xyz = av_normal*farfield_distance + coord
3574+
3575+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3576+ ! have options /ocean_forcing/iceshelf_meltrate/Holland08/melt_LayerRelax
3577+ if (have_option("/ocean_forcing/iceshelf_meltrate/Holland08/melt_LayerRelax")) then
3578+ call get_option('/ocean_forcing/iceshelf_meltrate/Holland08/melt_LayerRelax/N',Nit)
3579+ !!Check if xyz is within the domain
3580+ call picker_inquire(positions, xyz, ele, local_coord,global=.false.)
3581+ !! If sum(local_coord) is not equal to 1,
3582+ !! we know that this coord (funky position) does not exist in the domain.
3583+
3584+ counter = 1
3585+ do while (sum(local_coord) .gt. 2.0 .and. counter .lt. Nit)
3586+ xyz = av_normal*(farfield_distance-counter*(farfield_distance/Nit)) + coord
3587+ call picker_inquire(positions, xyz, ele, local_coord,global=.false.)
3588+ counter = counter+1
3589+ enddo
3590+
3591+ if (sum(local_coord) .gt. 2.0) then
3592+ ewrite(1,*) "icehslef location, coord: ", coord
3593+ ewrite(1,*) "funky position, xyz: ", xyz
3594+ call picker_inquire(positions, coord, ele, local_coord,global=.false.)
3595+ !! node_val(surface_positions,the_node) = node_val(positions,the_node)
3596+ ewrite(1,*) "Original ele: ",ele
3597+ ewrite(1,*) "sum of local_coord: ", sum(local_coord)
3598+ FLExit("In setting funky_position, your funky_positions is out of the domain. Change melt_LayerLength.")
3599+ endif
3600+ endif
3601+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3602+
3603+ call set(funky_positions,node,xyz) ! Set the coordinate of sinked nodes, funky positions.
3604+ call set(surface_positions,node,coord) !Original coordinate of the surface
3605+ call set(unit_normal_vectors,node,av_normal) !save the unit normal vector for Kt and Ks calculation
3606+
3607+ node = 0
3608+ enddo
3609+
3610+ ! Now deallocate junk
3611+ deallocate(coord)
3612+ deallocate(table)
3613+ deallocate(node_occupants)
3614+ deallocate(av_normal)
3615+ deallocate(xyz)
3616+ deallocate(local_coord)
3617+ call deallocate(surface_ids)
3618+ call deallocate(surface_mesh)
3619+ ewrite(1,*) "Melt interface allocation of surface parameters end"
3620+
3621+ end subroutine melt_interface_allocate_surface
3622+
3623+ subroutine melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
3624+
3625+ type(mesh_type), intent(in) :: mesh
3626+ type(integer_set), intent(in) :: surface_ids
3627+ type(mesh_type), intent(out) :: surface_mesh
3628+ integer, dimension(:), pointer, intent(out) :: surface_nodes ! allocated and returned by create_surface_mesh
3629+ integer, dimension(:), allocatable, intent(out) :: surface_element_list
3630+ ! integer, dimension(:), allocatable :: surface_nodes_out
3631+ type(integer_set) :: surface_elements
3632+ integer :: i
3633+ ! create a set of surface elements that have surface id in 'surface_ids'
3634+
3635+ call allocate(surface_elements)
3636+ do i=1, surface_element_count(mesh)
3637+ ! ewrite(1,*) "surf_normal surface_element_id(mesh, i)", surface_element_id(mesh, i)
3638+ ! ewrite(1,*) "surf_normal surface_ids", set2vector(surface_ids)
3639+ if (has_value(surface_ids, surface_element_id(mesh, i))) then
3640+ call insert(surface_elements, i)
3641+ end if
3642+ end do
3643+
3644+ allocate(surface_element_list(key_count(surface_elements)))
3645+ surface_element_list=set2vector(surface_elements)
3646+ call create_surface_mesh(surface_mesh, surface_nodes, mesh, surface_element_list, name=trim(mesh%name)//"ToshisMesh")
3647+ end subroutine melt_surf_mesh
3648+
3649+ subroutine melt_interface_read_coefficients(c0, cI, L, TI, a, b, gammaT, gammaS, farfield_distance, T_steady, S_steady)
3650+ real, intent(out), optional :: c0, cI, L, TI, a, b, gammaT, gammaS, farfield_distance, T_steady, S_steady
3651+ real :: Cd
3652+ character(len=*), parameter :: option_path = '/ocean_forcing/iceshelf_meltrate/Holland08'
3653+
3654+ ! Get the 6 model constants
3655+ ! TODO: Check these exist first and error with a useful message if not - in preprocessor
3656+ if (present(c0)) call get_option(trim(option_path)//'c0', c0, default = 3974.0)
3657+ if (present(cI)) call get_option(trim(option_path)//'cI', cI, default = 2009.0)
3658+ if (present(L)) call get_option(trim(option_path)//'/L', L, default = 3.35e5)
3659+ if (present(TI)) call get_option(trim(option_path)//'/TI', TI, default = -25.0)
3660+ if (present(a)) call get_option(trim(option_path)//'/a', a, default = -0.0573)
3661+ if (present(b)) call get_option(trim(option_path)//'/b', b, default = 0.0832)
3662+ if (present(farfield_distance)) call get_option(trim(option_path)//'/melt_LayerLength', farfield_distance)
3663+
3664+ if (present(gammaT).or.present(gammaS)) call get_option(trim(option_path)//'/Cd', Cd, default = 1.5e-3)
3665+ if (present(gammaT)) gammaT = sqrt(Cd)/(12.5*(7.0**(2.0/3.0))-9.0)
3666+ if (present(gammaS)) gammaS = sqrt(Cd)/(12.5*(700.0**(2.0/3.0))-9.0)
3667+
3668+ ! When steady boundary options are enabled
3669+ if (present(T_steady)) call get_option(trim(option_path)//'/calculate_boundaries/bc_value_temperature',T_steady, default = 0.0)
3670+ if (present(S_steady)) call get_option(trim(option_path)//'/calculate_boundaries/bc_value_salinity',S_steady, default = 34.0)
3671+
3672+ end subroutine melt_interface_read_coefficients
3673+
3674+ subroutine scalar_finder_ele(scalar,ele,element_dim,local_coord,scalar_out)
3675+ type(scalar_field), pointer, intent(in) :: scalar
3676+ integer, intent(in) :: ele
3677+ integer, intent(in) :: element_dim
3678+ real, dimension(element_dim), intent(in) :: local_coord
3679+ real, intent(out) :: scalar_out
3680+ integer :: j,node
3681+ integer, dimension(:), allocatable :: node_lists
3682+
3683+ allocate(node_lists(size(ele_nodes(scalar,ele))))
3684+ node_lists = ele_nodes(scalar,ele) !Lists of nodes that occupies the element.
3685+ scalar_out = 0.0
3686+ do j=1,size(node_lists)
3687+ node = node_lists(j)
3688+ scalar_out = scalar_out + node_val(scalar,node)*local_coord(j)
3689+ enddo
3690+ end subroutine scalar_finder_ele
3691+
3692+ subroutine vector_finder_ele(vector,ele,element_dim,local_coord,vector_out)
3693+ type(vector_field), pointer, intent(in) :: vector
3694+ integer, intent(in) :: ele
3695+ integer, intent(in) :: element_dim
3696+ real, dimension(element_dim), intent(in) :: local_coord
3697+ real, dimension(element_dim-1), intent(out) :: vector_out
3698+ integer :: j,node
3699+ integer, dimension(:), allocatable :: node_lists
3700+
3701+ allocate(node_lists(size(ele_nodes(vector,ele))))
3702+
3703+ node_lists = ele_nodes(vector,ele) !Lists of nodes that occupies the element.
3704+ vector_out(:) = 0.0
3705+ do j=1,size(node_lists)
3706+ node = node_lists(j)
3707+ vector_out = vector_out + node_val(vector,node)*local_coord(j)
3708+ enddo
3709+ end subroutine vector_finder_ele
3710+
3711+ real function setnan(arg)
3712+ real :: arg
3713+ setnan = sqrt(arg)
3714+ end function
3715+
3716+ real function calc_area2(positions,nodes)
3717+ type(vector_field), pointer :: positions
3718+ integer, dimension(2) :: nodes
3719+ real, dimension(2) :: coord1,coord2
3720+ coord1 = node_val(positions,nodes(1))
3721+ coord2 = node_val(positions,nodes(2))
3722+ calc_area2 = (sum((coord2 - coord1)**2))**0.5
3723+ end function
3724+
3725+ real function calc_area3(positions,nodes)
3726+ type(vector_field), pointer :: positions
3727+ integer, dimension(3) :: nodes
3728+ real, dimension(3) :: coord1,coord2,coord3
3729+ real :: a,b,c,s
3730+ coord1 = node_val(positions,nodes(1))
3731+ coord2 = node_val(positions,nodes(2))
3732+ coord3 = node_val(positions,nodes(3))
3733+ ! Use Heron's formula to calculate the area of triangle
3734+ a = (sum((coord2 - coord1)**2))**0.5
3735+ b = (sum((coord3 - coord1)**2))**0.5
3736+ c = (sum((coord2 - coord3)**2))**0.5
3737+ s = 0.5*(a+b+c)
3738+ calc_area3 = (s*(s-a)*(s-b)*(s-c))**0.5
3739+ end function
3740+
3741+end module ice_melt_interface
3742
3743=== modified file 'parameterisation/Makefile.dependencies'
3744--- parameterisation/Makefile.dependencies 2012-08-29 10:53:27 +0000
3745+++ parameterisation/Makefile.dependencies 2012-09-04 15:00:37 +0000
3746@@ -18,16 +18,15 @@
3747 ../include/fields.mod ../include/fldebug.mod \
3748 ../include/global_parameters.mod ../include/quadrature.mod \
3749 ../include/sparse_matrices_fields.mod ../include/spud.mod \
3750- ../include/state_fields_module.mod ../include/state_module.mod \
3751- ../include/vertical_extrapolation_module.mod
3752+ ../include/state_fields_module.mod ../include/state_module.mod
3753
3754-../include/iceshelf_meltrate_surf_normal.mod: iceshelf_meltrate_surf_normal.o
3755+../include/ice_melt_interface.mod: Ice_Melt_Interface.o
3756 @true
3757
3758-iceshelf_meltrate_surf_normal.o ../include/iceshelf_meltrate_surf_normal.mod: \
3759- iceshelf_meltrate_surf_normal.F90 ../include/boundary_conditions.mod \
3760+Ice_Melt_Interface.o ../include/ice_melt_interface.mod: \
3761+ Ice_Melt_Interface.F90 ../include/boundary_conditions.mod \
3762 ../include/elements.mod ../include/fdebug.h ../include/fetools.mod \
3763- ../include/field_derivatives.mod ../include/field_options.mod \
3764+ ../include/field_derivatives.mod ../include/fields_base.mod ../include/field_options.mod \
3765 ../include/fields.mod ../include/fields_allocates.mod \
3766 ../include/fields_manipulation.mod ../include/fldebug.mod \
3767 ../include/global_parameters.mod ../include/integer_set_module.mod \
3768
3769=== modified file 'parameterisation/Makefile.in'
3770--- parameterisation/Makefile.in 2011-10-21 10:05:25 +0000
3771+++ parameterisation/Makefile.in 2012-09-04 15:00:37 +0000
3772@@ -47,7 +47,7 @@
3773 OBJS = Equation_of_State.o \
3774 Gls_vertical_turbulence_model.o \
3775 k_epsilon.o \
3776-iceshelf_meltrate_surf_normal.o
3777+Ice_Melt_Interface.o
3778
3779 .SUFFIXES: .F90 .c .o .a
3780
3781
3782=== removed file 'parameterisation/iceshelf_meltrate_surf_normal.F90'
3783--- parameterisation/iceshelf_meltrate_surf_normal.F90 2011-04-07 12:42:58 +0000
3784+++ parameterisation/iceshelf_meltrate_surf_normal.F90 1970-01-01 00:00:00 +0000
3785@@ -1,678 +0,0 @@
3786-! Copyright (C) 2006 Imperial College London and others.
3787-!
3788-! Please see the AUTHORS file in the main source directory for a full list
3789-! of copyright holders.
3790-!
3791-! Prof. C Pain
3792-! Applied Modelling and Computation Group
3793-! Department of Earth Science and Engineering
3794-! Imperial College London
3795-!
3796-! amcgsoftware@imperial.ac.uk
3797-!
3798-! This library is free software; you can redistribute it and/or
3799-! modify it under the terms of the GNU Lesser General Public
3800-! License as published by the Free Software Foundation,
3801-! version 2.1 of the License.
3802-!
3803-! This library is distributed in the hope that it will be useful,
3804-! but WITHOUT ANY WARRANTY; without even the implied arranty of
3805-! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3806-! Lesser General Public License for more details.
3807-!
3808-! You should have received a copy of the GNU Lesser General Public
3809-! License along with this library; if not, write to the Free Software
3810-! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3811-! USA
3812-
3813-#include "fdebug.h"
3814-
3815-module iceshelf_meltrate_surf_normal
3816- use quadrature
3817- use elements
3818- use field_derivatives
3819- use fields
3820- use field_options
3821- use fields_allocates
3822- use state_module
3823- use spud
3824- use global_parameters, only: FIELD_NAME_LEN, OPTION_PATH_LEN
3825- use state_fields_module
3826- use boundary_conditions
3827- use fields_manipulation
3828- use surface_integrals
3829- use fetools
3830- use vector_tools
3831- use FLDebug
3832- use integer_set_module
3833- use pickers_inquire
3834- use transform_elements
3835- use state_fields_module
3836-implicit none
3837-
3838- private
3839- real, save :: c0, cI, L, TI, &
3840- a, b, gammaT, gammaS,Cd, dist_meltrate
3841- integer, save :: nnodes,dimen
3842-
3843- type(vector_field), save :: surface_positions
3844- type(vector_field), save :: funky_positions
3845- type(integer_set), save :: sf_nodes !Nodes at the surface
3846- !BC
3847- integer, dimension(:), pointer, save :: ice_element_list
3848- ! these are the fields and variables for the surface values
3849- type(scalar_field), save :: ice_surfaceT,ice_surfaceS! these are used to populate the bcs
3850- public :: melt_surf_init, melt_allocate_surface, melt_surf_calc, melt_bc
3851-
3852-
3853-
3854-contains
3855-
3856-!----------
3857-! initialise parameters based on options
3858-!----------
3859-
3860- subroutine melt_surf_init(state)
3861-
3862- type(state_type), intent(inout) :: state
3863- character(len=OPTION_PATH_LEN) :: melt_path
3864- ! hack
3865- type(scalar_field), pointer :: T,S
3866- ! When bc=Dirichlet
3867- character(len=FIELD_NAME_LEN) :: bc_type
3868- type(integer_set) :: surface_ids
3869- integer, dimension(:),allocatable :: surf_id
3870- integer, dimension(:), allocatable :: sf_nodes_ar
3871- integer, dimension(2) :: shape_option
3872- type(mesh_type), pointer :: mesh
3873- type(mesh_type) :: surface_mesh
3874- integer, dimension(:), pointer :: surface_nodes ! allocated and returned by create_surface_mesh
3875- integer, dimension(:), allocatable :: surface_element_list
3876- integer :: i,the_node
3877-
3878- melt_path = "/ocean_forcing/iceshelf_meltrate/Holland08"
3879-
3880-
3881- ewrite(1,*) "--------Begin melt_init-------------"
3882-
3883- ! Get the 6 model constants
3884- call get_option(trim(melt_path)//'/c0', c0, default = 3974.0)
3885- call get_option(trim(melt_path)//'/cI', cI, default = 2009.0)
3886- call get_option(trim(melt_path)//'/L', L, default = 3.35e5)
3887- call get_option(trim(melt_path)//'/TI', TI, default = -25.0)
3888- call get_option(trim(melt_path)//'/a', a, default = -0.0573)
3889- call get_option(trim(melt_path)//'/b', b, default = 0.0832)
3890- call get_option(trim(melt_path)//'/Cd', Cd, default = 1.5e-3)
3891- call get_option(trim(melt_path)//'/melt_LayerLength', dist_meltrate)
3892-
3893- gammaT = sqrt(Cd)/(12.5*(7.0**(2.0/3.0))-9.0)
3894- gammaS = sqrt(Cd)/(12.5*(700.0**(2.0/3.0))-9.0)
3895-
3896- !! bc= Dirichlet initialize T and S at the ice-ocean interface
3897- !hack
3898- !This change with bc type
3899- call get_option("/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries", bc_type)
3900-
3901- select case(bc_type)
3902- case("neumann")
3903-
3904-
3905- case("dirichlet")
3906- !! Define the values at ice-ocean interface
3907- ! Get the surface_id of the ice-ocean interface
3908- shape_option=option_shape(trim(melt_path)//"/melt_surfaceID")
3909- allocate(surf_id(1:shape_option(1)))
3910- call get_option(trim(melt_path)//'/melt_surfaceID',surf_id)
3911- call allocate(surface_ids)
3912- call insert(surface_ids,surf_id)
3913- mesh => extract_mesh(state,"VelocityMesh")
3914- ! Input, mesh, surface_id
3915- ! Output surface_mesh,surface_element_list
3916- call melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
3917-
3918- T => extract_scalar_field(state,"Temperature")
3919- S => extract_scalar_field(state,"Salinity")
3920- do i=1,size(surface_nodes)
3921- the_node = surface_nodes(i)
3922-
3923- call set(T,the_node,0.0)
3924- call set(S,the_node,34.0)
3925-
3926- enddo
3927-! T_bc => extract_scalar_field(state,"Tb")
3928-! S_bc => extract_scalar_field(state,"Sb")
3929- case default
3930- FLAbort('Unknown BC for TKE')
3931- end select
3932-
3933-
3934- ewrite(1,*) "---------End melt_surf_init---------------------------------"
3935-
3936- end subroutine melt_surf_init
3937-
3938-subroutine melt_allocate_surface(state)
3939-
3940- type(state_type), intent(inout) :: state
3941- type(vector_field), pointer :: positions
3942- type(mesh_type), pointer :: mesh
3943- type(mesh_type) :: surface_mesh
3944- type(integer_set) :: surface_elements
3945- integer, dimension(:), pointer :: surface_nodes ! allocated and returned by create_surface_mesh
3946- integer, dimension(:), allocatable :: surface_element_list
3947- integer :: ele, face,i,j,k
3948-!! The local coordinates of the coordinate in the owning element
3949-!! real, dimension(:), allocatable :: local_coord, local_coord_surf
3950- real, dimension(:), allocatable :: coord
3951-! for transform_facet_to_physical
3952- real, dimension(:,:), allocatable :: normal
3953- real, dimension(:), allocatable :: av_normal !Average of normal
3954-!! integer, dimension(:), pointer :: ele_faces,face_neighs
3955- type(element_type), pointer :: x_shape_f
3956- real :: al,be,ga
3957- real, dimension(:), allocatable :: xyz !New location of surface_mesh, dist_meltrate away from the boundary
3958-!integer, save :: melt_surfaceID
3959- character(len=OPTION_PATH_LEN) :: path
3960- type(integer_set) :: surface_ids
3961- integer, dimension(:),allocatable :: surf_id
3962-
3963-! For the vector averaging schem, taking the area of the surface element etc
3964- real, dimension(:,:), allocatable :: table
3965- integer, dimension(:,:), allocatable :: node_occupants
3966- integer :: st,en,node,dim_vec
3967- real :: area_sum
3968- integer, dimension(:), allocatable :: sf_nodes_ar
3969- integer, dimension(2) :: shape_option
3970-
3971- ewrite(1,*) "-------Begin melt_allocate_surface---------"
3972- path = "/ocean_forcing/iceshelf_meltrate/Holland08"
3973- ! Get the surface_id of the ice-ocean interface
3974- shape_option=option_shape(trim(path)//"/melt_surfaceID")
3975- allocate(surf_id(1:shape_option(1)))
3976- call get_option(trim(path)//'/melt_surfaceID',surf_id)
3977- call allocate(surface_ids)
3978- call insert(surface_ids,surf_id)
3979-! deallocate(surf_id)
3980-! ewrite(1,*) "aft_surface_ids: ", set2vector(surface_ids)
3981-
3982- mesh => extract_mesh(state,"VelocityMesh")
3983-! Input, mesh, surface_id
3984-! Output surface_mesh,surface_element_list
3985- call melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
3986-
3987- positions => extract_vector_field(state,"Coordinate")
3988-!SINK THE surface_mesh!!
3989-! call get_option("/geometry/dimension/", dimension)
3990- call allocate(surface_positions,positions%dim,mesh,"MySurfacePosition")
3991- call allocate(funky_positions,surface_positions%dim, surface_positions%mesh,"MyFunkyPosition")
3992-
3993-
3994-! Remap the positions vector to the surface mesh, surface_positions
3995- call remap_field_to_surface(positions, surface_positions, surface_element_list)
3996-
3997-! Loop over # of surface elements
3998-!nf = size(surface_element_list)
3999-!2 comes because there are 2 nodes per a surface_element (assumption!)
4000-! this number could be three for 3D problem, room for change
4001-! dim_vec is the dimension of the positions, either 2 or 3.
4002-! node_occupants(1,1:dim_vec*nf) = nodes that occupies the surface elements
4003-! node_occupants(2,:) = surface elements
4004-! table(1,:) = area of the face
4005-! table(2,:) = normal_x
4006-! table(3,:) = normal_y
4007-! table(4,:) = normal_z
4008- dim_vec = positions%dim
4009- allocate(coord(dim_vec))
4010- allocate(node_occupants(2,dim_vec*size(surface_element_list)))
4011- allocate(table(dim_vec+1,dim_vec*size(surface_element_list)))
4012- allocate(av_normal(dim_vec))
4013- allocate(xyz(dim_vec))
4014-
4015- do i=1,size(surface_element_list)
4016- st = 1+dim_vec*(i-1)
4017- en = dim_vec+dim_vec*(i-1)
4018- face = surface_element_list(i)
4019- node_occupants(2,st:en) = face
4020- node_occupants(1,st:en) = face_global_nodes(positions, face)
4021- ! Calculate the area of the surface element
4022- ! For 2D, the area is the length
4023- if (dim_vec .eq. 2) then
4024- table(1,st:en) = calc_area2(positions,node_occupants(1,st:en))
4025- endif
4026-
4027- ! For 3D, the area become the area of the triangle (assumption here).
4028- ! Subroutine calc_area3 uses Heron's formula.
4029- if (dim_vec .eq. 3) then
4030- table(1,st:en) = calc_area3(positions,node_occupants(1,st:en))
4031- endif
4032- ! Compute the normal vector at the surface element.
4033- x_shape_f=>face_shape(positions,face)
4034- allocate(normal(dim_vec,x_shape_f%ngi)) !(dim x x_shape_f%ngi)
4035- call transform_facet_to_physical(positions, face, normal=normal)
4036- av_normal = sum(normal,2)
4037- !"normal" should be the same; since the normal vector on the quadrature point does not change.
4038- av_normal = av_normal/(sum(av_normal*av_normal))**(0.5) ! normalize the vector
4039- deallocate(normal)
4040- !Since av_normal is outward to the boundary, we will put -sign. We want the vector into the boundary
4041- av_normal = -av_normal
4042- ! Store av_normal, the normal vector averaged over quadrature points, in table
4043- do j=1,dim_vec
4044- table(j+1,st:en)=av_normal(j)
4045- enddo
4046- enddo
4047-!! Now loop over surface_nodes
4048-! ewrite(1,*) "table(1,1:3): ", table(1,1:3)
4049-! ewrite(1,*) "table(2,1:3),normal_x: ", table(2,1:3)
4050-! ewrite(1,*) "table(3,1:3),normal_y: ", table(3,1:3)
4051-! ewrite(1,*) "table(4,1:3),normal_z: ", table(4,1:3)
4052-!!In this loop, we will average adjacent normal vectors, using the area of the surface elements.
4053-
4054- !allocate(sf_nodes_ar(size(node_occupants(1,:))))
4055- call allocate(sf_nodes)
4056-! call insert(sf_nodes,sf_nodes_ar)
4057- do i=1,size(node_occupants(1,:))
4058- node = node_occupants(1,i)
4059- av_normal(:) = 0.0
4060- area_sum = 0.0
4061- ! This loop average the normal vector based on the areas of surface elements
4062- !, which share the "node". Using hash table may speed up this process
4063- do j=1,size(node_occupants(1,:))
4064- !Pick the surface elements that sheare the "node"
4065- if (node_occupants(1,j) .eq. node) then
4066- do k=1,dim_vec
4067- !table(1,j) = area of the surface element
4068- av_normal(k) = av_normal(k) + table(k+1,j)*table(1,j)
4069- enddo
4070- !The total areas of the surface elements that occupies the "node"
4071- area_sum = area_sum + table(1,j)
4072- endif
4073- enddo
4074-
4075- av_normal = av_normal / area_sum
4076- ! normalize
4077- av_normal = av_normal/(sum(av_normal*av_normal))**(0.5)
4078-
4079- dist_meltrate = abs(dist_meltrate)
4080- ! Shift the location of the surface nodes.
4081- !The coordinate of the surface node.
4082-
4083- coord = node_val(positions,node)
4084-
4085- ! dist_meltrate = ||xyz - coord|| xyz and coord are vectors
4086- xyz = av_normal*dist_meltrate + coord
4087-
4088- call set(funky_positions,node,xyz) ! Set the coordinate of sinked nodes, funky positions.
4089-
4090- call set(surface_positions,node,coord) !Original coordinate of the surface
4091-!! ewrite(1,*) "--------------------------------"
4092-!! ewrite(1,*) "node: ", node
4093-!! ewrite(1,*) "av_normal: ", av_normal
4094-!! ewrite(1,*) "funky: ", xyz
4095-!! ewrite(1,*) "coord: ", coord
4096-!! ! Save the corresponding node number.
4097-!! !sf_nodes_ar(i) = node
4098- call insert(sf_nodes,node)
4099-
4100- node = 0
4101- enddo
4102-
4103-
4104- deallocate(coord)
4105- deallocate(table)
4106- deallocate(node_occupants)
4107- deallocate(av_normal)
4108- deallocate(xyz)
4109- call deallocate(surface_ids)
4110- ewrite(1,*) "-------End melt_allocate_surface---------"
4111-end subroutine melt_allocate_surface
4112-
4113-
4114-
4115-! Calculate the melt rate
4116- subroutine melt_surf_calc(state)
4117-
4118- type(state_type), intent(inout) :: state
4119- type(scalar_field), pointer :: Tb, Sb,MeltRate,Heat_flux,Salt_flux
4120- type(scalar_field), pointer :: scalarfield
4121- type(vector_field), pointer :: velocity,positions
4122- !Debugging purpose pointer
4123- type(scalar_field), pointer :: T_loc,S_loc,P_loc
4124- type(vector_field), pointer :: V_loc,Location,Location_org
4125- real, dimension(:), allocatable :: vel
4126- integer :: i,j,k
4127- ! Some internal variables
4128- real :: speed, T,S,P,Aa,Bb,Cc,topo
4129- real ::loc_Tb,loc_Sb,loc_meltrate,loc_heatflux,loc_saltflux
4130- ! Aa*Sb^2+Bv*Sb+Cc
4131- real :: arg = -1.0
4132- !Sink mesh part
4133- integer :: ele,face,node,stat,the_node
4134- real, dimension(:), allocatable :: local_coord,coord
4135- type(element_type), pointer :: x_shape_f
4136- integer, dimension(:), allocatable :: surface_node_list,node_lists
4137- type(scalar_field) :: re_temperature,re_salinity,re_pressure
4138- type(vector_field) :: re_velocity
4139-
4140-
4141- ewrite(1,*) "-------Begin melt_surf_calc------------"
4142-
4143- MeltRate => extract_scalar_field(state,"MeltRate")
4144- Tb => extract_scalar_field(state,"Tb")
4145- Sb => extract_scalar_field(state,"Sb")
4146- call set(MeltRate,setnan(arg))
4147- call set(Tb,setnan(arg))
4148- call set(Sb,setnan(arg))
4149- Heat_flux => extract_scalar_field(state,"Heat_flux")
4150- Salt_flux => extract_scalar_field(state,"Salt_flux")
4151- call set(Heat_flux,setnan(arg))
4152- call set(Salt_flux,setnan(arg))
4153-
4154-! Debugging
4155- T_loc => extract_scalar_field(state,"Tloc")
4156- S_loc => extract_scalar_field(state,"Sloc")
4157- P_loc => extract_scalar_field(state,"Ploc")
4158- V_loc => extract_vector_field(state,"Vloc")
4159- Location => extract_vector_field(state,"Location")
4160- Location_org => extract_vector_field(state,"Location_org")
4161- call set(T_loc,setnan(arg))
4162- call set(S_loc,setnan(arg))
4163- call set(P_loc,setnan(arg))
4164- allocate(vel(V_loc%dim))
4165- vel = setnan(arg)
4166- call set(V_loc,vel)
4167- call set(Location,vel)
4168- call set(Location_org,vel)
4169-
4170- positions => extract_vector_field(state,"Coordinate")
4171- !my positions
4172-
4173- ! Surface node list
4174- allocate(surface_node_list(key_count(sf_nodes)))
4175- ! Make it to vector from integer_set.
4176- ! sf_nodes is calculated in "melt_allocate_surface"
4177- surface_node_list=set2vector(sf_nodes)
4178-
4179- ! Remap temperature, salinity, pressure, and velocity onto positions mesh
4180- scalarfield => extract_scalar_field(state,"Temperature")
4181-
4182- call allocate(re_temperature,positions%mesh,name="ReTemperature")
4183-
4184- call remap_field(scalarfield,re_temperature,stat)
4185-
4186- ! Salinity
4187- scalarfield=> extract_scalar_field(state,"Salinity")
4188-
4189- call allocate(re_salinity,positions%mesh, name="ReSalinity")
4190- call remap_field(scalarfield,re_salinity,stat)
4191-
4192- ! Pressure
4193- scalarfield => extract_scalar_field(state,"Pressure")
4194- call allocate(re_pressure,positions%mesh, name="RePressure")
4195- call remap_field(scalarfield,re_pressure,stat)
4196-
4197- ! Velocity
4198- velocity => extract_vector_field(state,"Velocity")
4199- call allocate(re_velocity,velocity%dim,positions%mesh,name="ReVelocity")
4200- call remap_field(velocity, re_velocity, stat)
4201-
4202- allocate(local_coord(positions%dim+1))
4203- allocate(coord(positions%dim))
4204-
4205-
4206-
4207-
4208-!!!!!!!!!!!!!!!!!!!!!!!!!
4209- !surface_positions
4210-
4211- !! Loope over the surface nodes to calculate melt rate etc.
4212- do i=1,size(surface_node_list)
4213- the_node = surface_node_list(i)
4214- !!! Interpolating
4215- coord = node_val(funky_positions,the_node)
4216- call picker_inquire(positions, coord, ele, local_coord,global=.true.)
4217-
4218- !! If sum(local_coord) is not equal to 1,
4219- !! we know that this coord (funky position) does not exist in the domain.
4220-
4221- if (sum(local_coord) .gt. 2.0) then
4222- ewrite(1,*) "Funk coord: ", node_val(funky_positions,the_node)
4223- !! node_val(surface_positions,the_node) = node_val(positions,the_node)
4224- ewrite(1,*) "Original ele: ",ele
4225- ewrite(1,*) "sum of local_coord: ", sum(local_coord)
4226- FLExit("Your funky_positions is out of the domain. Change melt_LayerLength.")
4227- endif
4228- !Number of nodes per element
4229- allocate(node_lists(ele_and_faces_loc(positions, ele)))
4230- node_lists = ele_nodes(positions,ele) !Lists of nodes that occupies the element.
4231- ! This method of finding values at funky_positions works for P1, which are T,S, and velocity.
4232- coord = 0.0
4233- T = 0.0
4234- S = 0.0
4235- speed = 0.0
4236- vel = 0.0
4237-
4238- do j=1,size(node_lists)
4239- node = node_lists(j)
4240- coord = coord + node_val(positions,node)*local_coord(j)
4241- T = T + node_val(re_temperature,node)*local_coord(j)
4242- S = S + node_val(re_salinity,node)*local_coord(j)
4243- vel = vel + node_val(re_velocity,node)*local_coord(j)
4244- enddo
4245- deallocate(node_lists)
4246- ! Luckly P needs to be at the surface for the three equations
4247- P = node_val(re_pressure,the_node)
4248- speed = sqrt(sum(vel**2))
4249-
4250- if (speed .lt. 0.001) then
4251- speed = 0.001
4252- !ewrite(1,*) "----------iceshelf, speed less----", the_node,speed
4253- endif
4254- topo = -7.53e-8*P ! constant = -7.53e-8 [C Pa^(-1)] comes from Holland and Jenkins Table 1
4255-
4256- !! Define Aa,Bb,Cc
4257- !! Aa*Sb**2 + Bb*Sb + Cc = 0.0
4258- Aa = -gammaS*speed*cI*a + a*c0*gammaT*speed
4259-
4260- Bb = -gammaS*speed*L + gammaS*speed*S*cI*a
4261- Bb = Bb - gammaS*speed*cI*(b+topo) + gammaS*speed*cI*TI
4262- Bb = Bb - c0*gammaT*speed*T + c0*gammaT*speed*(b+topo)
4263-
4264- Cc = gammaS*speed*S*L +gammaS*speed*S*cI*(b+topo) + gammaS*speed*S*(-cI*TI)
4265-
4266-
4267- !! This could be a linear equation if Aa=0
4268- if (Aa .eq. 0.0) then
4269- loc_Sb = -Cc/Bb
4270- else
4271- !! Calculate for the 2nd oewrite(1,*) "size(surface_element_list)"rder polynomial.
4272- !! We have two solutions.
4273- loc_Sb = (-Bb + sqrt(Bb**2 - 4.0*Aa*Cc))/(2.0*Aa)
4274- !! loc_Sb has to be larger than 0; since the salinity in the ocean is positive definite.
4275- if (loc_Sb .lt. 0.0) then
4276- loc_Sb = (-Bb - sqrt(Bb**2 - 4.0*Aa*Cc))/(2.0*Aa)
4277- endif
4278- endif
4279- !ewrite(1,*) "----------iceshelf, loc_Sb----", loc_Sb
4280- loc_Tb = a*loc_Sb + b + topo
4281- loc_meltrate = gammaS*speed*(S-loc_Sb)/loc_Sb
4282- !! Heat flux to the ocean
4283- loc_heatflux = c0*(gammaT*speed+ loc_meltrate)*(T-loc_Tb) ! or loc_meltrate*L + loc_meltrate*cI*(loc_Tb-TI)
4284- loc_saltflux = (gammaS*speed+loc_meltrate)*(S-loc_Sb)
4285- !! Some debugging
4286- !!ewrite(1,*) "melt_rate: ",loc_meltrate
4287- !!ewrite(1,*) "tLHS: ", c0*gammaT*speed*(T-loc_Tb)
4288- !!ewrite(1,*) "tRHS: ", loc_meltrate*L + loc_meltrate*cI*(loc_Tb-TI)
4289- !!ewrite(1,*) "sLHS: ", gammaS*speed*(S-loc_Sb)
4290- !!ewrite(1,*) "sRHS: ", loc_meltrate*loc_Sb
4291- !!ewrite(1,*) "bLHS: ", loc_Tb
4292- !!ewrite(1,*) "bRHS: ", a*loc_Sb + b + topo
4293-
4294- !! These are needed to implement BCs.
4295- call set(MeltRate, the_node, loc_meltrate)
4296- call set(Tb, the_node, loc_Tb)
4297- call set(Sb, the_node, loc_Sb)
4298- call set(Heat_flux, the_node,loc_heatflux)
4299- call set(Salt_flux, the_node,loc_saltflux)
4300- !! More or less for debugging purposes.
4301- call set(T_loc,the_node,T)
4302- call set(S_loc,the_node,S)
4303- call set(P_loc,the_node,P)
4304- call set(V_loc,the_node,vel)
4305- call set(Location,the_node,node_val(funky_positions,the_node))
4306- call set(Location_org,the_node,node_val(positions,the_node))
4307- !! BC test
4308- !!call set(TT,the_node,11.1)
4309-! ewrite(1,*) "----------iceshelf, loc_saltflux----", the_node,loc_saltflux
4310-! ewrite(1,*) "----------melt_surf_calc, loc_Tb----", the_node,loc_Tb
4311-! ewrite(1,*) "----------iceshelf, surfaceS----",node_val(re_salinity,the_node)
4312-! ewrite(1,*) "----------line 477 iceshelf, loc_meltrate----",loc_meltrate
4313-! ewrite(1,*) "----------iceshelf, node_val(TT,the_node)----",node_val(TT,the_node)
4314- enddo
4315-
4316- deallocate(local_coord)
4317- deallocate(coord)
4318- ewrite(1,*) "-----END melt_surf_calc-------"
4319-end subroutine melt_surf_calc
4320-
4321-
4322-subroutine melt_bc(state)
4323- type(state_type), intent(inout) :: state
4324- !! BC
4325- type(scalar_field), pointer :: TT,SS
4326- type(scalar_field), pointer :: scalar_surface
4327- type(mesh_type), pointer :: ice_mesh
4328- character(len=FIELD_NAME_LEN) :: bc_type
4329- type(scalar_field), pointer :: T_bc,S_bc
4330- integer, dimension(:), allocatable :: surface_node_list
4331- integer :: i, the_node
4332-!! Insert BC for temperature and salinity. This could be a separate subroutine
4333-
4334-
4335- TT=> extract_scalar_field(state,"Temperature")
4336- SS => extract_scalar_field(state,"Salinity")
4337-
4338- !This change with bc type
4339- call get_option("/ocean_forcing/iceshelf_meltrate/Holland08/calculate_boundaries", bc_type)
4340-
4341- select case(bc_type)
4342- case("neumann")
4343- T_bc => extract_scalar_field(state,"Heat_flux")
4344- S_bc => extract_scalar_field(state,"Salt_flux")
4345- do i=1,node_count(T_bc)
4346- call set(T_bc,node_val(T_bc,i)*10.0**3)
4347- call set(S_bc,node_val(S_bc,i)*10.0**3)
4348- enddo
4349-
4350- case("dirichlet")
4351- T_bc => extract_scalar_field(state,"Tb")
4352- S_bc => extract_scalar_field(state,"Sb")
4353- case default
4354- FLAbort('Unknown BC for TKE')
4355- end select
4356-
4357-
4358- ! Surface node list
4359- allocate(surface_node_list(key_count(sf_nodes)))
4360- ! Make it to vector from integer_set.
4361- ! sf_nodes is calculated in "melt_allocate_surface"
4362- surface_node_list=set2vector(sf_nodes)
4363-
4364- ! create a surface mesh to place values onto. This is for the top surface
4365- call get_boundary_condition(TT, 'temperature_iceshelf_BC', surface_mesh=ice_mesh)
4366- call allocate(ice_surfaceT, ice_mesh, name="ice_surfaceT")
4367- call get_boundary_condition(SS, 'salinity_iceshelf_BC', surface_mesh=ice_mesh)
4368- call allocate(ice_surfaceS, ice_mesh, name="ice_surfaceS")
4369- ! Define ice_surfaceT according to Heat_flux?
4370- ewrite(1,*) "node_count(ice_surfaceT)",node_count(ice_surfaceT)
4371- do i=1,node_count(ice_surfaceT)
4372- the_node = surface_node_list(i)
4373- call set(ice_surfaceT,i,node_val(T_bc,the_node))
4374- call set(ice_surfaceS,i,node_val(S_bc,the_node))
4375- enddo
4376- !! Temperature
4377- scalar_surface => extract_surface_field(TT, 'temperature_iceshelf_BC', "value")
4378- call remap_field(ice_surfaceT, scalar_surface)
4379- !! Salinity
4380- scalar_surface => extract_surface_field(SS, 'salinity_iceshelf_BC', "value")
4381- call remap_field(ice_surfaceS, scalar_surface)
4382-! ewrite(1,*) "iceBC, ice_element_list",ice_element_list
4383-! ewrite(1,*) "iceBC, node_count(scalar_surface)", node_count(scalar_surface)
4384-! ewrite(1,*) "node_val(scalar_surface,1): ", node_val(scalar_surface,1)
4385-
4386-!! Insert BC for salinity
4387-
4388-
4389-end subroutine melt_bc
4390-
4391-
4392-
4393-!!------------------------------------------------------------------!
4394-!! Private subroutines !
4395-!!------------------------------------------------------------------!
4396-
4397-subroutine melt_surf_mesh(mesh,surface_ids,surface_mesh,surface_nodes,surface_element_list)
4398-
4399- type(mesh_type), intent(in) :: mesh
4400- type(integer_set), intent(in) :: surface_ids
4401- type(mesh_type), intent(out) :: surface_mesh
4402- integer, dimension(:), pointer, intent(out) :: surface_nodes ! allocated and returned by create_surface_mesh
4403- integer, dimension(:), allocatable, intent(out) :: surface_element_list
4404- ! integer, dimension(:), allocatable :: surface_nodes_out
4405- type(scalar_field) :: surface_field
4406- type(integer_set) :: surface_elements
4407- integer :: i
4408- ! create a set of surface elements that have surface id in 'surface_ids'
4409-
4410- call allocate(surface_elements)
4411- do i=1, surface_element_count(mesh)
4412-! ewrite(1,*) "surf_normal surface_element_id(mesh, i)", surface_element_id(mesh, i)
4413-! ewrite(1,*) "surf_normal surface_ids", set2vector(surface_ids)
4414- if (has_value(surface_ids, surface_element_id(mesh, i))) then
4415- call insert(surface_elements, i)
4416-
4417- end if
4418- end do
4419-
4420- allocate(surface_element_list(key_count(surface_elements)))
4421- surface_element_list=set2vector(surface_elements)
4422- call create_surface_mesh(surface_mesh, surface_nodes, mesh, surface_element_list, name=trim(mesh%name)//"ToshisMesh")
4423-
4424-
4425-end subroutine melt_surf_mesh
4426-
4427-
4428-real function setnan(arg)
4429- real :: arg
4430- setnan = sqrt(arg)
4431-end function
4432-
4433-real function calc_area2(positions,nodes)
4434- type(vector_field), pointer :: positions
4435- integer, dimension(2) :: nodes
4436- real, dimension(2) :: coord1,coord2
4437-! real :: area
4438- coord1 = node_val(positions,nodes(1))
4439- coord2 = node_val(positions,nodes(2))
4440-
4441- calc_area2 = (sum((coord2 - coord1)**2))**0.5
4442-
4443-end function
4444-
4445-real function calc_area3(positions,nodes)
4446- type(vector_field), pointer :: positions
4447- integer, dimension(3) :: nodes
4448- real, dimension(3) :: coord1,coord2,coord3
4449- real :: a,b,c,s
4450-! real :: area
4451- coord1 = node_val(positions,nodes(1))
4452- coord2 = node_val(positions,nodes(2))
4453- coord3 = node_val(positions,nodes(3))
4454-! Use Heron's formula to calculate the area of triangle
4455- a = (sum((coord2 - coord1)**2))**0.5
4456- b = (sum((coord3 - coord1)**2))**0.5
4457- c = (sum((coord2 - coord3)**2))**0.5
4458- s = 0.5*(a+b+c)
4459- calc_area3 = (s*(s-a)*(s-b)*(s-c))**0.5
4460-
4461-end function
4462-
4463-end module iceshelf_meltrate_surf_normal
4464
4465=== removed file 'schemas/multiphase_interaction.rnc'
4466--- schemas/multiphase_interaction.rnc 2012-06-15 19:32:03 +0000
4467+++ schemas/multiphase_interaction.rnc 1970-01-01 00:00:00 +0000
4468@@ -1,58 +0,0 @@
4469-# Phase interaction options for Fluidity's multiphase flow model.
4470-
4471-multiphase_interaction =
4472- (
4473- ## Phase interaction options for Fluidity's multiphase flow model.
4474- element multiphase_interaction {
4475- comment,
4476- fluid_particle_drag?,
4477- heat_transfer?
4478- }
4479- )
4480-
4481-fluid_particle_drag =
4482- (
4483- ## Fluid-particle drag term.
4484- element fluid_particle_drag {
4485- drag_correlation
4486- }
4487- )
4488-
4489-drag_correlation =
4490- (
4491- ## Stokes drag correlation: 24/Re_p
4492- ## where Re_p is the particle Reynolds number.
4493- element drag_correlation {
4494- attribute name { "stokes" },
4495- comment
4496- }|
4497- ## Fluid-particle drag term by Wen & Yu (1966).
4498- element drag_correlation {
4499- attribute name { "wen_yu" },
4500- comment
4501- }|
4502- ## Fluid-particle drag term by Ergun (1952).
4503- element drag_correlation {
4504- attribute name { "ergun" },
4505- comment
4506- }
4507- )
4508-
4509-heat_transfer =
4510- (
4511- ## Heat transfer term for the
4512- ## multiphase internal energy equation.
4513- ## Note: Only for fluid-particle phase pairs.
4514- element heat_transfer {
4515- heat_transfer_coefficient
4516- }
4517- )
4518-
4519-heat_transfer_coefficient =
4520- (
4521- ## Heat transfer coefficient by Gunn (1978).
4522- element heat_transfer_coefficient {
4523- attribute name { "gunn" },
4524- comment
4525- }
4526- )
4527\ No newline at end of file
4528
4529=== removed file 'schemas/multiphase_interaction.rng'
4530--- schemas/multiphase_interaction.rng 2012-06-15 19:32:03 +0000
4531+++ schemas/multiphase_interaction.rng 1970-01-01 00:00:00 +0000
4532@@ -1,65 +0,0 @@
4533-<?xml version="1.0" encoding="UTF-8"?>
4534-<grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0">
4535- <!-- Phase interaction options for Fluidity's multiphase flow model. -->
4536- <define name="multiphase_interaction">
4537- <element name="multiphase_interaction">
4538- <a:documentation>Phase interaction options for Fluidity's multiphase flow model.</a:documentation>
4539- <ref name="comment"/>
4540- <optional>
4541- <ref name="fluid_particle_drag"/>
4542- </optional>
4543- <optional>
4544- <ref name="heat_transfer"/>
4545- </optional>
4546- </element>
4547- </define>
4548- <define name="fluid_particle_drag">
4549- <element name="fluid_particle_drag">
4550- <a:documentation>Fluid-particle drag term.</a:documentation>
4551- <ref name="drag_correlation"/>
4552- </element>
4553- </define>
4554- <define name="drag_correlation">
4555- <choice>
4556- <element name="drag_correlation">
4557- <a:documentation>Stokes drag correlation: 24/Re_p
4558-where Re_p is the particle Reynolds number.</a:documentation>
4559- <attribute name="name">
4560- <value>stokes</value>
4561- </attribute>
4562- <ref name="comment"/>
4563- </element>
4564- <element name="drag_correlation">
4565- <a:documentation>Fluid-particle drag term by Wen &amp; Yu (1966).</a:documentation>
4566- <attribute name="name">
4567- <value>wen_yu</value>
4568- </attribute>
4569- <ref name="comment"/>
4570- </element>
4571- <element name="drag_correlation">
4572- <a:documentation>Fluid-particle drag term by Ergun (1952).</a:documentation>
4573- <attribute name="name">
4574- <value>ergun</value>
4575- </attribute>
4576- <ref name="comment"/>
4577- </element>
4578- </choice>
4579- </define>
4580- <define name="heat_transfer">
4581- <element name="heat_transfer">
4582- <a:documentation>Heat transfer term for the
4583-multiphase internal energy equation.
4584-Note: Only for fluid-particle phase pairs.</a:documentation>
4585- <ref name="heat_transfer_coefficient"/>
4586- </element>
4587- </define>
4588- <define name="heat_transfer_coefficient">
4589- <element name="heat_transfer_coefficient">
4590- <a:documentation>Heat transfer coefficient by Gunn (1978).</a:documentation>
4591- <attribute name="name">
4592- <value>gunn</value>
4593- </attribute>
4594- <ref name="comment"/>
4595- </element>
4596- </define>
4597-</grammar>
4598
4599=== modified file 'schemas/prognostic_field_options.rnc'
4600--- schemas/prognostic_field_options.rnc 2012-08-22 16:03:09 +0000
4601+++ schemas/prognostic_field_options.rnc 2012-09-04 15:00:37 +0000
4602@@ -91,8 +91,7 @@
4603 ## T the scalar field value on the surface,
4604 ## C0 is the input order zero coefficient and
4605 ## C1 is the input order one coefficient.
4606- ## THIS WILL ONLY WORK FOR CONTINUOUS GALERKIN OR CONTROL VOLUME
4607- ## (USING AN ELEMENTGRADIENT DIFFUSION SCHEME) SPATIAL DISCRETISATIONS.
4608+ ## THIS WILL ONLY WORK FOR CONTINUOUS GALERKIN SPATIAL DISCRETISATION
4609 element type {
4610 attribute name { "robin" },
4611 ## The order zero coefficient represented as C0 in
4612
4613=== modified file 'schemas/prognostic_field_options.rng'
4614--- schemas/prognostic_field_options.rng 2012-08-22 16:03:09 +0000
4615+++ schemas/prognostic_field_options.rng 2012-09-04 15:00:37 +0000
4616@@ -113,8 +113,7 @@
4617 T the scalar field value on the surface,
4618 C0 is the input order zero coefficient and
4619 C1 is the input order one coefficient.
4620- THIS WILL ONLY WORK FOR CONTINUOUS GALERKIN OR CONTROL VOLUME
4621- (USING AN ELEMENTGRADIENT DIFFUSION SCHEME) SPATIAL DISCRETISATIONS.</a:documentation>
4622+ THIS WILL ONLY WORK FOR CONTINUOUS GALERKIN SPATIAL DISCRETISATION</a:documentation>
4623 <attribute name="name">
4624 <value>robin</value>
4625 </attribute>
4626
4627=== modified file 'schemas/spatial_discretisation.rnc'
4628--- schemas/spatial_discretisation.rnc 2012-06-02 12:15:06 +0000
4629+++ schemas/spatial_discretisation.rnc 2012-09-04 15:00:37 +0000
4630@@ -494,7 +494,7 @@
4631 ## basis functions of the parent finite element mesh to
4632 ## form the divergence.
4633 ##
4634- ## DOES NOT CURRENTLY WORK WITH WEAK DIRICHLET BOUNDARY CONDITIONS!
4635+ ## DOES NOT CURRENTLY WORK WITH ROBIN OR WEAK DIRICHLET BOUNDARY CONDITIONS!
4636 ##
4637 ## Based on schemes in Handbook of Numerical Analysis,
4638 ## P.G. Ciarlet, J.L. Lions eds, vol 7, pp 713-1020
4639
4640=== modified file 'schemas/spatial_discretisation.rng'
4641--- schemas/spatial_discretisation.rng 2012-06-02 12:15:06 +0000
4642+++ schemas/spatial_discretisation.rng 2012-09-04 15:00:37 +0000
4643@@ -546,7 +546,7 @@
4644 basis functions of the parent finite element mesh to
4645 form the divergence.
4646
4647-DOES NOT CURRENTLY WORK WITH WEAK DIRICHLET BOUNDARY CONDITIONS!
4648+DOES NOT CURRENTLY WORK WITH ROBIN OR WEAK DIRICHLET BOUNDARY CONDITIONS!
4649
4650 Based on schemes in Handbook of Numerical Analysis,
4651 P.G. Ciarlet, J.L. Lions eds, vol 7, pp 713-1020</a:documentation>
4652
4653=== modified file 'tests/diff_src_robin_1d/diff_src_robin_1d.xml'
4654--- tests/diff_src_robin_1d/diff_src_robin_1d.xml 2012-06-02 12:15:06 +0000
4655+++ tests/diff_src_robin_1d/diff_src_robin_1d.xml 2012-09-04 15:00:37 +0000
4656@@ -6,8 +6,7 @@
4657 <tags>flml</tags>
4658 <problem_definition length="short" nprocs="1">
4659 <command_line>
4660-../../bin/fluidity diff_src_robin_1d_cg_p1_A.flml
4661-../../bin/fluidity diff_src_robin_1d_cvelegrad_p1_A.flml
4662+../../bin/fluidity diff_src_robin_1d_cg_p1_A.flml
4663 </command_line>
4664 <!-- One dimensional problem for diffusion, source with right robin bc and left neumann zero compared to analytic-->
4665 </problem_definition>
4666@@ -20,14 +19,6 @@
4667 from fluidity_tools import stat_parser as stat
4668 cg_p1_A_error_linf = stat("diff_src_robin_1d_cg_p1_A.stat")["fluid"]["TError"]["max"][-1]
4669 </variable>
4670- <variable name="cvelegrad_p1_A_error_l2" language="python">
4671-from fluidity_tools import stat_parser as stat
4672-cvelegrad_p1_A_error_l2 = stat("diff_src_robin_1d_cvelegrad_p1_A.stat")["fluid"]["TError"]["cv_l2norm"][-1]
4673- </variable>
4674- <variable name="cvelegrad_p1_A_error_linf" language="python">
4675-from fluidity_tools import stat_parser as stat
4676-cvelegrad_p1_A_error_linf = stat("diff_src_robin_1d_cvelegrad_p1_A.stat")["fluid"]["TError"]["max"][-1]
4677- </variable>
4678 </variables>
4679 <pass_tests>
4680 <test name="Check cg_p1_A_error_l2 less than 1.0e-8" language="python">
4681@@ -36,12 +27,6 @@
4682 <test name="Check cg_p1_A_error_linf less than 1.0e-8" language="python">
4683 assert(abs(cg_p1_A_error_linf) &lt; 1.0e-8)
4684 </test>
4685- <test name="Check cvelegrad_p1_A_error_l2 less than 1.0e-8" language="python">
4686-assert(abs(cvelegrad_p1_A_error_l2) &lt; 1.0e-8)
4687- </test>
4688- <test name="Check cvelegrad_p1_A_error_linf less than 1.0e-8" language="python">
4689-assert(abs(cvelegrad_p1_A_error_linf) &lt; 1.0e-8)
4690- </test>
4691 </pass_tests>
4692 <warn_tests>
4693 </warn_tests>
4694
4695=== removed file 'tests/diff_src_robin_1d/diff_src_robin_1d_cvelegrad_p1_A.flml'
4696--- tests/diff_src_robin_1d/diff_src_robin_1d_cvelegrad_p1_A.flml 2012-06-02 12:18:06 +0000
4697+++ tests/diff_src_robin_1d/diff_src_robin_1d_cvelegrad_p1_A.flml 1970-01-01 00:00:00 +0000
4698@@ -1,244 +0,0 @@
4699-<?xml version='1.0' encoding='utf-8'?>
4700-<fluidity_options>
4701- <simulation_name>
4702- <string_value lines="1">diff_src_robin_1d_cvelegrad_p1_A</string_value>
4703- </simulation_name>
4704- <problem_type>
4705- <string_value lines="1">fluids</string_value>
4706- </problem_type>
4707- <geometry>
4708- <dimension>
4709- <integer_value rank="0">1</integer_value>
4710- </dimension>
4711- <mesh name="CoordinateMesh">
4712- <from_file file_name="diff_src_robin_1d_A">
4713- <format name="triangle"/>
4714- <stat>
4715- <include_in_stat/>
4716- </stat>
4717- </from_file>
4718- </mesh>
4719- <quadrature>
4720- <degree>
4721- <integer_value rank="0">2</integer_value>
4722- </degree>
4723- </quadrature>
4724- </geometry>
4725- <io>
4726- <dump_format>
4727- <string_value>vtk</string_value>
4728- </dump_format>
4729- <dump_period_in_timesteps>
4730- <constant>
4731- <integer_value rank="0">0</integer_value>
4732- </constant>
4733- </dump_period_in_timesteps>
4734- <output_mesh name="CoordinateMesh"/>
4735- <stat/>
4736- </io>
4737- <timestepping>
4738- <current_time>
4739- <real_value rank="0">0.0</real_value>
4740- </current_time>
4741- <timestep>
4742- <real_value rank="0">2.5</real_value>
4743- <comment>Time stepping is not actually needed for this test case but this is just to check the acceleration field solver</comment>
4744- </timestep>
4745- <finish_time>
4746- <real_value rank="0">10.0</real_value>
4747- <comment>Time stepping is not actually needed for this test case but this is just to check the acceleration field solver</comment>
4748- </finish_time>
4749- </timestepping>
4750- <physical_parameters/>
4751- <material_phase name="fluid">
4752- <vector_field name="Velocity" rank="1">
4753- <prescribed>
4754- <mesh name="CoordinateMesh"/>
4755- <value name="WholeMesh">
4756- <constant>
4757- <real_value shape="1" dim1="dim" rank="1">0.0</real_value>
4758- </constant>
4759- </value>
4760- <output/>
4761- <stat>
4762- <include_in_stat/>
4763- </stat>
4764- <detectors>
4765- <exclude_from_detectors/>
4766- </detectors>
4767- </prescribed>
4768- </vector_field>
4769- <scalar_field name="T" rank="0">
4770- <prognostic>
4771- <mesh name="CoordinateMesh"/>
4772- <equation name="AdvectionDiffusion"/>
4773- <spatial_discretisation>
4774- <control_volumes>
4775- <mass_terms>
4776- <exclude_mass_terms/>
4777- </mass_terms>
4778- <face_value name="FirstOrderUpwind"/>
4779- <diffusion_scheme name="ElementGradient"/>
4780- </control_volumes>
4781- <conservative_advection>
4782- <real_value rank="0">1.0</real_value>
4783- </conservative_advection>
4784- </spatial_discretisation>
4785- <temporal_discretisation>
4786- <theta>
4787- <real_value rank="0">1.0</real_value>
4788- </theta>
4789- <control_volumes>
4790- <pivot_theta>
4791- <real_value rank="0">1.0</real_value>
4792- </pivot_theta>
4793- </control_volumes>
4794- </temporal_discretisation>
4795- <solver>
4796- <iterative_method name="gmres">
4797- <restart>
4798- <integer_value rank="0">30</integer_value>
4799- </restart>
4800- </iterative_method>
4801- <preconditioner name="sor"/>
4802- <relative_error>
4803- <real_value rank="0">1.0e-10</real_value>
4804- </relative_error>
4805- <absolute_error>
4806- <real_value rank="0">1.0e-10</real_value>
4807- </absolute_error>
4808- <max_iterations>
4809- <integer_value rank="0">1000</integer_value>
4810- </max_iterations>
4811- <never_ignore_solver_failures/>
4812- <diagnostics>
4813- <monitors/>
4814- </diagnostics>
4815- </solver>
4816- <initial_condition name="WholeMesh">
4817- <constant>
4818- <real_value rank="0">9.87654321</real_value>
4819- </constant>
4820- </initial_condition>
4821- <boundary_conditions name="right">
4822- <surface_ids>
4823- <integer_value shape="1" rank="1">2</integer_value>
4824- </surface_ids>
4825- <type name="robin">
4826- <order_zero_coefficient>
4827- <constant>
4828- <real_value rank="0">3.14</real_value>
4829- </constant>
4830- </order_zero_coefficient>
4831- <order_one_coefficient>
4832- <constant>
4833- <real_value rank="0">0.78</real_value>
4834- </constant>
4835- </order_one_coefficient>
4836- </type>
4837- </boundary_conditions>
4838- <tensor_field name="Diffusivity" rank="2">
4839- <prescribed>
4840- <value name="WholeMesh">
4841- <isotropic>
4842- <constant>
4843- <real_value rank="0">1.0</real_value>
4844- </constant>
4845- </isotropic>
4846- </value>
4847- <output/>
4848- </prescribed>
4849- </tensor_field>
4850- <scalar_field name="Source" rank="0">
4851- <prescribed>
4852- <value name="WholeMesh">
4853- <constant>
4854- <real_value rank="0">1.234</real_value>
4855- </constant>
4856- </value>
4857- <output/>
4858- <stat/>
4859- <detectors>
4860- <exclude_from_detectors/>
4861- </detectors>
4862- </prescribed>
4863- </scalar_field>
4864- <output/>
4865- <stat>
4866- <include_cv_stats/>
4867- </stat>
4868- <convergence>
4869- <include_in_convergence/>
4870- </convergence>
4871- <detectors>
4872- <include_in_detectors/>
4873- </detectors>
4874- <steady_state>
4875- <include_in_steady_state/>
4876- </steady_state>
4877- <consistent_interpolation/>
4878- </prognostic>
4879- </scalar_field>
4880- <scalar_field name="TAnalytic" rank="0">
4881- <prescribed>
4882- <mesh name="CoordinateMesh"/>
4883- <value name="WholeMesh">
4884- <python>
4885- <string_value lines="20" type="code" language="python">def val(X,t):
4886- # get the 1d coord
4887- x = X[0]
4888-
4889- # define the analytic input
4890- x_left = 0.0
4891- x_right = 10.0
4892- src = 1.234
4893- bc_right_r0 = 3.14
4894- bc_right_r1 = 0.78
4895-
4896- T_inf = bc_right_r0 / bc_right_r1
4897- gamma = bc_right_r1
4898-
4899- # calc the analytic solution
4900- T = (src/2.0)*(x_right**2 - x**2) \
4901- + (src*x_left)*(x - x_right) \
4902- + (src/gamma)*(x_right - x_left) \
4903- + T_inf
4904-
4905- return T</string_value>
4906- </python>
4907- </value>
4908- <output/>
4909- <stat>
4910- <include_cv_stats/>
4911- </stat>
4912- <detectors>
4913- <exclude_from_detectors/>
4914- </detectors>
4915- </prescribed>
4916- </scalar_field>
4917- <scalar_field name="TError" rank="0">
4918- <diagnostic>
4919- <algorithm name="scalar_python_diagnostic" material_phase_support="single">
4920- <string_value lines="20" type="code" language="python">field1=state.scalar_fields["T"]
4921-field2=state.scalar_fields["TAnalytic"]
4922-for i in range(field.node_count):
4923- field.set(i, abs(field1.node_val(i)-field2.node_val(i)))</string_value>
4924- </algorithm>
4925- <mesh name="CoordinateMesh"/>
4926- <output/>
4927- <stat>
4928- <include_cv_stats/>
4929- </stat>
4930- <convergence>
4931- <include_in_convergence/>
4932- </convergence>
4933- <detectors>
4934- <include_in_detectors/>
4935- </detectors>
4936- <steady_state>
4937- <include_in_steady_state/>
4938- </steady_state>
4939- </diagnostic>
4940- </scalar_field>
4941- </material_phase>
4942-</fluidity_options>
4943
4944=== removed directory 'tests/flux_bc'
4945=== removed file 'tests/flux_bc/Makefile'
4946--- tests/flux_bc/Makefile 2012-07-09 23:48:46 +0000
4947+++ tests/flux_bc/Makefile 1970-01-01 00:00:00 +0000
4948@@ -1,22 +0,0 @@
4949-preprocess:
4950- @echo **********Creating meshes
4951- gmsh -2 -o flux_bc_2d.msh src/flux_bc_2d.geo
4952- gmsh -3 -o flux_bc_3d.msh src/flux_bc_3d.geo
4953- ../../bin/gmsh2triangle --2d flux_bc_2d.msh
4954- ../../bin/gmsh2triangle flux_bc_3d.msh
4955-
4956-run:
4957- @echo **********Running 2D simulation
4958- ../../bin/fluidity flux_bc_2d.flml
4959- @echo **********Running 3D simulation
4960- ../../bin/fluidity flux_bc_3d.flml
4961-
4962-input: clean preprocess
4963-
4964-clean:
4965- rm -f *.stat *.steady_state*
4966- rm -f *.d.* *.vtu
4967- rm -f *.msh
4968- rm -f *.ele *.edge *.node *.poly *.face
4969- rm -f matrixdump* *checkpoint* *.log* *.err*
4970-
4971
4972=== removed file 'tests/flux_bc/flux_bc.xml'
4973--- tests/flux_bc/flux_bc.xml 2012-08-17 19:58:54 +0000
4974+++ tests/flux_bc/flux_bc.xml 1970-01-01 00:00:00 +0000
4975@@ -1,54 +0,0 @@
4976-<?xml version="1.0" encoding="UTF-8" ?>
4977-<!DOCTYPE testproblem SYSTEM "regressiontest.dtd">
4978-
4979-<testproblem>
4980-
4981- <name>flux_bc</name>
4982- <owner userid="ctj10"/>
4983- <tags>flml</tags>
4984-
4985- <problem_definition length="medium" nprocs="1">
4986- <command_line>make run</command_line>
4987- <!-- This checks the integral of the PhaseVolumeFraction field at t = 1 second. -->
4988- <!-- The volumetric flux (r) is set to 0.5 m^3/s/m^2, and the flux boundary condition -->
4989- <!-- enforces d/dt \int_{volume} vfrac = \int_{surface} r -->
4990- </problem_definition>
4991-
4992- <variables>
4993- <variable name="vfrac_integral_2d" language="python">
4994-from fluidity_tools import stat_parser
4995-s = stat_parser("flux_bc_2d.stat")
4996-vfrac_integral_2d = s["Tephra"]["PhaseVolumeFraction"]["integral"]
4997- </variable>
4998-
4999- <variable name="vfrac_integral_3d" language="python">
5000-from fluidity_tools import stat_parser
The diff has been truncated for viewing.