├── .dir-locals.el
├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── doc.md
├── doc_pages
├── 1-running
│ ├── 1-build.md
│ ├── 2-writing.md
│ ├── 3-compile.md
│ └── index.md
├── 2-numerics
│ ├── 1-equations.md
│ ├── 2-spatial.md
│ ├── 3-shelf-solver.md
│ ├── 4-plume-solver.md
│ ├── 5-benchmarking.md
│ ├── 6-horzint.md
│ └── index.md
├── 3-codeDesign
│ ├── 1-representing.md
│ ├── 2-discretisation.md
│ ├── 3-solvers.md
│ ├── 4-parameterisations.md
│ └── index.md
├── 4-plotting
│ ├── 1-readers.md
│ ├── 3-tools.md
│ ├── 5-layers.md
│ ├── 6-scripts.md
│ └── index.md
├── 6-bibliog.md
└── index.md
├── get_deps
├── main.f90
├── media
├── MacMackin_thesis.pdf
├── ambient.png
├── basal.png
├── boundaries.svg
├── cryo.png
├── entrainment.png
├── eos.png
├── err_comp_100.svg
├── err_comp_50.svg
├── err_dt.svg
├── err_evolution.svg
├── err_evolution_const.svg
├── factual_classes.png
├── glacier.png
├── horizontal_integration.svg
├── horizontal_integration_planar.svg
├── icebound.png
├── iceshelf.svg
├── inheritance_diagram.png
├── isoft_classes.png
├── isoft_sequence.png
├── layers.svg
├── melt.png
├── object_tree.png
├── plumebound.png
├── precond.png
├── precond_001.png
├── precond_002.png
└── viscosity.png
├── mj-config.js
├── plotting
├── __init__.py
├── calculus.py
├── dimensionless_nums.py
├── entrainment.py
├── eos.py
├── layers.py
├── melt.py
├── readers.py
└── viscosity.py
├── scripts
├── __init__.py
├── decompose.py
├── display.py
├── get_trans_coefs
├── ice_continuity.py
├── internal_layers.py
├── plume_momentum.py
└── shock.py
├── src
├── ambient.F90
├── ambient
│ └── uniform.F90
├── asymmetric_plume.F90
├── averaged_linear_eos.F90
├── basal_surface.F90
├── boundaries
│ ├── dallaston2015_glacier.F90
│ ├── dallaston2015_seasonal.F90
│ ├── seasonal_glacier.F90
│ ├── simple_plume.F90
│ └── upstream_plume.F90
├── boundary_types.f90
├── coriolis_block.F90
├── cryosphere.F90
├── entrainment.F90
├── equation_of_state.F90
├── finite_difference_block.F90
├── glacier.F90
├── glacier_boundary.F90
├── ground.F90
├── ice_sheet.F90
├── ice_shelf.F90
├── jacobian_block.F90
├── linear_eos.F90
├── melt_relationship.F90
├── meta.F90
├── meta_implementation.F90
├── nitgm2.f
├── nitsol.f90
├── nitstb2.f
├── nittfq2.f
├── ode_solvers.f90
├── parameterisations
│ ├── averaged_one_equation_melt.f90
│ ├── dallaston2015_melt.F90
│ ├── glens_law.F90
│ ├── jenkins1991_entrainment.F90
│ ├── kochergin1987_entrainment.F90
│ ├── newtonian_viscosity.F90
│ └── one_equation_melt.F90
├── plume.F90
├── plume_boundary.F90
├── preconditioner.F90
├── prescribed_salinity_eos.F90
├── pseudospectral_block.F90
├── rksuite_90.f90
├── rootfind.f90
├── specfun.f90
├── splev.f
├── static_plume.F90
├── transverse_coefficients.f90
├── uniform_gradient_field.f90
└── viscosity.F90
└── tests
├── .dir-locals.el
├── ambient_test.pf
├── asym_plume_test.pf
├── bc_test.pf
├── coriolis_block_test.pf
├── cryosphere_test.pf
├── dallaston2015_melt_test.pf
├── fin_diff_block_test.pf
├── glens_law.pf
├── ground_test.pf
├── ice_sheet_test.pf
├── ice_shelf_test.pf
├── jacobian_block_test.pf
├── jenkins1991_entrainment.pf
├── kochergin1987_entrainment.pf
├── newtonian_viscosity.pf
├── nitsol_test.pf
├── plume_test.pf
├── preconditioner_test.pf
├── pseudospec_block_test.pf
├── testSuites.inc
└── test_utils.pf
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((nil . ((eval . (set (make-local-variable 'my-project-path)
2 | (file-name-directory
3 | (let ((d (dir-locals-find-file ".")))
4 | (if (stringp d) d (car d))))))
5 | (eval . (message "Project directory set to `%s'." my-project-path))
6 | (eval . (setq flycheck-gfortran-include-path
7 | (mapcar (lambda (p) (expand-file-name p my-project-path))
8 | '("~/.local/include/"
9 | "./mod"
10 | "./factual/mod"
11 | "./tests/mod"))))
12 | (eval . (message "Include directories set to `%s'." flycheck-gfortran-include-path))
13 | (eval . (setq flycheck-gfortran-args
14 | (concat "-DDEBUG -J"
15 | (expand-file-name '"./mod" my-project-path))))
16 | (eval . (message "Gfortran arguments set to `%s'." flycheck-gfortran-args))
17 | )))
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | doc/
2 | *~
3 | #*
4 | *.o
5 | *.a
6 | *.so
7 | *.gcda
8 | *.gcno
9 | *.gcov
10 | *.mod
11 | *.smod
12 | *.x
13 | *.d
14 | tests/*.F90
15 | *.pyc
16 | *.log
17 | *.log.*
18 | *.mp4
19 | *.png
20 | !media/*.png
21 | *.out
22 | *.old
23 | *.h5
24 | isoft
25 | buildtools/
26 | compile.sh
27 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "factual"]
2 | path = libs/factual
3 | url = https://github.com/cmacmackin/factual
4 | [submodule "libs/nitsol"]
5 | path = libs/nitsol
6 | url = https://github.com/cmacmackin/nitsol
7 | [submodule "libs/flogging"]
8 | path = libs/flogging
9 | url = https://github.com/cmacmackin/flogging
10 | [submodule "libs/FACE"]
11 | path = libs/FACE
12 | url = https://github.com/szaghi/FACE
13 | [submodule "libs/lapack95"]
14 | path = libs/lapack95
15 | url = https://github.com/cmacmackin/lapack95
16 | [submodule "libs/pFUnit"]
17 | path = libs/pFUnit
18 | url = https://github.com/Goddard-Fortran-Ecosystem/pFUnit
19 | [submodule "libs/PENF"]
20 | path = libs/PENF
21 | url = https://github.com/szaghi/PENF
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ISOFT: Ice Shelf/Ocean Fluid and Thermodynamics
2 |
3 | [](https://doi.org/10.5281/zenodo.1422482)
4 | [](https://www.gnu.org/licenses/gpl-3.0)
5 |
6 | ISOFT is a piece of software/suite of tools which I developed while
7 | working on my PhD thesis to simulate the evolution of ice shelves
8 | coupled to ocean plumes. It attempts to provide an object oriented,
9 | extensible framework with which to model glacial flow using modern
10 | Fortran. Though developed for ice shelves, it could in principle be
11 | modified to simulated grounded ice dynamics as well. I've published
12 | the code and documentation to GitHub in the hopes that it might be
13 | useful to others.
14 |
15 | As much as practical, ISOFT was kept agnostic as to whether it was
16 | handling a 1-D or a 2-D representation of an ice shelf/plume. However,
17 | the current implementation does explicitly assume a 1-D system in a
18 | number of instances and would thus need to be modified to handle 2-D
19 | problems. 1-D simulations were sufficiently fast that they could be
20 | run in serial. Multithreading could easily be implemented in many
21 | parts of the code where arithmetic is performed on arrays. Indeed,
22 | most of these cases are simple enough that a compiler may be able to
23 | parallelise them automatically. More sophisticated approaches
24 | involving message passing would likely be necessary to make 2-D
25 | simulations practical, but this would be far more difficult to
26 | implement and would likely require substantial refactoring of the
27 | code. In particular, the nonlinear solvers would likely need to be
28 | replaced.
29 |
30 | ## Quick Start
31 |
32 | The build-script for FORD assumes you have the libraries and header
33 | files for [FFTW3](http://www.fftw.org/),
34 | [HDF5](https://www.hdfgroup.org/solutions/hdf5/), an implementation of
35 | [BLAS](http://www.netlib.org/blas/), and
36 | [LAPACK](http://www.netlib.org/lapack/) installed on your computer. It
37 | also requires you to have the
38 | [virtualenv](https://virtualenv.pypa.io/en/latest/) installed. ISOFT
39 | is known to compile with `gfortran` v6.2 and, barring regressions,
40 | should work with all subsequent releases. There are a number of
41 | other, more obscure libraries upon which ISOFT depends, but the
42 | Makefile handles the downloading and building of these itself. You can
43 | build ISOFT on an Ubuntu-like operating system using the commands
44 | below:
45 | ```
46 | sudo apt-get install libfftw3-dev libhdf5-dev libopenblas-dev liblapack-dev python-virtualenv gfortran-6
47 | git clone --recurse-submodules https://github.com/cmacmackin/isoft
48 | cd isoft
49 | make lib # Builds libisoft.a
50 | make tests # Builds and runs unit tests
51 | make script # Creates a compile-script you can use to build and
52 | # link your own programs with the ISOFT library
53 | ```
54 |
55 | This builds a static library which provides derived types for
56 | simulating the evolution of an ice shelf. You initialise these objects
57 | in your own programs and then call their methods to start the
58 | simulation. An example of such a program can be found in the sample
59 | file `main.f90`. This can be built and run as follows:
60 | ```
61 | compile.sh main.f90 isoft_example
62 | ./isoft_example
63 | ```
64 |
65 | The `compile.sh` script was generated specifically for your build of
66 | ISOFT and can be used, without modification from any directory on your
67 | computer. It's call signature is
68 | ```
69 | ./compile.sh [ main-file-name [ executable-name ] ]
70 | ```
71 | where `main-file-name` is the program file to be compiled and
72 | `executable-name` is the name to give the resulting executable. These
73 | default to `main.f90` and `isoft`, respectively.
74 |
75 | ## Documentation
76 |
77 | Information on how to install and use ISOFT is available on
78 | [GitHub Pages](http://cmacmackin.github.io/isoft). This includes
79 | sections (taken from my thesis) on the numerical methods and code
80 | design choices which were made. A detailed description of the API
81 | is also provided. This documentation can be generated locally using the
82 | [FORD tool](https://github.com/Fortran-FOSS-Programmers/ford). To
83 | install FORD in a virtual environment and then run it to generate the
84 | documentation, execute
85 | ```
86 | make doc
87 | ```
88 | If FORD is already present on your system then you can simply run
89 | ```
90 | ford doc.md
91 | ```
92 |
93 | ## License
94 |
95 | ISOFT is licensed under the GNU General Public License (GPL) v3.0 or
96 | later. The terms are provided in the file `LICENSE`. The Lesser General
97 | Public License (LGPL) would have been used, but the Chebyshev pseudo-spectral
98 | implementation uses the FFTW3 library, which is licensed under the GPL.
99 |
100 |
--------------------------------------------------------------------------------
/doc.md:
--------------------------------------------------------------------------------
1 | project: ISOFT
2 | src_dir: ./src
3 | ./plotting
4 | ./scripts
5 | include: ~/.local/include
6 | media_dir: ./media
7 | page_dir: doc_pages
8 | output_dir: ./doc
9 | summary: Ice Shelf/Ocean Fluid- and Thermodynamics: a suite of tools and
10 | programs to use for simulating the evolution of ice shelves
11 | interacting with the ocean.
12 | project_github: https://github.com/cmacmackin/isoft
13 | display: public
14 | protected
15 | private
16 | graph: true
17 | source: true
18 | search: true
19 | mathjax_config: mj-config.js
20 | extra_filetypes: py #
21 | exclude: __init__.py
22 | extra_mods: factual_mod:https://cmacmackin.github.io/factual
23 | f95_lapack:http://www.netlib.org/lapack95/lug95/
24 | logger_mod:https://cmacmackin.github.io/flogging/
25 | penf:http://szaghi.github.io/PENF/index.html
26 | h5lt:https://support.hdfgroup.org/HDF5/doc/HL/RM_H5LT.html
27 | hdf5:https://support.hdfgroup.org/HDF5/doc/RM/RM_H5Front.html#LowLevelAPIs
28 | chebyshev_mod:https://cmacmackin.github.io/factual/module/chebyshev_mod.html
29 | author: Chris MacMackin
30 | author_description: I am a graduate student at the University of Oxford, studying
31 | the melting and evolution of ice shelves.
32 | website: https://cmacmackin.github.io
33 | github: https://github.com/cmacmackin
34 | linkedin: https://www.linkedin.com/in/christopher-macmackin-a11283173/
35 | email: cmacmackin@gmail.com
36 |
37 |
38 | ISOFT is a piece of software/suite of tools which I developed while
39 | working on my PhD thesis to simulate the evolution of ice shelves
40 | coupled to ocean plumes. It attempts to provide an object oriented,
41 | extensible framework with which to model glacial flow using modern
42 | Fortran. Though developed for ice shelves, it could in principle be
43 | modified to simulated grounded ice dynamics as well. I've published
44 | the code and documentation to GitHub in the hopes that it might be
45 | useful to others.
46 |
47 |
--------------------------------------------------------------------------------
/doc_pages/1-running/1-build.md:
--------------------------------------------------------------------------------
1 | Title: Compiling the Code
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | The easiest way to obtain ISOFT is by downloading it from GitHub:
6 | ```
7 | make clone --recurse-submodules https://github.com/cmacmackin/isoft
8 | ```
9 | This will download all libraries included in ISOFT as submodules
10 | (listed below).
11 |
12 | ## Dependencies
13 |
14 | ISOFT depends on a number of external pieces of software. Mostly these
15 | are libraries which it uses, although there are also a few programs
16 | which are used at build-time. Except for the compiler, the latter are
17 | all Python-based and the Makefile will automatically install them in a
18 | virtual environment. Of the libraries, those which are widely used
19 | must be installed on your system prior to building ISOFT. The
20 | remainder are distributed with ISOFT and will automatically be
21 | compiled as part of the build process. Builds have been tested with
22 | the `gfortran` compiler and are known to work with v6.2. In principle,
23 | subsequent releases of `gfortran` should also work, barring any
24 | regressions.
25 |
26 | The following libraries and programs must be installed on your operating system
27 | prior to starting the build process. The corresponding package name in
28 | Ubuntu is given in parentheses.
29 |
30 | - [FFTW3](http://www.fftw.org/) (libfftw3-dev)
31 | - [HDF5](https://www.hdfgroup.org/solutions/hdf5/) (libhdf5-dev)
32 | - An implementation of [BLAS](http://www.netlib.org/blas/) (e.g., libopenblas-dev)
33 | - [LAPACK](http://www.netlib.org/lapack/) (liblapack-dev)
34 | - [virtualenv](https://virtualenv.pypa.io/en/latest/) (python-virtualenv)
35 | - [gfortran](https://gcc.gnu.org/wiki/GFortran) (gfortran-6)
36 |
37 | The following programs are required during the build process. They
38 | will be installed in a virtual environment called `buildtools` in the
39 | top of the ISOFT directory.
40 |
41 | - [fypp](https://fypp.readthedocs.io/en/stable/fypp.html), a preprocessor for Fortran
42 | - [FoBiS.py](https://github.com/szaghi/FoBiS/wiki), a simple build system for Fortran
43 | - [FORD](https://github.com/Fortran-FOSS-Programmers/ford/wiki), a documentation tool for Fortran
44 |
45 | The following libraries were downloaded with ISOFT as submodules and
46 | will automatically be compiled as part of the build process:
47 |
48 | - [FACE](https://szaghi.github.io/FACE/index.html)
49 | - [factual](https://github.com/cmacmackin/factual)
50 | - [flogging](https://cmacmackin.github.io/flogging/)
51 | - [lapack95](http://www.netlib.org/lapack95/)
52 | - [nitsol](https://www.osti.gov/biblio/433349)
53 | - [PENF](http://szaghi.github.io/PENF/index.html)
54 | - [pFUnit](http://pfunit.sourceforge.net/)
55 |
56 | ## The Makefile
57 |
58 | A Makefile is provided which can handle the build process on Unix-like
59 | operating systems in most cases. It features the following build
60 | targets:
61 |
62 | - __all__: alias for __lib__ (default)
63 | - __all_objects__: rebuilds all object files for the library
64 | - __clean__: deletes object files, module files, dependency files, and Emacs backup files
65 | - __doc__: generates HTML documentation for ISOFT
66 | - __script__: writes a script for compiling and linking programs which use the ISOFT library
67 | - __tests__: builds and runs the unit tests
68 | - __lib__: builds the static library of ISOFT routines and derived types
69 |
70 | At the top of the Makefile are two definitions used for finding
71 | libraries installed on your system. These are
72 | ```Makefile
73 | SYSTEM_LIB := /usr/lib # Paths containing external libraries
74 | SYSTEM_INC := /usr/include # Paths containing module/include files
75 | # for external libraries
76 | ```
77 | These defaults should work on Ubuntu-like operating systems.
78 | They may be overridden if necessary. Multiple paths may also be
79 | provided, separated by white-space.
80 |
81 | ## Building
82 |
83 | After having amended the Makefile as described above, building ISOFT
84 | should be straightforward. Simply run
85 | ```
86 | make lib
87 | ```
88 | to compile the necessary dependencies and create the `libisoft.a` file.
89 | Then run
90 | ```
91 | make script
92 | ```
93 | to generate a script which can [compile and link programs](./3-compile.html)
94 | using ISOFT.
95 |
96 | ## Unit Tests
97 |
98 | An extensive set of unit tests has been written, using the
99 | [pFUnit](http://pfunit.sourceforge.net/) framework. These ensure that
100 | methods behave as expected and, where possible, test that simulations
101 | converge to analytically-predicted solutions. These tests can be built
102 | and run with the command
103 | ```
104 | make tests
105 | ```
106 |
--------------------------------------------------------------------------------
/doc_pages/1-running/2-writing.md:
--------------------------------------------------------------------------------
1 | Title: Writing Software Using the ISOFT Framework
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ## The Basics
6 |
7 | Before proceeding with using ISOFT, you need to initialise the logging
8 | tool which it uses, provided by
9 | [flogging](https://cmacmackin.github.io/flogging/). This allows you to
10 | specify what level of information to output to the terminal and the
11 | log file. This is done as follows:
12 | ```fortran
13 | use logger_mod, only: error, info, logger => master_logger, logger_init
14 | ! ...
15 | call logger_init('isoft.log', error, info, info)
16 | logger%trivia('isoft', 'Logger initialised successfully.')
17 | logger%info('isoft', 'Welcome to ISOFT.')
18 | ```
19 | The first line in this code fragment imports the module providing the
20 | [logger](https://cmacmackin.github.io/flogging/type/logger.html)
21 | object. The parameters `error` and `info` specify different priorities
22 | of messages which can be sent to the logger. The `master_logger` is a
23 | global logging object which is used within the ISOFT library. It is
24 | initialised using the
25 | [logger_init](https://cmacmackin.github.io/flogging/proc/logger_init.html)
26 | routine, which specifies the name of the log file and what priorities
27 | of messages get printed to the terminal and the log file. Methods of the
28 | `logger` object can be used to write out messages of different priority
29 | levels: "debug", "trivia", "info", "warning", "error", and "fatal".
30 |
31 | It is also necessary to initialise the HDF5 library, in order to allow I/O
32 | to be performed. This is done by running
33 | ```fortran
34 | use hdf5
35 | ! ...
36 | integer :: hdf_err
37 | ! ...
38 | call h5open_f(hdf_err)
39 | if (hdf_err < 0) error stop
40 | ```
41 | Similarly, at the end of your simulation you should deactivate HDF5 by
42 | running
43 | ```fortran
44 | call h5close_f(hdf_err)
45 | if (hdf_err < 0) error stop
46 | ```
47 |
48 | To run simulations with ISOFT, you must first build a
49 | [[cryosphere(type)]] object using the [[cryosphere(type):initialise]]
50 | method. This takes, as arguments, allocatable polymorphic objects of class
51 | [[glacier(type)]] and [[basal_surface(type)]]. These objects must be
52 | initialised with concrete types such as [[ice_shelf(type)]] and
53 | [[plume(type)]] (likely to be the most frequently used implementations).
54 |
55 | [[Glacier(type)]] and [[basal_surface(type)]] objects will typically
56 | feature `initialise` methods. However, such methods are unique to each
57 | sub-type. Thus, the concrete type of the [[glacier(type)]] or
58 | [[basal_surface(type)]] objects must be known when they are being
59 | initialised The simplest way to do this is to work with allocatable
60 | variables with those concrete types and then use the intrinsic
61 | `move_alloc` routine to transfer the object a polymorphic variable:
62 | ```fortran
63 | type(ice_shelf), allocatable :: shelf_obj
64 | class(glacier), allocatable :: glacier_obj
65 |
66 | allocate(shelf_obj)
67 | call shelf_obj%initialise(...)
68 | call move_alloc(shelf_obj, glacier_obj)
69 | ```
70 |
71 | The initialisation methods allow you to choose various parameter
72 | values, specify initial conditions, set boundary conditions, and
73 | select which parameterisations to use in the simulation. A number of
74 | additional objects must be created to accomplish the latter two. As
75 | before, the arguments must be allocatable objects of polymorphic
76 | type. However, classes representing boundary conditions and
77 | parameterisations have constructor functions, meaning that it is not
78 | necessary to use the `move_alloc` intrinsic. Instead, these objects
79 | can be initialised as below:
80 | ```fortran
81 | type(abstract_viscosity), allocatable :: viscosity
82 | allocate(viscosity, source=newtonian_viscosity(1.0d0))
83 | ```
84 | which creates an [[abstract_viscosity(type)]] object using the
85 | [[newtonian_viscosity(proc)]] constructor.
86 |
87 | A full description of all the derived types which can be used to
88 | construct a [[cryosphere(type)]] object is beyond the scope of this
89 | section. An overview can be found in the
90 | [discussion of the code design](../3-codeDesign/1-representing.html)
91 | and by consulting the [API documentation](|url|/lists/types.html).
92 |
93 | After creating a [[cryosphere(type)]] object, you can optionally
94 | initialise it using the output from a previous simulation. This is
95 | done with the [[cryosphere(type):read_data]] method. The simulation is
96 | run by integrating forward in time using the
97 | [[cryosphere(type):integrate]] method. At any point during the
98 | simulation, the state of the cryosphere can be written to the disk as
99 | an HDF5 file using the [[cryosphere(type):write_data]] method. This
100 | output can be used to initialise future simulations or be analysed
101 | using the provided set of
102 | [plotting scripts](../4-plotting/index.html).
103 |
104 | ## An Example
105 |
106 | Below is an example of a simple ISOFT simulation, designed to run to a
107 | steady state. It is provided as `main.f90` in the top directory of ISOFT.
108 |
109 | ```fortran
110 | {!main.f90!}
111 | ```
112 |
--------------------------------------------------------------------------------
/doc_pages/1-running/3-compile.md:
--------------------------------------------------------------------------------
1 | Title: Linking to the Library
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | As [previously discussed](./1-build.html), by running
6 | ```
7 | make script
8 | ```
9 | you will produce a script which can be used to compile programs that
10 | use ISOFT. It will contain information on the location of all the
11 | library files needed for ISOFT to run.
12 |
13 | This script is called `compile.sh` and has the following call signature:
14 | ```
15 | ./compile.sh [ main-file-name [ executable-name ] ]
16 | ```
17 | where `main-file-name` is the program file to be compiled and
18 | `executable-name` is the name to give the resulting executable. These
19 | default to `main.f90` and `isoft`, respectively.
20 |
21 | The [example program](./2-writing.html) provided earlier can be
22 | compiled and run as follows:
23 | ```fortran
24 | ./compile.sh
25 | ./isoft
26 | ```
27 | The provided script assumes that your ISOFT-dependent code is all in a
28 | single source file. It is trivial to modify the script should further
29 | files need to be compiled and linked.
30 |
31 |
--------------------------------------------------------------------------------
/doc_pages/1-running/index.md:
--------------------------------------------------------------------------------
1 | Title: Using ISOFT
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ISOFT isn't so much a program or library in the traditional sense, as
6 | a suite of tools for simulating the evolution of an ice shelf. It
7 | provides:
8 |
9 | - a library of derived types to run these simulations
10 | - a sample program making use of this library
11 | - a compile script to link your programs with the necessary libraries
12 | - a set of python scripts for plotting the output of simulations
13 |
14 | This section will walk you through compiling the library and programs
15 | which use it. The plotting scripts are discussed in the section
16 | [Plotting ISOFT Output](../4-plotting/index.html).
17 |
--------------------------------------------------------------------------------
/doc_pages/2-numerics/2-spatial.md:
--------------------------------------------------------------------------------
1 | Title: Spatial Discretisation
2 | Author: Chris MacMackin
3 | Date: December 2018
4 |
5 | The shelf/plume simulation requires computing various derivatives, for
6 | which a pseudospectral method is used. An introduction to this
7 | technique is provided below; for a more thorough explanation, see
8 | [Trefethen (2000)](../6-bibliog.html#Trefethen2000). Spectral methods
9 | provide a fast and accurate way to numerically differentiate discrete
10 | data. While more computationally expensive than finite difference
11 | methods for the same number of grid points, spectral methods give
12 | exponential convergence and thus often require significantly fewer
13 | grid points to achieve the same level of accuracy. Numerical accuracy
14 | is of particular importance here, as the purpose of running
15 | simulations is to test the stability of an ice-shelf to potentially
16 | small perturbations. Spectral methdos are often used for problems with
17 | periodic boundary conditions, where a Fourier series, \(\setcounter{20} f(\theta) =
18 | \sum_k a_k e^{ik\theta}\), can be used to interpolate between grid
19 | points. If the grid points are evenly spaced then the coefficients
20 | \(a_k\) can easily be calculated with a discrete Fourier
21 | transform. Typically this would be done using the highly efficient
22 | fast Fourier transform (FFT) algorithm
23 | [(Cooley and Tukey, 1965)](../6-bibliog.html#Cooley1965), which
24 | requires \(O(N\log N)\) operations for N grid-points. The derivative
25 | is then \(f'(\theta) = \sum_k ika_k e^{ik\theta}\) and an inverse FFT
26 | can be used to convert the new coefficients \(ika_k\) to the values of
27 | \(f'\) at each grid point.
28 |
29 | However, the boundary conditions for the ice shelf and plume
30 | are not periodic. Instead, say there is an
31 | interpolant \(F(x)\) for data mapped onto \(-1 \le x \le 1\) using a
32 | linear coordinate rescaling. To apply a spectral
33 | method, it is necessary to map the interpolant to a function
34 | \(f(\theta)\), \(0 \le \theta < 2\pi\), where \(x = \cos\theta\). Regardless
35 | of the boundary conditions on \(F\), \(f\) will be periodic and even in
36 | \(\theta\) and can thus be differentiated as before. The results can
37 | then be mapped back onto the grid points in the \(x\)-domain. By
38 | choosing \(x\) grid points to be _Chebyshev collocation points_,
39 | defined below, the corresponding grid points in \(\theta\) will be
40 | equally spaced and an FFT can be used to find the Fourier
41 | coefficients. This is known as the _Chebyshev pseudospectral
42 | method_ [(Trefethen, 2000)](../6-bibliog.html#Trefethen2000). If \(N + 1\) Chebyshev collocation
43 | points are needed, their positions are given by
44 | \begin{equation}
45 | \label{eq:cheb-colloc}
46 | x_j = \cos(j\pi/N), \quad j = 0, \ldots,N.
47 | \end{equation}
48 | This approach provides uneven spacing of points in \(x\), with a
49 | clustering of resolution near the domain boundaries, and hence is also
50 | well suited to capture rapid variation near the grounding line.
51 |
52 | Following [Trefethen (2000)](../6-bibliog.html#Trefethen2000), the practical algorithm used to
53 | differentiate discrete data \(v_j = v(x_j)\), for
54 | \(0 \le j \le N\), corresponding to values at Chebyshev collocation
55 | nodes \(x_0=1,\ldots,x_N=-1\), is as follows:
56 |
57 | 1. Take a type-I discrete cosine transform of the data, to
58 | determine the Fourier coefficients
59 | $$\hat{v}_j = \frac{\pi}{N}\left[v_0 + v_{N}\cos(\pi j) +
60 | 2\sum^{N-1}_{k=1} v_{k}\cos\left(\frac{\pi
61 | jk}{N-1}\right)\right].$$
62 | 2. Let \(\hat{w}_{j} = -j\hat{v}_{j}\) for \(1\le j\le N-1\) and
63 | \(\hat{w}_N = 0\).
64 | 3. Take a type-I discrete sine transform of \(\hat{w}_j\) from \(j=1\)
65 | to \(j=N-1\), yielding
66 | $$w_j = \frac{1}{\pi}\sum^{N-1}_{k=1}\hat{w}_k\sin\left(\frac{\pi
67 | kj}{N}\right).$$
68 | 4. Compute
69 | $$ v_j' = \begin{cases}
70 | \frac{1}{2\pi}\left[\frac{N^2}{2}\hat{v}_N + \sum_{k=1}^{N-1}
71 | k^2\hat{v}_k \right], & j = 0 \\
72 | \frac{-w_j}{\sqrt{1-x_j^2}}, & 1 \le j \le N - 1 \\
73 | \frac{1}{2\pi}\left[\frac{{(-1)}^{N+1}N^2}{2}\hat{v}_N +
74 | \sum_{k=1}^{N-1} {(-1)}^{k+1}k^2\hat{v}_k \right], & j = N
75 | \end{cases}.
76 | $$
77 |
78 | Discrete sine and cosine transforms are variations of the discrete
79 | Fourier transform which take advantage of data being real and either
80 | even or odd. The FFTW3 package
81 | [(Frigo and Johnson, 2005)](../6-bibliog.html#Frigo2005) was used to
82 | compute these. A more rigorous and detailed explanation of the above
83 | methods for periodic and non-periodic functions is provided by
84 | [Trefethen (2000)](../6-bibliog.html#Trefethen2000) in chapters 3 and
85 | 8, respectively.
86 |
87 | If a domain other than \(-1 \le x \le 1\) is desired then the
88 | Collocation points can be scaled and offset as necessary, giving a
89 | coordinate system \(x^*_j\). The above differentiation algorithm is
90 | applied unchanged, except that the result is scaled by twice the
91 | inverse of the domain-width.
92 |
--------------------------------------------------------------------------------
/doc_pages/2-numerics/3-shelf-solver.md:
--------------------------------------------------------------------------------
1 | Title: Shelf Solver
2 | Author: Chris MacMackin
3 | Date: December 2018
4 |
5 | Simulating the evolution the ice shelf mass balance using
6 | [equation 1](./1-equations.html#mjx-eqn-eqice-height-nd) requires a time-stepping scheme. In
7 | order to allow numerical stability with large time steps, a
8 | semi-implicit method is used. This works by defining the residual
9 | operator \(
10 | \def\bm#1{{\boldsymbol{#1}}}
11 | \newcommand{\dx}{\mathcal{D}_x}
12 | \setcounter{21}
13 | \)
14 | \begin{equation}
15 | \label{eq:shelf-resid}
16 | \bm{f}(h_n) =
17 | \frac{h_n - h_{n-1}}{\Delta t} +
18 | \frac{\partial}{\partial x}(h_{n}u_n(h_n)) + \lambda m_{n-1}
19 | \end{equation}
20 | where the subscript \(n\) indicates the value at the time step being
21 | solved for, while subscript \(n - 1\) indicates the value at the
22 | previous time step. This is a semi-implicit scheme (rather than fully
23 | implicit) because melt rate \(m_{n-1}\) is used from the previous time
24 | step, rather using \(m_n\) from the current time step. In this equation,
25 | \(h_n\) and \(u_n\) represent vectors of thickness and velocity values at
26 | each grid point at time step \(n\), while \(h_{n-1}\) is a vector of
27 | thickness values at the previous time step and \(\partial/\partial x\)
28 | is evaluated using the [Chebyshev differentiation procedure](./2-spatial.html)
29 | previously described. This is a nonlinear system
30 | and can be solved using Newton's method, where the root is determined
31 | iteratively by solving the linear equation
32 | \begin{equation}
33 | \label{eq:newton}
34 | \bm{J}\delta\bm{s}^k_n = -\bm{f}(\bm{s}^k_n).
35 | \end{equation}
36 | Here, \(\bm{s}_n = h_n\) is the current value of the ice thickness, \(\bm{J}\) is the Jacobian of \(f\),
37 | and the new iterate \(\bm{s}^{k+1}_n = \bm{s}^{k}_n + \delta\bm{s}^k_n\).
38 |
39 | In order to avoid having to evaluate the Jacobian of this system, a
40 | Jacobian-free Newton-Krylov method
41 | [(Knoll and Keyes, 2004)](../bibliog.html#Knoll2004) is used. This
42 | solves the linear [equation 23](#mjx-eqn-eqnewton) iteratively via a Krylov
43 | method which only requires the product of the Jacobian with the
44 | iterate, and not the actual Jacobian itself. This product is
45 | approximated as a finite difference:
46 | \begin{equation}
47 | \label{eq:jac-fin-diff}
48 | \bm{J}\bm{v} \approx \frac{\bm{f}(\bm{s} + \epsilon\bm{v}) -
49 | \bm{f}(\bm{s})}{\epsilon}.
50 | \end{equation}
51 | The NITSOL implementation
52 | [(Pernice and Walker, 1998)](../6-bibliog.html#Pernice1998) of a Newton-Krylov
53 | solver was chosen, as it is very flexible and written in Fortran,
54 | which was the language other portions of the code were to be
55 | implemented with.
56 |
57 | The spectral discretisation used here corresponds to dense matrices,
58 | making [equation 23](#mjx-eqn-eqnewton) very poorly conditioned. As a
59 | result, the Krylov solvers in NITSOL were unable to converge on a
60 | solution without a preconditioner. Even with relatively-sparse
61 | matrices, preconditioners are often needed for iterative methods
62 | (e.g.: [Pernice and Walker, 1998](../6-bibliog.html#Pernice1998);
63 | [Knoll and Keyes, 2004](../6-bibliog.html#Knoll2004)). A right preconditioner,
64 | \(\bm{P}^{-1}\), is chosen so that the modified problem
65 | \((\bm{J}\bm{P}^{-1})\bm{q} = -\bm{f}(\bm{s})\) is well-conditioned and
66 | can be solved for \(\bm{q}\). It is then easy to find \(\bm{s}\) using
67 | \(\bm{P}\delta\bm{s} = \bm{q} \Rightarrow \delta\bm{s} =
68 | \bm{P}^{-1}\bm{q}\). A good preconditioner will have
69 | \(\bm{P}^{-1} \approx \bm{J}^{-1}\), so that
70 | \(\bm{J}\bm{P}^{-1} \approx \bm{I}\) to a decent approximation. A
71 | tradeoff must be made between a preconditioner which is a sufficiently
72 | good approximation of \(\bm{J}^{-1}\) to be useful and one which is not
73 | too expensive or unstable to apply (e.g. \(P^{-1} = J^{-1}\) would be a
74 | perfect preconditioner but constructing it is of equal difficulty to
75 | solving the original, unpreconditioned problem).
76 |
77 | The Jacobian of [equation 1](#mjx-eqn-eqshelf-resid) can be expressed as
78 | \begin{equation}
79 | \label{eq:shelf-jacob}
80 | \bm{J} = \frac{1}{\Delta t} + \dx u,
81 | \end{equation}
82 | where we define \(\dx A \equiv \partial A/\partial x + A\Delta_x\), and
83 | \(\Delta_x\) is the differential operator in the \(x\)-direction. Although
84 | \(\Delta_x\) will be a dense matrix when using a spectral method, it can
85 | be approximated as a sparse finite difference operator, as proposed
86 | by [Orszag (1980)](../6-bibliog.html#Orszag1980). In this case
87 | \(\Delta_x\), and thus also \(\dx\),
88 | are tridiagonal matrices. This means that the finite difference form
89 | of the Jacobian can be "inverted" simply by solving the tridiagonal
90 | system, which can be done efficiently using a routine in LAPACK
91 | [(Anderson et al., 1999)](../6-bibliog.html#Anderson1999).
92 | This proved effective at preconditioning the
93 | Krylov solver in NITSOL, whilst maintaining the accuracy of the
94 | underlying pseudospectral method.
95 |
96 | The \(u_n(h_n)\) term in [equation 1](#mjx-eqn-eqshelf-resid) can itself be
97 | found by solving a nonlinear system, this time with the form
98 | \begin{equation}
99 | \label{eq:u-resid}
100 | \bm{f}(u_n) =
101 | \frac{\partial}{\partial x}\left(4\eta_{n}h_{n}\frac{\partial
102 | u_n}{\partial x}\right) - \chi \frac{\partial h_n^2}{\partial x}.
103 | \end{equation}
104 | This has a Jacobian \begin{equation}
105 | \label{eq:u-jacob}
106 | \bm{J} = \dx(4\eta h)\Delta_x.
107 | \end{equation}
108 | Every time a new residual is calculated using
109 | [equation 22](#mjx-eqn-eqshelf-resid),
110 | [equation 26](#mjx-eqn-equ-resid) is solved iteratively using NITSOL.
111 |
112 | It is also possible to construct a nonlinear system which takes both
113 | \(h_n\) and \(u_n\) as arguments and solve for both simultaneously. While
114 | this avoids the need to repeatedly solve for \(u_n\), it proved to be much
115 | less stable and tended to require smaller time-steps in order to
116 | prevent failure. As such, the approach outlined above proved to be
117 | computationally cheaper overall.
118 |
119 | Of the three linear solvers provided with NITSOL, only GMRES proved
120 | reliable, with the BiCGSTAB and TFQMR solvers typically
121 | failing. Consult [Pernice and Walker (1998)](../6-bibliog.html#Pernice1998) for more details on these
122 | routines. It was also found that the "backtracking" globalisation
123 | used in NITSOL, which is meant to prevent the solution from starting
124 | to diverge, had a tendency to make the solver get trapped in local
125 | minima for this problem. As such, it was turned off and this was found
126 | to greatly improve the robustness of the nonlinear solver.
127 |
--------------------------------------------------------------------------------
/doc_pages/2-numerics/index.md:
--------------------------------------------------------------------------------
1 | Title: Numerical Methods
2 | Author: Chris MacMackin
3 | Date: December 2018
4 |
5 | ISOFT simulates the evolution of ice shelves and meltwater plumes
6 | beneath them. A cartoon diagram of such a can be found below. The ice
7 | flow has a vertically integrated velocity \(\vec{u}\), with longitudinal and
8 | transverse components \(u\) and \(v\), respectively. \(h\) is the thickness of the
9 | ice shelf, while \(b\) is the depth of its lower surface below sea
10 | level. Subglacial discharge at the grounding line, with volume flux \(Q_g\), feeds a plume of thickness \(D\) flowing underneath the ice shelf with
11 | vertically integrated velocity \(\vec{U}\). This velocity also has
12 | longitudinal, \(U\), and transverse, \(V\), components. The plume has a
13 | temperature, \(T\), and salinity, \(S\), which drive melting \(m\) at the base of
14 | the ice shelf. The plume is further fed by turbulent entrainment, \(e\),
15 | of the ambient ocean water. This water has its own temperature and
16 | salinity: \(T_a\) and \(S_a\), respectively.
17 |
18 | 
19 |
20 | This section of the documentation describes the mathematics behind
21 | ISOFT. First, the equations describing ice shelf and plume behaviour
22 | are provided. The solvers used for the shelf and plume components are
23 | described in turn. Finally, an overview of a benchmarking problem is
24 | provided.
25 |
--------------------------------------------------------------------------------
/doc_pages/3-codeDesign/1-representing.md:
--------------------------------------------------------------------------------
1 | Title: Representing the Coupled Ice Shelf/Plume
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ISOFT uses a large number of *derived types* (equivalent to *classes*
6 | in other object oriented languages) in order to model the full glacial
7 | system. The system as a whole is contained within a [[cryosphere(type]] type,
8 | with methods for saving/loading data to/from the disk as HDF5 files
9 | and for integrating forward in time. The [[cryosphere(type)]] contains
10 | objects of abstract classes [[basal_surface(type)]] and [[glacier(type)]], the latter
11 | representing a glacier and the former representing whatever is
12 | underneath it. Objects of these types have their own methods for
13 | reading and writing data, integration, and accessing useful
14 | information. Both are general enough to allow ISOFT to model either
15 | floating or grounded ice. Below are class diagrams showing the
16 | inheritance/encapsulation relationships between different derived
17 | types, as well as methods for the [[cryosphere(type)]], [[glacier(type)]], and
18 | [[basal_surface(type)]] types.
19 |
20 | 
23 |
24 | 
25 |
26 | 
27 |
28 | 
29 |
30 | The only concrete existing implementation of the [[glacier(type)]] class is
31 | the [[ice_shelf(type)]] type. As its name suggests, this models the behaviour
32 | of an ice shelf. While the implementation of the continuity equation
33 | is agnostic towards whether the model is 1-D or 2-D, at present the
34 | ice momentum equation is explicitly 1-D. Ideally this will be fixed in
35 | the future. The [[ice_shelf(type)]] type may optionally feature a Lagrangian
36 | tracer, assumed to indicate the age of the ice as would be measured
37 | from internal reflectors. There is stub for a grounded [[ice_sheet(type)]]
38 | type, but its methods have not been implemented.
39 |
40 | A few implementations of the [[basal_surface(type)]] class are available. The
41 | most commonly used of these is the [[plume(type)]] type, modelling the 1-D
42 | subglacial plume. In principle this can model a second
43 | velocity component, but such a model is physically unstable. There is
44 | also the [[static_plume(type)]] type, which does not evolve from the state with
45 | which it is initialised or has loaded from saved data. This is useful if
46 | a simulation is to be performed with a fixed melt rate. The
47 | [[asym_plume(type)]] provides an implementation of a
48 | horizontally-integrated model. Various parameters describing the
49 | transverse profile of this plume are provided through the associated
50 | [[plume_shape(type)]] derived type. Finally, a [[ground(type)]] type exists as a stub,
51 | which could be fully implemented in future to provide a basal surface
52 | with frictional information for a grounded ice sheet.
53 |
54 | 
57 |
58 | All of these implementations contain *field types* (described in the
59 | [next section](./discretisation.html)) for each variable, describing
60 | their state. They also contain objects representing the boundary
61 | conditions and choices of parameterisations, described in more detail
62 | in the
63 | [Parameterisations and Boundary Conditions](./parameterisations.md)
64 | section. This is illustrated in the preceding figure, showing the
65 | state of the objects at the beginning of a representative
66 | simulation. The [[cryosphere(type)]] class is a Puppeteer pattern which, as
68 | described by
69 | [Rouson, Xia, and Xu (2014)](../6-bibliog.html#Rouson2014),
70 | coordinates interactions between various other classes
71 | ([[glacier(type)]] and [[basal_surface(type)]], in this case) without
72 | them needing to directly interact with each other. Thus,
73 | interdependencies between different modules of code are
74 | simplified. For each time-step, the following sequence of steps
75 | occurs, as illustrated in the UML sequence diagram which follows:
76 |
77 | 1. The [[cryosphere(type)]] first gets the ice thickness from the [[glacier(type)]].
78 |
79 | 2. This information is sent to the [[basal_surface(type)]] object, with which
80 | it can solve for its state at the current time using the QLM solver.
81 |
82 | 3. The [[cryosphere(type)]] gets the current melt rate and/or friction
83 | parameters from the [[basal_surface(type)]].
84 |
85 | 4. This information is sent to the [[glacier(type)]] object, where it is used
86 | to integrate its state forward in time with NITSOL.
87 |
88 |
89 | 
92 |
--------------------------------------------------------------------------------
/doc_pages/3-codeDesign/3-solvers.md:
--------------------------------------------------------------------------------
1 | Title: Nonlinear Solvers
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ## Ice Shelf Solver: NITSOL
6 |
7 | As [described earlier](../2-numerics/2-shelf-solver.html), when
8 | integrating the ice shelf the nonlinear solver NITSOL
9 | [(Pernice and Walker, 1998)](../6-bibliog.html#Pernice1998) was
10 | used. This is a legacy package written in FORTRAN77. ISOFT contains an
11 | explicit interface to the main NITSOL subroutine so that arguments can
12 | be checked by the compiler when it is invoked. NITSOL takes as an
13 | argument a subroutine which receives the current estimate of the state
14 | of the system and returns the corresponding residual according to
15 | [equation 22](../2-numerics/3-shelf-solver.html#mjx-eqn-eqshelf-resid)
16 | or [26](../2-numerics/3-shelf-solver.html#mjx-eqn-equ-resid), depending
17 | on whether solving for the ice
18 | thickness or velocity. The state of the system and the residual are
19 | both represented as 1-D arrays of real values. When the state array is
20 | received by the residual subroutine it is used to update the value of
21 | a field type (see [the previous page](./2-discretisation.html)).
22 | Operations are performed using the field type to calculate a
23 | residual field. A 1-D array containing the data of the residual field
24 | is then extracted and returned by the subroutine.
25 |
26 | For NITSOL to converge it required a preconditioner which inverts the
27 | Jacobian operator \(\mathcal{D}_x A \equiv \partial A/\partial x +
28 | A\Delta_x\) (where \(\Delta_x\) is the differential operator in the
29 | \(x\)-direction). NITSOL receives the preconditioner as an additional
30 | subroutine which takes as an argument an array to be preconditioned
31 | and returns the result of that preconditioning as an array. Similarly
32 | to the calculation of the residual, the preconditioner subroutine
33 | converts the array to a field type, performs the preconditioning, and
34 | then converts back to an array. As
35 | [previously described](../2-numerics/2-shelf-solver.html), the operator
36 | can be inverted by solving a tridiagonal matrix approximating the
37 | Jacobian using finite-difference discretisation. A derived type called
38 | a [[jacobian_block(type)]] (see below) was written to encapsulate this
39 | process, reinitialised every time a new value of \(A\) is needed by the
40 | operator. This derived type is also able to represent two variations
41 | on the \(\mathcal{D}_x\) operator: \(\alpha + \mathcal{D}_x X\), where
42 | \(\alpha\) is a scalar; and \(\mathcal{D}_x A\Delta_x\). In addition
43 | to inverting the operator on a field, [[jacobian_block(type)]] objects can
44 | apply the forward operator to fields.
45 |
46 | 
47 |
48 | 
49 |
50 | 
51 |
52 |
53 | ## Plume Solver: QLM
54 |
55 | The plume is solved using the
56 | [quasi-linearisation method](../2-numerics/4-plume-solver.html) (QLM).
57 | As the QLM is an obscure algorithm, a custom implementation was
58 | written in modern Fortran for ISOFT. It takes as arguments functions
59 | representing the linear and nonlinear portions of the nonlinear system
60 | of ODEs being solved. It also requires a function which computes the
61 | product of the Jacobian of the nonlinear operator with an arbitrary
62 | state vector and, optionally, a preconditioner function. All of these
63 | operate on and return arrays of real data. The QLM requires solving a
64 | linear system at each iteration and this is done using a slightly
65 | modified version of the GMRES implementation in NITSOL. The
66 | modification allows the GMRES solver to use an initial guess of the
67 | solution to the linear system, rather than assume a good initial guess
68 | to be zero (as made sense in the context of NITSOL). An explicit
69 | interface was written to this FORTRAN77 implementation, along with a
70 | wrapper which makes many of the arguments optional, automatically
71 | creates the necessary work arrays, and allows for less verbose
72 | definitions of the linear system.
73 |
74 | Much as when solving for the state of the ice shelf, the linear and
75 | nonlinear plume operators take 1-D arrays of real values as arguments.
76 | They then use a method of the [[plume(type)]] class to update the various fields
77 | representing the plume variables from this array. The necessary
78 | mathematics is performed using these fields and the results converted
79 | back to 1-D arrays which are then returned to the QLM solver. The
80 | preconditioner works by inverting the linear operator of the plume,
81 | taking the anti-derivative of each variable. A derived type called the
82 | [[pseudospec_block(type)]] (see figure above) was written to apply
83 | this to field types, reversing the Chebyshev differentiation algorithm
84 | described on page . A similar derived type called [[fin_diff_block(type)]]
85 | was also written which performs the same
86 | operation using a tridiagonal matrix representing a finite-difference
87 | approach to differentiation. However, the much greater accuracy and
88 | comparable computational cost of the [[pseudospec_block(type)]] made it the
89 | better choice.
90 |
91 | As previously mentioned, it was found that to get the level of
92 | accuracy needed for the plume solver to converge, the product of the
93 | Jacobian could not simply be evaluated using a finite difference
94 | approximation. Instead, the automatic differentiation feature of the
95 | field types described [on the previous page](./2-discretisation.html)
96 | was used. The vector to be multiplied by the Jacobian is used to
97 | provide derivative values for the plume variables. The nonlinear
98 | operator is then applied for the current plume state, with the
99 | overloaded operators of the field types applying the chain rule at
100 | each step to propagate the derivative. The derivative of the operator
101 | result will then be the product of the Jacobian and the initial
102 | vector.
103 |
--------------------------------------------------------------------------------
/doc_pages/3-codeDesign/4-parameterisations.md:
--------------------------------------------------------------------------------
1 | Title: Parameterisations and Boundary Conditions
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | One of the goals of ISOFT is to allow choices of parameterisations to
6 | easily be changed. This is achieved using the Strategy pattern
8 | [(Rouson, Xia, and Xu, 2014, Chapter 7)](../6-bibliog.html#Rouson2014),
9 | which provides a common abstract interface to accomplish some task,
10 | with subtypes implementing different strategies to do so. In ISOFT,
11 | the methods in the abstract types were generally given a large number
12 | of arguments, to ensure sufficient information is available for all
13 | potential parameterisations. Parameter and coefficient values can be
14 | specified for each parameterisation when initialising its object.
15 |
16 | The only parameterisation for the ice shelf is viscosity. The general
17 | interface is provided by the [[abstract_viscosity(type)]] type. It’s subtypes
18 | are [[newtonian_viscosity(type)]], which returns a `uniform_field` all
19 | cases, and [[glens_law_viscosity(type)]] which calculates the viscosity from
20 | the ice velocity as described in [equation 5](../2-numerics/1-equations.html#mjx-eqn-eqglens-nd). Currently Glen’s law is
21 | only implemented for the 1-D case, as attempting to implement it for
22 | higher dimensions resulted in a compiler bug. A class diagram is provided below.
23 |
24 | 
27 |
28 | The plume contains a few parameterisations. The subtypes of
29 | [[abstract_entrainment(type)]] calculate an entrainment rate for the
30 | plume. These are [[jenkins1991_entrainment(type)]] and
31 | [[kochergin1987_entrainment(type)]], implementing
32 | [equations 14](../2-numerics/1-equations.html#mjx-eqn-eqentrainment-jenkins-nd)
33 | and
34 | [15](../2-numerics/1-equations.html#mjx-eqn-eqentrainment-koch-nd),
35 | respectively. The [[abstract_melt_relationship(type)]] provides an
36 | interface for calculating the melt rate of the ice, in addition to the
37 | heat and salt fluxes resulting from melting. The one equation
38 | approximation of
39 | [equation 17](../2-numerics/1-equations.html#mjx-eqn-eqmelt-nondim)
40 | was implemented as [[one_equation_melt(type)]]. A variation of this
41 | was implemented as [[ave_one_equation_melt(type)]], implementing the
42 | horizontally-averaged version of the one equation formulation found in
43 | [equations 48 and 49](../2-numerics/6-horzint.html#mjx-eqn-eqhorz-melt).
44 | The subtype [[dallaston2015_melt(type)]] provides a way
45 | to convert from the scaling choices used by
46 | [Dallaston, Hewitt, and Wells (2015)](../bibliog.html#Dallaston2015)
47 | to those used in ISOFT, which was useful for writing unit
48 | tests. Finally, the abstract type [[equation_of_state(type)]] sets out
49 | the interface for calculating the density of water from salinity and
50 | temperature. Subtype [[linear_eos(type)]] implements the linearised
51 | equation of state in
52 | [equation 20](../2-numerics/1-equations.html#mjx-eqn-eqlin-eos). The
53 | related [[ave_linear_eos(type)]] provides additional methods methods for
54 | calculating \(\overline{\rho}\) and \(\tilde{\rho}\), as defined in
55 | [equations 46 and 47](6-horzint.html#mjx-eqn-eqrho-bar), respectively.
56 | Last, the subtype [[prescribed_eos(type)]]
57 | calculates the density assuming no dependence on temperature and using
58 | a prescribed salinity profile; this is also useful in unit
59 | tests. Class diagrams for each set of derived types are provided
60 | below.
61 |
62 | 
65 |
66 | 
69 |
70 | 
73 |
74 | A similar approach was taken for boundary conditions and ambient ocean
75 | properties. The types [[glacier_boundary(type)]] and
76 | [[plume_boundary(type)]] (class diagrams below) provide interfaces for
77 | identifying the types of boundary conditions at different locations
78 | and determining the appropriate values. The default implementations
79 | effectively do not specify boundary conditions and the methods must be
80 | overridden to be useful. The interface provided by
81 | [[plume_boundary(type)]] is quite different from that provided by
82 | [[glacier_boundary(type)]]. The latter should ideally be refactored to
83 | be closer to the more usable interface provided by the former. The
84 | subtypes for [[glacier_boundary(type)]] are
85 | [[dallaston2015_glacier_boundary(type)]], which prescribes a
86 | time-independent ice thickness and velocity at the grounding line and
87 | a balance between normal stress and hydrostatic pressure at the
88 | calving front, and [[seasonal_glacier_boundary(type)]], which modifies
89 | introduces sinusoidal or square-wave variation to the grounding line
90 | velocity.
91 |
92 | 
95 |
96 | 
99 |
100 | The first subtype of [[plume_boundary(type)]] is
101 | [[simple_plume_boundary(type)]], which implements Dirichlet boundary
102 | conditions at the grounding line and Neumann conditions (zero
103 | gradient) for velocity, salinity, and temperature at the calving
104 | front. Closely related to this type is
105 | [[dallaston2015_seasonal_boundary(type)]], which modifies the boundary
106 | conditions by sinusoidally perturbing the plume thickness and velocity
107 | at the grounding line. The type which was ultimately used in all
108 | simulations was [[upstream_plume_boundary(type)]]. This takes a
109 | user-provided function which specifies the inflow value of each plume
110 | variable and then, assuming no diffusion, integrates the plume a small
111 | distance upstream along the current basal draft of the ice shelf using
112 | [[rksuite_90]]
113 | [(Brankin and Gladwell, 1994)](../6-bibliog.html#Brankin1994). This
114 | allows the plume solver itself to avoid handling narrow boundary
115 | layers where the plume salinity and temperature change
116 | rapidly. Outflow conditions remain unchanged. Ambient ocean conditions
117 | are described according to the interface defined by the abstract type
118 | [[ambient_conditions(type)]]. As shown in the class diagram below, at
119 | present only one implementation is provided
120 | ([[uniform_ambient_conditions(type)]]), specifying constant ambient
121 | salinity and temperature.
122 |
123 | 
126 |
--------------------------------------------------------------------------------
/doc_pages/3-codeDesign/index.md:
--------------------------------------------------------------------------------
1 | Title: Code Design
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | This section of the documentation provides an overview of the
6 | structure of ISOFT and an explanation of the design choices made. It
7 | is limited to a discussion of code architecture, with numerical
8 | methods already having been described in
9 | [the previous section](../2-numerics/). A number of design patterns
10 | were consciously used when developing ISOFT. The names of these are
11 | indicated in the text by small-caps. An understanding of object
12 | oriented programming techniques in general and object oriented
13 | features in Fortran, in particular, will be useful when reading these
14 | notes; see, for example,
15 | [Rouson, Xia, and Xu (2014)](../6-bibliog.html#Rouson2014). Familiarity
16 | with Universal Modelling Language (UML) diagrams will also be helpful
17 | [(Rouson, Xia, and Xu, 2014)](../6-bibliog.html#Rouson2014). Note that,
18 | in the UML diagrams in this section, names of derived types are shown
19 | in camel case, as is the convention for class names in most object
20 | oriented languages. However, as Fortran is case-insensitive, the
21 | decision was made to use underscore separation within the code.
22 |
--------------------------------------------------------------------------------
/doc_pages/4-plotting/1-readers.md:
--------------------------------------------------------------------------------
1 | Title: Reader Objects
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | The most basic part of the plotting library is the classes it provides
6 | for reading the HDF5 files which ISOFT produces. These can be found in
7 | the plotting/[[readers.py]] file. The class which you will mostly use
8 | is `ShelfPlumeCryosphere`, which loads files from simulations run with
9 | the [[ice_shelf(type)]] and [[plume(type)]] (or [[static_plume(type)]]
10 | or [[asym_plume(type)]]) derived types.
11 | Readers of this type are created using the constructor
12 | ```python
13 | ShelfPlumeCryosphere(hdf_file)
14 | ```
15 | where `hdf_file` is the name of an HDF5 file created by ISOFT.
16 |
17 | This reader object a number of properties which provide information on
18 | the state of the simulation. Arrays containing simulation variables
19 | may, in principle, be multidimensional. However, as code currently
20 | exists only for 1-D simulations, they are all 1-D in practice (except
21 | `uvec` and `Uvec`, which are 2-D).
22 |
23 | - __isoft_version__: Version number of ISOFT which generated the HDF5 file
24 | - __compilation_time__: Time and date at which the ISOFT library used
25 | to generate the HDF5 file was compiled
26 | - __output_time__: Wall-clock time the HDF5 file was created
27 | - __time__: Time within the simulation when the HDF5 file was created
28 | - __grid__: The coordinates of the grid discretising the domain
29 | - __glacier_type__: The type of [[glacier(type)]] object which created this data
30 | - __h__: Array containing thickness of the glacier's ice
31 | - __uvec__: Array containing glacier ice velocity in vector form, with
32 | `u = uvec[...,0]` and `v = uvec[...,1]` (if transverse present)
33 | - __u__: Array containing longitudinal glacier velocity
34 | - __v__: Array containing transverse glacier velocity; will raise an error if not present
35 | - __s__: The surface elevation of the glacier, `h*(1.-1./r)`
36 | - __b__: The basal depth of the glacier, `-h/r`
37 | - __kappa__: Array containingTaylor coefficients in _z_ for a Lagrangian tracer field,
38 | with `kappa[i,...]` corresponding to the coefficient for the
39 | i-1th power term; raises an error if not present in
40 | simulation output
41 | - __lambd__: The dimensionless melt parameter, \(\lambda\)
42 | - __chi__: The dimensionless stretching parameter, \(\chi\)
43 | - __basal_surface_type__: The type of [[basal_surface(type)]] object to create this data
44 | - __D__: Array containing the thickness of the subglacial plume
45 | - __Uvec__: Array containing plume ice velocity in vector form, with `U = Uvec[...,0]` and `V = Uvec[...,1]` (if transverse present)
46 | - __U__: Array containing longitudinal plume velocity
47 | - __V__: Array containing transverse plume velocity; will raise an error if not present
48 | - __S__: Array containing plume salinity
49 | - __T__: Array containing plume temperature
50 | - __delta__: The buoyancy correction parameter, \(\delta\)
51 | - __mu__: The dimensionless drag coefficient, \(\mu\)
52 | - __nu__: The dimensionless eddy diffusivity, \(\nu\)
53 | - __r__: The density ratio for ocean water and glacier ice, \(r\)
54 | - __phi__: The dimensionless Coriolis parameter, \(\Phi\)
55 |
56 | A simple example of using a reader object to make a plot is provided below:
57 | ```python
58 | from plotting.readers import ShelfPlumeCryosphere
59 | import matplotlib.pyplot as plt
60 |
61 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
62 | plt.plot(plt.grid, plt.h)
63 | plt.xlabel('$x$')
64 | plt.ylabel('$h$')
65 | plt.show()
66 | ```
67 |
--------------------------------------------------------------------------------
/doc_pages/4-plotting/3-tools.md:
--------------------------------------------------------------------------------
1 | Title: General Plotting Tools
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | For convenience, a number of additional tools are provided for
6 | analysing ISOFT output. These are organised into a series of
7 | additional Python modules.
8 |
9 | ## plotting/[[calculus.py]] {: #differentiate }
10 |
11 | Provides a `Differentiator` class which can be used to calculate
12 | derivatives of ISOFT output using the
13 | [Chebyshev pseudospectral](../2-numerics/2-spatial.html)
14 | method. Rather than the previously described algorithm using the FFT,
15 | a differentiation matrix is used here
16 | [(Trefethen, 2000)](../6-bibliog.html#Trefethen2000). The constructor
17 | for this object is
18 | ```python
19 | Differentiator(size, lower=0.0, upper=1.0)
20 | ```
21 | where `size` is the number of Chebyshev pseudo-spectral nodes in the
22 | data to be differentiated, `lower` is the lower bound of the domain,
23 | and `upper` is the upper bound. The differentiator object is applied
24 | by calling it like a function, with the array to be differentiated
25 | passed as an argument.
26 |
27 | ```python
28 | from plotting.readers import ShelfPlumeCryosphere
29 | from plotting.calculus import Differentiator
30 |
31 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
32 | diff = Differentiator(cryo.grid.size, cryo.grid[-1], cryo.grid[0])
33 | dh_dx = diff(cryo.h)
34 | ```
35 |
36 |
37 | ## plotting/[[dimensionless_nums.py]]
38 |
39 | This module provides routines to calculate dimensionless numbers used
40 | in fluid dynamics. To date, only the
41 | [Froude number](https://en.wikipedia.org/wiki/Froude_number) has been
42 | implemented. This has routine
43 | ```python
44 | froude(coef, U, drho, D)
45 | ```
46 | where `coef` is a dimensionless coefficient \(1/\sqrt{\delta}\), `U =
47 | ShelfPlumeCryosphere.Uvec` is the vector velocity, `drho` is a
48 | density difference causing buoyant forcing, and `D` is thickness of
49 | the fluid layer.
50 |
51 |
52 | ## plotting/[[entrainment.py]]
53 |
54 | This provides classes which can calculate the entrainment rate for a
55 | subglacial plume. Two such classes are provided: one for the
56 | parameterisation of [Jenkins (1991)](../6-bibliog.html#Jenkins1991)
57 | and another for that of
58 | [Kochergin (1987)](../6-bibliog.html#Kochergin1987). The constructor
59 | for the first has the form
60 | ```python
61 | Jenkins1991Entrainment(coefficient, size, lower=0.0, upper=1.0)
62 | ```
63 | where `coefficient` is the dimensionless entrainment coefficient
64 | \(E_0/\delta\) (typically with value 1) and the remaining arguments
65 | have the same meaning as those in the constructor for the
66 | [Differentiator type](#differentiate). The constructor for the latter
67 | has the form
68 | ```python
69 | Kochergin1987Entrainment(coefficient, delta)
70 | ```
71 | where `coefficient` is \(K = c_L^2 x_0/D_0\), as described in
72 | [equation 16](../2-numerics/1-equations.html#mjx-eqn-eqent-koch-coef-nd),
73 | and `delta` is the buoyancy correction \(\delta\).
74 |
75 | For both of these classes, the entrainment is calculated by calling
76 | the object like a function. For an entrainment object named `ent`, the
77 | call signature is
78 | ```python
79 | ent(U, D, b, rho_diff)
80 | ```
81 | where `U` is the vector velocity of the plume, `D` is the plume
82 | thickness, `b` is the basal depth of the ice shelf, and `rho_diff` is
83 | the density difference between the ambient ocean and the plume. The
84 | last argument is not used and may be omitted for the
85 | `Jenkins1991Entrainment` class.
86 |
87 |
88 | ## plotting/[[eos.py]]
89 |
90 | This provides a class, `LinearEos`, for calculating the plume density
91 | according to the linearisation in
92 | [equation 20](../2-numerics/1-equations.html#mjx-eqn-eqlin-eos). The
93 | constructor for this class
94 | ```python
95 | LinearEos(ref_density, beta_T, beta_S, T_ref, S_ref)
96 | ```
97 | where the arguments are the reference density, thermal contraction
98 | coefficient, haline contraction coefficient, reference temperature,
99 | and reference salinity. All quantities should be scaled to be in
100 | nondimensional units. The resulting object (called, say, `eos`) is
101 | called like a function:
102 | ```python
103 | eos(T, S)
104 | ```
105 | where `T` is the plume temperature and `S` is the plume salinity.
106 |
107 |
108 | ## plotting/[[melt.py]]
109 |
110 | This module provides classes which can calculate the melt rate of the
111 | ice shelf and melt contributions to the plume's salinity and heat
112 | equations. The first class is `Dallaston2015Melt` which calculates the
113 | melt rate in the same manner as the [[dallaston2015_melt(type)]]
114 | derived type. It has constructor
115 | ```python
116 | Dallaston2015Melt(beta, epsilon_g, epsilon_m)
117 | ```
118 | where `beta` is the inverse Stefan number, `epsilon_g` is the ratio of
119 | subglacial flux to entrained flux, and `epsilon_g` is the ratio of
120 | subshelf melt and entrained flux. The other class is `OneEquationMelt`,
121 | which calculates the melt rate in the manner of the
122 | [[one_equation_melt(type)]] derived type. It has constructor
123 | ```python
124 | OneEquationMelt(coef1, coef2, fresh_sal=0., melt_temp=0.)
125 | ```
126 | where `coef1` and `coef2` correspond to \(\zeta_1\) and \(\zeta_2\) in
127 | [equation 18](../2-numerics/1-equations.html#mjx-eqn-eqtherm-trans-nondim),
128 | `fresh_sal` is the salinity value which is designated as "fresh", and
129 | `melt_temp` is the temperature at which melting occurs (scale analysis
130 | shows that it often makes sense for these to be non-zero).
131 |
132 | For both classes, the melt rate is calculated by calling the melt
133 | object (here named `m`) like a function. Additionally, there are
134 | methods `thermal_forcing` and `saline_forcing` to calculate the
135 | contribution of melting to the plume heat and salinity equations,
136 | respectively. These can be called as follows:
137 | ```python
138 | m(U, p, T, S, D)
139 | m.thermal_forcing(U, p, T, S, D)
140 | m.saline_forcing(U, p, T, S, D)
141 | ```
142 | In each case, `U` refers to the plume vector velocity, `p` to the
143 | pressure at the base of the ice shelf, `T` to the plume temperature,
144 | `S` to the plume salinity, and `D` to the plume thickness.
145 |
146 |
147 | ## plotting/[[viscosity.py]]
148 |
149 | Finally, this module provides two classes which can calculate the
150 | viscosity of the glacier: `NewtonianViscosity` and `GlensLaw`. The
151 | first of these is a trivial implementation with a constructor
152 | ```python
153 | NewtonianViscosity(value=1.0)
154 | ```
155 | Making calls to objects of this type will return an array filled with
156 | the value with which the viscosity object was initialised. The
157 | second implementation is `GlensLaw`, which treats viscosity as a
158 | power law of strain
159 | [(equation 5)](../2-numerics/1-equations.html#mjx-eqn-eqglens-nd). It
160 | has constructor
161 | ```python
162 | GlensLaw(size, lower=0.0, upper=1.0, coef=1.0, index=3)
163 | ```
164 | The first 3 arguments have the same meaning as in the constructor for
165 | the [Differentiator class](#differentiate). The argument `coef`
166 | corresponds to \(\xi\), as defined in
167 | [equation 6](../2-numerics/1-equations.html#mjx-eqn-eqglens-coef-nd)
168 | and `index` is the value of \(n\) in the equation for Glen's Law.
169 |
170 | Both viscosity classes are called like functions and (if an instance
171 | of them is named `visc`) have the call signature
172 | ```python
173 | visc(uvec, temperature=-15.0, time=0)
174 | ```
175 | where `uvec` is the vector ice velocity, `temperature` is the
176 | temperature of the ice, and `time` is the time during the simulation
177 | at which the calculation is being performed. The latter two arguments
178 | are not used in either implementation of viscosity.
179 |
180 |
--------------------------------------------------------------------------------
/doc_pages/4-plotting/5-layers.md:
--------------------------------------------------------------------------------
1 | Title: Plotting Internal Layers
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ISOFT has the ability to simulate the advection of a Lagrangian tracer
6 | distributed in the z-direction. The main use for this is to track the
7 | evolution of isochrones, which correspond to internal radar reflectors
8 | observed in ice sheets and shelves. ISOFT achieves this by expressing
9 | the tracer as a Taylor expansion in _z_ and then calculating changes
10 | to the Taylor coefficients. It is only the coefficients which are
11 | stored.
12 |
13 | Visualising this tracer data requires converting it to a 2-D form in
14 | _x_ and _z_. For this purpose, the plotting/[[layers.py]] module
15 | provides a routine called `compute_layers`. This has the call
16 | signature
17 | ```python
18 | x, z, k = compute_layers(shelf, vertical_resolution=300)
19 | ```
20 | where `x` and `z` are 2-D arrays containing the coordinates in _x_ and
21 | _z_ respectively, `k` is a 2-D array containing the values of the
22 | tracer. These are calculated from `shelf`, which is either a `Glacier`
23 | or a `ShelfPlumeCryosphere` object (see
24 | [Reader Objects](./readers.html)). The number of intervals in _z_ on
25 | which to calculate `k` is set by the argument `vertical_ resolution`.
26 |
27 | With this output, a contour plot can be produced, showing the presence
28 | of internal layers within the ice shelf. This is demonstrated in the
29 | script below, along with example output.
30 |
31 | ```python
32 | from plotting.readers import ShelfPlumeCryosphere
33 | from plotting.layers import compute_layers
34 |
35 | import numpy as np
36 | import numpy.ma as ma
37 | import matplotlib
38 | import matplotlib.pyplot as plt
39 |
40 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
41 |
42 | nz = 1200
43 | conts = 16
44 |
45 | x, z, k = compute_layers(cryo, nz)
46 |
47 | plt.plot(cryo.grid, cryo.s, 'k')
48 | plt.plot(cryo.grid, cryo.b, 'k')
49 | plt.contour(x, z, k, conts, linewidths=0.5, colors='k')
50 | plt.contourf(x, z, k, conts, cmap='PuBu')
51 | plt.xlabel('$x$')
52 | plt.ylabel('$z$')
53 | plt.colorbar(orientation='horizontal', label=r'$k$')
54 | plt.show()
55 | ```
56 |
57 | 
58 |
--------------------------------------------------------------------------------
/doc_pages/4-plotting/6-scripts.md:
--------------------------------------------------------------------------------
1 | Title: Provided Plotting Scripts
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | The Python library described in the previous sections allows you to
6 | produce plots with tools such as
7 | [matplotlib](https://matplotlib.org/). This can be useful when
8 | creating bespoke visualisations or undertaking detailed analysis of
9 | ISOFT output. However, there are many simple types of plots which you
10 | will want to create regularly. For these, a selection of programs have
11 | been provided in the directory `scripts/`. Where it is necessary to
12 | specify a scaling or parameterisation choice, the same ones are used
13 | as in the [example program](../1-running/2-writing.html). Unless
14 | otherwise indicated, all scripts have the following call-signature:
15 | ```
16 | ./script-name.py [ infile [ outfile ] ]
17 | ```
18 | Here, `infile` refers to the name of the HDF5 file the data in which
19 | is to be plotted. If not specified, it defaults to
20 | `isoft-0000.h5`. Conversely, `outfile` is the name of a file in which
21 | the plot should be saved, with the format taken from the file-name
22 | extension. If this is not specified then the script will produce the
23 | plot in an interactive graphical interface.
24 |
25 |
26 | ## scripts/[[decompose.py]]
27 |
28 | Displays a series of plots, one for each of the equations describing
29 | the 1-D plume
30 | ([equations 8, 9, 11, and 12](../2-numerics/1-equations.html#mjx-eqn-eqplume-cont)). In
31 | each figure, the magnitude of the individual terms in that equation
32 | are plotted against each other, alongside their sum (which should be
33 | 0). The script can not save plots to a file and only takes one
34 | argument. The next plot will be produced once you have closed the
35 | previous one.
36 |
37 |
38 | ## scripts/[[display.py]]
39 |
40 | Produces a plot of all plume and ice shelf variables. Salinity and
41 | temperature are shown on a seperate set of axes, as these values can
42 | be quite different from those of the other variables.
43 |
44 |
45 | ## scripts/[[ice_continuity.py]]
46 |
47 | Produces a plot showing the value of each term in the continuity
48 | equation for the ice shelf
49 | ([equation 1](../2-numerics/1-equations.html#mjx-eqn-eqice-height-nd)).
50 |
51 |
52 | ## scripts/[[internal_layers.py]]
53 |
54 | Produces a contour plot showing the value of a Lagrangian tracer
55 | (indicating, e.g., the age of ice). Similar to the plot shown on the
56 | [previous page](./layers.html).
57 |
58 |
59 | ## scripts/[[plume_momentum.py]]
60 |
61 | Produces a plot showing the value of each term in the transverse
62 | momentum equation for the plume
63 | ([equation 9](../2-numerics/1-equations.html#mjx-eqn-eqplume-mom-x)).
64 |
65 |
66 | ## scripts/[[shock.py]]
67 |
68 | A plot of the plume velocity, thickness, and Froude number. This can
69 | be useful for diagnosing the presence of a hydrolic shock.
70 |
--------------------------------------------------------------------------------
/doc_pages/4-plotting/index.md:
--------------------------------------------------------------------------------
1 | Title: Plotting ISOFT Output
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | ISOFT provides a small library of plotting scripts with which to
6 | analyse the HDF5 files which it outputs. This is divided into two parts:
7 |
8 | 1. A set of routines which read and process the HDF5 data.
9 | 2. A selection of executable scripts which actually produce plots.
10 |
11 | The scripts provide some basic functionality with which to visualise
12 | the output of ISOFT. However, you will likely want to write some
13 | scripts of your own to provide more complex, bespoke analysis.
14 |
--------------------------------------------------------------------------------
/doc_pages/6-bibliog.md:
--------------------------------------------------------------------------------
1 | Title: Bibliography
2 | Author: Chris MacMackin
3 | Date: December 2018
4 |
5 | Anderson, E. et al. (1999). _LAPACK Users’ Guide_. Third. Philadelphia, PA: Society for
6 | Industrial and Applied Mathematics.
7 | {: #Anderson1999 }
8 |
9 | Bindschadler, R., D.G. Vaughan, and P. Vornberger (2011). “Variability of basal melt
10 | beneath the Pine Island Glacier ice shelf, West Antarctica”. In: _Journal of Glaciology_
11 | 57.204, pp. 581–595. doi: 10.3189/002214311797409802.
12 | {: #Bindschadler2011 }
13 |
14 | Brankin, RW and I Gladwell (1994). _rksuite 90: software for ODE IVPs_. Tech. rep. NAG
15 | Tech. Report, TR6/94, 1994.
16 | {: #Brankin1994 }
17 |
18 | Brent, R.P. (1973). _Algorithms for Minimization Without Derivatives_. Prentice-Hall
19 | series in automatic computation. Prentice-Hall.
20 | {: #Brent1973 }
21 |
22 | Cooley, J.W. and J.W. Tukey (1965). “An algorithm for the machine calculation of
23 | complex fourier series”. In: Mathematics of Computation 19.90, pp. 249–259. doi:
24 | 10.1090/S0025-5718-1965-0178586-1.
25 | {: #Cooley1965 }
26 |
27 | Dallaston, M.C., I.J. Hewitt, and A.J. Wells (2015). “Channelization of plumes beneath
28 | ice shelves”. In: _Journal of Fluid Mechanics_ 785, pp. 109–134. doi:
29 | 10.1017/jfm.2015.609.
30 | {: #Dallaston2015 }
31 |
32 | Frigo, Matteo and Steven G. Johnson (2005). “The Design and Implementation of
33 | FFTW3”. In: _Proceedings of the IEEE_ 93.2. Special issue on “Program Generation,
34 | Optimization, and Platform Adaptation”, pp. 216–231.
35 | {: #Frigo2005 }
36 |
37 | Grand, Mark (2002). _Patterns in Java: A Catalog of Reusable Design Patterns Illustrated
38 | with UML, Volume 1_. 2nd ed. New York: Wiley.
39 | {: #Grand2002 }
40 |
41 | Jacobs, S.S., H.H. Hellmer, and A. Jenkins (1996). “Antarctic Ice Sheet melting in the
42 | southeast Pacific”. In: _Geophysical Research Letters_ 23.9, pp. 957–960. doi:
43 | 10.1029/96GL00723.
44 | {: #Jacobs1996 }
45 |
46 | Jenkins, A. (1991). “A one-dimensional model of ice shelf-ocean interaction”. English. In:
47 | _Journal of Geophysical Research_ 96 (C11), 20671–7.
48 | {: #Jenkins1991 }
49 |
50 | Jenkins, A. (2011). “Convection-driven melting near the grounding lines of ice shelves and
51 | tidewater glaciers”. In: _Journal of Physical Oceanography_ 41.12, pp. 2279–2294. doi:
52 | 10.1175/JPO-D-11-03.1.
53 | {: #Jenkins2011 }
54 |
55 | Knoll, D.A. and D.E. Keyes (2004). “Jacobian-free Newton-Krylov methods: A survey of
56 | approaches and applications”. In: _Journal of Computational Physics_ 193.2,
57 | pp. 357–397. doi: 10.1016/j.jcp.2003.08.010.
58 | {: #Knoll2004 }
59 |
60 | Kochergin, V. P. (1987). “Three-Dimensional Prognostic Models”. In: _Three-Dimensional
61 | Coastal Ocean Models_. American Geophysical Union, pp. 201–208. doi:
62 | 10.1029/CO004p0201.
63 | {: #Kochergin1987 }
64 |
65 | MacMackin, C., (2019). _[Understanding the Effect of Seasonal
66 | Variability on the Structure of Ice Shelves and Meltwater Plumes](|media|/MacMackin_thesis.pdf)_,
67 | DPhil in Atmospheric, Oceanic, and Planetary Physics, University of
68 | Oxford, Oxford, UK.
69 | {: #MacMackin2019 }
70 |
71 | Mandelzweig, V. B. and F. Tabakin (2001). “Quasilinearization approach to nonlinear
72 | problems in physics with application to nonlinear ODEs”. In: _Computer Physics
73 | Communications_ 141, pp. 268–281. doi: 10.1016/S0010-4655(01)00415-5.
74 | {: #Mandelzweig2001 }
75 |
76 | Neidinger, R. (2010). “Introduction to Automatic Differentiation and MATLAB
77 | Object-Oriented Programming”. In: _SIAM Review_ 52.3, pp. 545–563. doi:
78 | 10.1137/080743627.
79 | {: #Neidinger2010 }
80 |
81 | Orszag, Steven A (1980). “Spectral methods for problems in complex geometries”. In:
82 | _Journal of Computational Physics_ 37.1, pp. 70–92. doi:
83 | 10.1016/0021-9991(80)90005-4.
84 | {: #Orszag1980 }
85 |
86 | Pernice, M. and H.F. Walker (1998). “NITSOL: A Newton iterative solver for nonlinear
87 | systems”. In: _SIAM Journal on Scientific Computing_ 19.1, pp. 302–318.
88 | {: #Pernice1998 }
89 |
90 | Press, William H. et al. (2007). _Numerical Recipes 3rd Edition: The Art of Scientific
91 | Computing_. 3rd ed. New York, NY, USA: Cambridge University Press.
92 | {: #Press2007 }
93 |
94 | Rouson, D., J. Xia, and X. Xu (2014). _Scientific Software Design: The Object-Oriented
95 | Way_. Cambridge University Press.
96 | {: #Rouson2014 }
97 |
98 | Sergienko, O. V. (2013). “Basal channels on ice shelves”. In: _Journal of Geophysical
99 | Research -- Earth Surface_ 118.3, 1342–1355. doi: 10.1002/jgrf.20105.
100 | {: #Sergienko2013 }
101 |
102 | Trefethen, L. (2000). _Spectral Methods in MATLAB_. Society for Industrial and Applied
103 | Mathematics. doi: 10.1137/1.9780898719598. eprint: http://epubs.siam.org/doi/pdf/10.1137/1.9780898719598.
104 | {: #Trefethen2000 }
105 |
--------------------------------------------------------------------------------
/doc_pages/index.md:
--------------------------------------------------------------------------------
1 | Title: Documentation
2 | Author: Chris MacMackin
3 | Date: November 2018
4 |
5 | {!README.md!}
6 |
--------------------------------------------------------------------------------
/get_deps:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # get_deps
5 | #
6 | # Copyright 2016 Christopher MacMackin
7 | #
8 | # This program is free software; you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation; either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program; if not, write to the Free Software
20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 | # MA 02110-1301, USA.
22 | #
23 |
24 | # A script which prints the dependencies for a given Fortran source file in
25 | # a format suitable for inclusion into a Makefile.
26 | #
27 | # Invocation:
28 | #
29 | # ./get_deps FORTRAN_FILE MOD_DIRECTORY IGNORE [INCLUDE_DIR [INCLUDE_DIR ...]]
30 | #
31 | # FORTRAN_FILE
32 | # The path to the source file for which dependencies are to be
33 | # printed.
34 | #
35 | # MOD_DIRECTORY
36 | # The directory into which compiled module files will be placed.
37 | #
38 | # IGNORE
39 | # A regular expression for module names which should not be listed as
40 | # dependencies (i.e. because they are for external libraries or
41 | # intrinsic modules).
42 | #
43 | # INCLUDE_DIR
44 | # Directories which may contain an include file (when it is not
45 | # next to or below the file doing the including in the source
46 | # tree).
47 | #
48 |
49 |
50 | import re
51 | from sys import argv
52 | import os
53 |
54 | # Taken from my project FORD
55 | USE_RE = re.compile(r"""^[^!'"#]*use(?:\s*(?:,\s*((?:non_)?intrinsic)\s*)?::\s*|\s+)(\w+)\s*""",re.IGNORECASE)
56 | MODULE_RE = re.compile(r"""^[^!'"#]*(? 4:
66 | incdirs += argv[4:]
67 |
68 | def get_moddir(modname):
69 | return os.path.join(moddir, modname)
70 |
71 | modules = set()
72 | dependencies = set()
73 | includes = set()
74 |
75 | src = open(filename,'r')
76 | for line in src:
77 | if os.path.splitext(filename)[1] == '.f' and \
78 | (line[0] == 'c' or line[0] == '*' or line[0] == '!'):
79 | continue
80 | use = USE_RE.search(line)
81 | if use:
82 | if not use.group(1) == 'intrinsic' and not ignore_re.search(use.group(2)):
83 | dependencies.add(get_moddir(use.group(2).lower() + '.mod'))
84 | else:
85 | mod = MODULE_RE.search(line)
86 | if mod:
87 | modules.add(get_moddir(mod.group(1).lower() + '.mod'))
88 | modules.add(get_moddir(mod.group(1).lower() + '.smod'))
89 | else:
90 | submod = SUBMODULE_RE.search(line)
91 | if submod:
92 | modules.add(get_moddir(submod.group(1).lower() + '@'
93 | + submod.group(3).lower() + '.smod'))
94 | if submod.group(2) and not ignore_re.search(mod.group(1)):
95 | dependencies.add(get_moddir(submod.group(1).lower() + '@'
96 | + submod.group(2).lower()
97 | + '.smod'))
98 | elif not ignore_re.search(submod.group(1)):
99 | dependencies.add(get_moddir(submod.group(1).lower() + '.smod'))
100 | else:
101 | inc = INCLUDE_RE.search(line)
102 | if inc:
103 | ifile = inc.group(3)
104 | includes.update(tuple(os.path.join(idir,ifile) for idir in incdirs))
105 |
106 | src.close()
107 |
108 | basename = os.path.splitext(filename)[0]
109 | if len(modules) > 0:
110 | print(' '.join(modules) + ': ' + filename + ' ' + basename + '.o')
111 | if len(dependencies) + len(includes) > 0:
112 | print(basename + '.o: ' + ' '.join(dependencies) +
113 | ' $(wildcard ' + ' '.join(includes) + ')')
114 | # for dep in dependencies:
115 | # print(dep + ':')
116 |
117 |
118 |
--------------------------------------------------------------------------------
/media/MacMackin_thesis.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/MacMackin_thesis.pdf
--------------------------------------------------------------------------------
/media/ambient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/ambient.png
--------------------------------------------------------------------------------
/media/basal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/basal.png
--------------------------------------------------------------------------------
/media/cryo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/cryo.png
--------------------------------------------------------------------------------
/media/entrainment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/entrainment.png
--------------------------------------------------------------------------------
/media/eos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/eos.png
--------------------------------------------------------------------------------
/media/factual_classes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/factual_classes.png
--------------------------------------------------------------------------------
/media/glacier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/glacier.png
--------------------------------------------------------------------------------
/media/icebound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/icebound.png
--------------------------------------------------------------------------------
/media/inheritance_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/inheritance_diagram.png
--------------------------------------------------------------------------------
/media/isoft_classes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/isoft_classes.png
--------------------------------------------------------------------------------
/media/isoft_sequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/isoft_sequence.png
--------------------------------------------------------------------------------
/media/melt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/melt.png
--------------------------------------------------------------------------------
/media/object_tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/object_tree.png
--------------------------------------------------------------------------------
/media/plumebound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/plumebound.png
--------------------------------------------------------------------------------
/media/precond.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/precond.png
--------------------------------------------------------------------------------
/media/precond_001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/precond_001.png
--------------------------------------------------------------------------------
/media/precond_002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/precond_002.png
--------------------------------------------------------------------------------
/media/viscosity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/media/viscosity.png
--------------------------------------------------------------------------------
/mj-config.js:
--------------------------------------------------------------------------------
1 | window.MathJax = {
2 | AuthorInit: function() {
3 | MathJax.Hub.Register.StartupHook("TeX AMSmath Ready", function() {
4 | MathJax.InputJax.TeX.Definitions.Add({
5 | macros: {
6 | setcounter: "setCounter"
7 | }
8 | }, null, true);
9 | MathJax.InputJax.TeX.Parse.Augment({
10 | setCounter: function(name) {
11 | var num = parseInt(this.GetArgument(name));
12 | MathJax.Extension["TeX/AMSmath"].number = num;
13 | }
14 | });
15 | });
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/plotting/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/plotting/__init__.py
--------------------------------------------------------------------------------
/plotting/calculus.py:
--------------------------------------------------------------------------------
1 | #
2 | # calculus.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | import numpy as np
24 |
25 | cheb_cache = dict()
26 |
27 | def cheb(size, lower=0.0, upper=1.0):
28 | '''
29 | Computes a Chebyshev differentiation matrix of the appropriate size.
30 | Returns a 1D Numpy array of Chebyshev collocation points and a 2D
31 | differentiation matrix.
32 | '''
33 | if (size, lower, upper) in cheb_cache:
34 | return cheb_cache[(size, lower, upper)]
35 |
36 | def c(i):
37 | if i%2 == 0:
38 | f = 1.0
39 | else:
40 | f = -1.0
41 | if i == 0 or i == size:
42 | return f*2.0
43 | else:
44 | return f*1.0
45 |
46 | if size < 1:
47 | raise Exception('Must have more than one Chebyshev node.')
48 | #~ x = 0.5*np.cos(np.linspace(0.0, 1.0, size+1) * np.pi) + 0.5
49 | x = np.cos(np.linspace(0.0, 1.0, size+1) * np.pi)
50 | D = np.empty((size+1, size+1))
51 | for i in range(size + 1):
52 | if i != 0 and i != size: D[i,i] = -x[i]/(2.0*(1.0-x[i]**2))
53 | for j in range(i) + range(i+1, size+1):
54 | D[i,j] = c(i)/c(j)/(x[i] - x[j])
55 | c = (2.0*float(size**2) + 1.0)/6.0
56 | D[0,0] = c
57 | D[size,size] = -c
58 | # Adjust for use on [0,1] interval
59 | factor = (upper - lower)/2.0
60 | x = factor*(1 + x) + lower
61 | D = 2.0*D/(upper - lower)
62 | cheb_cache[(size, lower, upper)] = (x,D)
63 | return (x,D)
64 |
65 |
66 | class Differentiator(object):
67 | """An object which can perform a differentiation operation on a field
68 | from an ISOFT run.
69 |
70 | size
71 | The number of Chebyshev modes in the field
72 | lower
73 | The lower boundary of the field domain
74 | upper
75 | The upper boundary of the field domain
76 | """
77 |
78 | def __init__(this, size, lower=0.0, upper=1.0):
79 | this.x, this.D = cheb(size - 1, lower, upper)
80 |
81 | def __call__(this, rhs):
82 | return this.D.dot(rhs)
83 |
84 |
--------------------------------------------------------------------------------
/plotting/dimensionless_nums.py:
--------------------------------------------------------------------------------
1 | #
2 | # dimensionless_num.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''Contains routines for calculating various useful dimensionless
24 | numbers.
25 |
26 | '''
27 |
28 | import numpy as np
29 |
30 | def froude(coef, U, drho, D):
31 | return coef * np.linalg.norm(U, axis=-1)/np.sqrt(drho*D)
32 |
33 |
--------------------------------------------------------------------------------
/plotting/entrainment.py:
--------------------------------------------------------------------------------
1 | #
2 | # entrainment.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''Contains classes for calculating the entrainment rate of the plume
24 | using different parameterisations.
25 | '''
26 |
27 | import numpy as np
28 | import calculus
29 |
30 | class Jenkins1991Entrainment(object):
31 | '''A class representing the entrainment formulation used by Jenkins et
32 | al. (1991).
33 |
34 | coefficient
35 | The coefficient used in the entrainment calculation
36 | size
37 | The number of Chebyshev modes in the field
38 | lower
39 | The lower boundary of the field domain
40 | upper
41 | The upper boundary of the field domain
42 | '''
43 |
44 | def __init__(this, coefficient, size, lower=0.0, upper=1.0):
45 | this.coef = coefficient
46 | this.diff = calculus.Differentiator(size, lower, upper)
47 |
48 | def __call__(this, U, D, b, rho_diff = None):
49 | return this.coef * np.linalg.norm(U, axis=-1) * np.abs(this.diff(b))
50 |
51 |
52 | class Kochergin1987Entrainment(object):
53 | '''A class representing the entrainment formulation used by Kochergin
54 | (1987).
55 |
56 | coefficient
57 | The coefficient $c_l^2x_0/D_0$ used in the entrainment calculation
58 | delta
59 | The ratio between the scale of the plume thickness and that of the
60 | ice thickness, $D_0/h_0$
61 | '''
62 |
63 | def __init__(this, coefficient, delta):
64 | this.coef = coefficient
65 | this.delta = delta
66 |
67 | def __call__(this, U, D, b, rho_diff):
68 | Ri = np.abs(this.delta*rho_diff*D)/np.linalg.norm(U, axis=-1)**2
69 | Sm = Ri/(0.0725*(Ri + 0.186 - np.sqrt(Ri**2 - 0.316*Ri + 0.0346)))
70 | return this.coef*np.linalg.norm(U, axis=-1)/Sm * np.sqrt(1 + Ri/Sm)
71 |
--------------------------------------------------------------------------------
/plotting/eos.py:
--------------------------------------------------------------------------------
1 | #
2 | # entrainment.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''Contains classes for calculating density of water.
24 | '''
25 |
26 | import numpy as np
27 | import calculus
28 |
29 | class LinearEos(object):
30 | '''A class representing a linearised equation of state. It uses the
31 | equation:
32 |
33 | density = ref_density*[1 - beta_T*(T-T_ref) + beta_S*(S-S_ref)]
34 | '''
35 |
36 | def __init__(this, ref_density, beta_T, beta_S, T_ref, S_ref):
37 | this.rd = ref_density
38 | this.bT = beta_T
39 | this.bS = beta_S
40 | this.Tr = T_ref
41 | this.Sr = S_ref
42 |
43 | def __call__(this, T, S):
44 | return this.rd*(1 - this.bT*(T - this.Tr) + this.bS*(S - this.Sr))
45 |
--------------------------------------------------------------------------------
/plotting/layers.py:
--------------------------------------------------------------------------------
1 | #
2 | # layers.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2018 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | import numpy as np
24 | import numpy.ma as ma
25 |
26 | def compute_layers(shelf, vertical_resolution=300):
27 | """Computes the internal layers or age field for an ice shelf from
28 | the Taylor coefficients.
29 |
30 | """
31 | xc = shelf.grid
32 | zc = np.linspace(np.min(shelf.b), np.max(shelf.s), vertical_resolution)
33 |
34 | xx, zz = np.meshgrid(xc, zc)
35 | shelf_domain = np.logical_or(np.greater(zz, shelf.s), np.less(zz, shelf.b))
36 | x = ma.array(xx, mask=shelf_domain, copy=False)
37 | z = ma.array(zz, mask=shelf_domain, copy=False)
38 |
39 | kappa = shelf.kappa
40 |
41 | # This isn't really the most efficient way to calculate the Taylor
42 | # series, but array broadcasting was giving me a headache.
43 | k = np.zeros_like(z)
44 | for i in range(1, 1+kappa.shape[0]):
45 | k += kappa[i-1] * (shelf.s - z)**i
46 | return x, z, k
47 |
--------------------------------------------------------------------------------
/plotting/melt.py:
--------------------------------------------------------------------------------
1 | #
2 | # melt.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''Contains classes for calculating the melt rate of the ice shelf
24 | using different parameterisations.
25 | '''
26 |
27 | import numpy as np
28 |
29 | class Dallaston2015Melt(object):
30 | '''A class representing the melt formulation used by Dallaston et
31 | al. (2015).
32 |
33 | beta
34 | Inverse Stefan number
35 | epsilon_g
36 | subglacial flux/entrained flux
37 | epsilon_m
38 | subshelf melt/entrained flux
39 | '''
40 |
41 | def __init__(self, beta, epsilon_g, epsilon_m):
42 | self.beta = beta
43 | self.epsilon_g = epsilon_g
44 | self.epsilon_m = epsilon_m
45 |
46 | def __call__(self, U, p, T, S, D):
47 | return np.linalg.norm(U, axis=-1)*(1 - self.epsilon_m/self.beta*T)
48 |
49 | def thermal_forcing(self, U, p, T, S, D):
50 | return self(U, p, T, S, D)*(self.beta + 1)
51 |
52 | def saline_forcing(self, U, p, T, S, D):
53 | return np.zeros(T.size)
54 |
55 |
56 | class OneEquationMelt(object):
57 | '''A class representing a melt formulation similar to that used by
58 | Dallaston et al. (2015), but including the terms they removed
59 | after scale analysis.
60 |
61 | coef1
62 | The factor $Gamma_tx_0/D_0$
63 | coef2
64 | The factor $c_0T_0/L$
65 | fresh_sal
66 | The numerical salinity value given to fresh water
67 | melt_temp
68 | The numerical temperature value at which melting occurs
69 | '''
70 |
71 | def __init__(self, coef1, coef2, fresh_sal=0., melt_temp=0.):
72 | self.coef1 = coef1
73 | self.coef2 = coef2
74 | self.fresh_sal = fresh_sal
75 | self.melt_temp = melt_temp
76 |
77 | def forcing_value(self, U, p, T, S, D):
78 | return self.coef1*np.linalg.norm(U, axis=-1)*(T - self.melt_temp)
79 |
80 | def __call__(self, U, p, T, S, D):
81 | return self.coef2*self.forcing_value(U, p, T, S, D)
82 |
83 | def thermal_forcing(self, U, p, T, S, D):
84 | return (1. - self.coef2*self.melt_temp)*self.forcing_value(U, p, T, S, D)
85 |
86 | def saline_forcing(self, U, p, T, S, D):
87 | return -self(U, p, T, S, D)*self.fresh_sal
88 |
--------------------------------------------------------------------------------
/plotting/readers.py:
--------------------------------------------------------------------------------
1 | #
2 | # readers.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2017 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''
24 | Contains classes for reading simulation data from HDF5 files.
25 | '''
26 |
27 | import h5py
28 | import numpy as np
29 |
30 | class Glacier(object):
31 | '''A class which reads glacier data from HDF5 output. It presents
32 | the data in a convenient manner for plotting.
33 |
34 | hdf_obj
35 | An HDF5 file or group.
36 | hdf_group
37 | The name of the group in `hdf_obj` which contains the ice shelf
38 | data.
39 | '''
40 |
41 | def __init__(self, hdf_obj, hdf_group):
42 | self.groupname = hdf_group
43 | self.data = hdf_obj[hdf_group]
44 |
45 | @property
46 | def grid(self):
47 | return self.data[self.data['thickness'].attrs['gridpoints_dim1']][...]
48 |
49 | @property
50 | def h(self):
51 | return self.data['thickness'][...]
52 |
53 | @property
54 | def uvec(self):
55 | return self.data['velocity'][...].T
56 |
57 | @property
58 | def u(self):
59 | return self.uvec[0,...]
60 |
61 | @property
62 | def v(self):
63 | return self.uvec[1,...]
64 |
65 | @property
66 | def kappa(self):
67 | n = self.data.attrs['num_kappas']
68 | kap = np.empty((n, self.data['thickness'].len()))
69 | for i in range(n):
70 | kap[i,:] = self.data['kappa_{0:04d}'.format(i+1)][...]
71 | return kap
72 |
73 | @property
74 | def glacier_type(self):
75 | return self.data.attrs['glacier_type']
76 |
77 | @property
78 | def lambd(self):
79 | return self.data.attrs['lambda'][0]
80 |
81 | @property
82 | def chi(self):
83 | return self.data.attrs['chi'][0]
84 |
85 |
86 | class Plume(object):
87 | '''A class which reads Plume data from HDF5 output. It presents
88 | the data in a convenient manner for plotting.
89 |
90 | hdf_obj
91 | An HDF5 file or group.
92 | hdf_group
93 | The name of the group in `hdf_obj` which contains the ice shelf
94 | data.
95 | '''
96 |
97 | def __init__(self, hdf_obj, hdf_group):
98 | self.groupname = hdf_group
99 | self.data = hdf_obj[hdf_group]
100 |
101 | @property
102 | def grid(self):
103 | return self.data[self.data['thickness'].attrs['gridpoints_dim1']][...]
104 |
105 | @property
106 | def D(self):
107 | return self.data['thickness'][...]
108 |
109 | @property
110 | def Uvec(self):
111 | return self.data['velocity'][...].T
112 |
113 | @property
114 | def U(self):
115 | return self.Uvec[...,0]
116 |
117 | @property
118 | def V(self):
119 | return self.Uvec[...,1]
120 |
121 | @property
122 | def S(self):
123 | return self.data['salinity'][...]
124 |
125 | @property
126 | def T(self):
127 | return self.data['temperature'][...]
128 |
129 | @property
130 | def basal_surface_type(self):
131 | return self.data.attrs['basal_type']
132 |
133 | @property
134 | def delta(self):
135 | return self.data.attrs['delta'][0]
136 |
137 | @property
138 | def mu(self):
139 | return self.data.attrs['mu'][0]
140 |
141 | @property
142 | def nu(self):
143 | return self.data.attrs['nu'][0]
144 |
145 | @property
146 | def r(self):
147 | return self.data.attrs['r_val'][0]
148 |
149 | @property
150 | def phi(self):
151 | return self.data.attrs['phi'][0]
152 |
153 |
154 | class ShelfPlumeCryosphere(object):
155 | '''An abstract class which represents a Cryosphere object containing
156 | at least one glacier and one basal surface.
157 |
158 | hdf_file
159 | The HDF5 file containing the ice shelf/plume data.
160 | '''
161 | def __init__(self, hdf_file):
162 | self.filename = hdf_file
163 | self.data = h5py.File(hdf_file, 'r')
164 | self.shelf = Glacier(self.data, 'glacier')
165 | self.plume = Plume(self.data, 'basal_surface')
166 |
167 | @property
168 | def isoft_version(self):
169 | return self.data.attrs['isoft_version']
170 |
171 | @property
172 | def compilation_time(self):
173 | return self.data.attrs['binary_compilation_time']
174 |
175 | @property
176 | def output_time(self):
177 | return self.data.attrs['data_output_time']
178 |
179 | @property
180 | def time(self):
181 | return self.data.attrs['simulation_time'][0]
182 |
183 | @property
184 | def grid(self):
185 | return self.shelf.grid
186 |
187 | @property
188 | def h(self):
189 | return self.shelf.h
190 |
191 | @property
192 | def uvec(self):
193 | return self.shelf.uvec
194 |
195 | @property
196 | def u(self):
197 | return self.shelf.u
198 |
199 | @property
200 | def v(self):
201 | return self.shelf.v
202 |
203 | @property
204 | def kappa(self):
205 | return self.shelf.kappa
206 |
207 | @property
208 | def glacier_type(self):
209 | return self.shelf.glacier_type
210 |
211 | @property
212 | def lambd(self):
213 | return self.shelf.lambd
214 |
215 | @property
216 | def chi(self):
217 | return self.shelf.chi
218 |
219 | @property
220 | def s(self):
221 | return self.h*(1.0 - 1.0/self.r)
222 |
223 | @property
224 | def b(self):
225 | return -self.h/self.r
226 |
227 | @property
228 | def D(self):
229 | return self.plume.D
230 |
231 | @property
232 | def Uvec(self):
233 | return self.plume.Uvec
234 |
235 | @property
236 | def U(self):
237 | return self.plume.U
238 |
239 | @property
240 | def V(self):
241 | return self.plume.V
242 |
243 | @property
244 | def S(self):
245 | return self.plume.S
246 |
247 | @property
248 | def T(self):
249 | return self.plume.T
250 |
251 | @property
252 | def basal_surface_type(self):
253 | return self.plume.basal_surface_type
254 |
255 | @property
256 | def delta(self):
257 | return self.plume.delta
258 |
259 | @property
260 | def mu(self):
261 | return self.plume.mu
262 |
263 | @property
264 | def nu(self):
265 | return self.plume.nu
266 |
267 | @property
268 | def r(self):
269 | return self.plume.r
270 |
271 | @property
272 | def phi(self):
273 | return self.plume.phi
274 |
--------------------------------------------------------------------------------
/plotting/viscosity.py:
--------------------------------------------------------------------------------
1 | #
2 | # viscosity.py
3 | # This file is part of ISOFT.
4 | #
5 | # Copyright 2018 Chris MacMackin
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | # MA 02110-1301, USA.
21 | #
22 |
23 | '''Contains classes for calculating the viscosity of the ice
24 | using different parameterisations.
25 | '''
26 |
27 | import numpy as np
28 | import calculus
29 |
30 | class NewtonianViscosity(object):
31 | '''A class representing Newtonian viscosity.
32 |
33 | value
34 | The viscosity value of the ice
35 | '''
36 |
37 | def __init__(this, value=1.0):
38 | this.val = value
39 |
40 | def __call__(this, uvec, temperature=-15.0, time=0):
41 | return this.val*np.ones_like(uvec[0,...])
42 |
43 |
44 | class GlensLaw(object):
45 | '''A class using Glen's Law to represent viscosity. It treats
46 | viscosity as a power law for strain.
47 |
48 | size
49 | The number of Chebyshev modes in the field
50 | lower
51 | The lower boundary of the field domain
52 | upper
53 | The upper boundary of the field domain
54 | coef
55 | The coefficient by which Glen's Law is scaled
56 | index
57 | The index of the power law
58 | '''
59 |
60 | def __init__(this, size, lower=0.0, upper=1.0, coef=1.0, index=3):
61 | this.diff = calculus.Differentiator(size, lower, upper)
62 | this.coef = coef
63 | this.index = float(index)
64 |
65 | def __call__(this, uvec, temperature=-15.0, time=0):
66 | if (uvec.ndim > 2):
67 | raise NotImplementedError('GlensLaw only implemented for 1-D '
68 | 'velocity field.')
69 | return 0.5*this.coef*abs(this.diff(uvec[:,0]))**(1./this.index - 1.)
70 |
71 |
--------------------------------------------------------------------------------
/scripts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacmackin/isoft/af0b29b5c6bf5484de1b1544d0c8e6a5338ca137/scripts/__init__.py
--------------------------------------------------------------------------------
/scripts/decompose.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from plotting.readers import ShelfPlumeCryosphere
4 | from plotting.melt import OneEquationMelt, Dallaston2015Melt
5 | from plotting.entrainment import Jenkins1991Entrainment
6 | from plotting.eos import LinearEos
7 | from plotting.calculus import Differentiator
8 | from plotting.dimensionless_nums import froude
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | import sys
12 |
13 | if len(sys.argv) > 1:
14 | cryo = ShelfPlumeCryosphere(sys.argv[1])
15 | else:
16 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
17 |
18 | x = cryo.grid
19 | diff = Differentiator(x.size, x[-1], x[0])
20 | m = OneEquationMelt(0.0182, 4.86e-4, -2035.3, -54.92)
21 | e = Jenkins1991Entrainment(1.0, x.size, x[-1], x[0])
22 | eos = LinearEos(3.05e5, 1.409e-6, 1.336e-5, 0., 0.)
23 | D = cryo.D
24 | Uvec = cryo.Uvec
25 | U = cryo.U
26 | T = cryo.T
27 | S = cryo.S
28 | b = cryo.b
29 | ent = e(Uvec, D, b)
30 | mu = cryo.mu
31 | nu = cryo.nu
32 | delta = cryo.delta
33 | T_a = 0.0
34 | S_a = 0.0
35 | rho = eos(T, S)
36 | rho_a = eos(T_a, S_a)
37 |
38 | plt.plot(x, diff(D*U), label=r'$(DU)_x$')
39 | plt.plot(x, ent, label=r'$e$')
40 | plt.plot(x, m(Uvec, b, T, S, D), label=r'$m$')
41 | plt.plot(x, diff(D*U) - ent - m(Uvec, b, T, S, D), label='Sum')
42 | #plt.plot(x, U*diff(D), label=r'$D_xU$')
43 | #plt.plot(x, D*diff(U), label=r'$DU_x$')
44 | plt.legend()
45 | plt.show()
46 |
47 | plt.plot(x, diff(D*U**2), label=r'$(DU^2)_x$')
48 | plt.plot(x, D*(rho_a - rho)*diff(b - delta*D),
49 | label=r'$D(\rho_a - \rho)(b_x - \delta D_x)$')
50 | plt.plot(x, nu*diff(D*diff(U)), label=r'$\nu (DU_{x})_{x}$')
51 | plt.plot(x, -mu*np.abs(U)*U, label=r'$-\mu |U|U$')
52 | plt.plot(x, delta/2*D**2*diff(rho), label=r'$\frac{\delta D^2}{2}\rho_x$')
53 | plt.plot(x, diff(D*U**2) - D*(rho_a - rho)*diff(b - delta*D) -
54 | nu*diff(D*diff(U)) + mu*abs(U)*U - delta/2*D**2*diff(rho),
55 | label='Sum')
56 | #plt.plot(x, U**2*diff(D), label=r'$D_xU^2$')
57 | #plt.plot(x, 2*D*U*diff(U), label=r'$2DUU_x$')
58 | plt.legend()
59 | plt.show()
60 |
61 | plt.plot(x, diff(D*U*T), label='$(DUT)_x$')
62 | plt.plot(x, ent*T_a, label='$eT_a$')
63 | plt.plot(x, nu*diff(D*diff(T)), label=r'$\nu (DT_{x})_{x}$')
64 | plt.plot(x, -m.thermal_forcing(Uvec, b, T, S, D), label='$-\gamma_T (T-T_m)$')
65 | plt.plot(x, diff(D*U*T) - ent*T_a - nu*diff(D*diff(T)) +
66 | m.thermal_forcing(Uvec, b, T, S, D), label='Sum')
67 | #plt.plot(x, T*U*diff(D), label=r'$D_xUT$')
68 | #plt.plot(x, T*D*diff(U), label=r'$DU_xT$')
69 | #plt.plot(x, D*U*diff(T), label=r'$DUT_x$')
70 | plt.legend()
71 | plt.show()
72 |
73 | plt.plot(x, diff(D*U*S), label='$(DUS)_x$')
74 | plt.plot(x, ent*S_a, label='$eS_a$')
75 | plt.plot(x, nu*diff(D*diff(S)), label=r'$\nu (DS_{x})_{x}$')
76 | plt.plot(x, -m.saline_forcing(Uvec, b, T, S, D), label='$-\gamma_S (S-S_m)$')
77 | plt.plot(x, diff(D*U*S) - ent*S_a - nu*diff(D*diff(S)) +
78 | m.saline_forcing(Uvec, b, T, S, D), label='Sum')
79 | #plt.plot(x, S*U*diff(D), label=r'$D_xUS$')
80 | #plt.plot(x, S*D*diff(U), label=r'$DU_xS$')
81 | #plt.plot(x, D*U*diff(S), label=r'$DUS_x$')
82 | plt.legend()
83 | plt.show()
84 |
85 |
--------------------------------------------------------------------------------
/scripts/display.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from plotting.readers import ShelfPlumeCryosphere
4 | import matplotlib.pyplot as plt
5 | from matplotlib import gridspec
6 | import numpy as np
7 | import sys
8 |
9 |
10 | if len(sys.argv) > 1:
11 | cryo = ShelfPlumeCryosphere(sys.argv[1])
12 | else:
13 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
14 |
15 | x = cryo.grid
16 | D = cryo.D
17 | Uvec = cryo.Uvec
18 | U = cryo.U
19 | T = cryo.T
20 | S = cryo.S
21 | b = cryo.b
22 |
23 | r = 1.12
24 | gamma = 4
25 | lambd = 1e2
26 | X = 1.0/(lambd*2.02188465743*4.3316e-4)
27 | velocity = np.sqrt(1.0 + gamma/4.0*X - gamma/4.0*X*(1.0 - x/X)**2)
28 | thickness = (1.0-x/X)/velocity
29 | depth = -thickness/r
30 |
31 | fig = plt.figure(figsize=(5,5))
32 | gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
33 | ax1 = fig.add_subplot(gs[0])
34 | ax2 = fig.add_subplot(gs[1], sharex=ax1)
35 |
36 | ax1.plot(x, D, label='$D$')
37 | ax1.plot(x, U, label='$U$')
38 | ax1.plot(x, cryo.b, label='$b$')
39 | ax1.plot(x, depth, '--', label=r'$\hat{b}$')
40 | ax1.plot(x, cryo.u, label=r'$u$')
41 | ax1.get_xaxis().set_visible(False)
42 | ax1.set_ylim(-1, 3.5)
43 | ax1.get_yticklabels()[0].set_visible(False)
44 | ax1.legend(prop={'size': 'medium'}, ncol=3, columnspacing=1.0, loc=2)
45 |
46 | ax2.plot(x, T, label='$T$')
47 | ax2.plot(x, S, label='$S$')
48 | ax2.set_xlabel('$x$')
49 | ax2.legend(prop={'size': 'medium'}, loc=3)
50 |
51 | fig.tight_layout()
52 | fig.subplots_adjust(hspace=0)
53 |
54 | if len(sys.argv) > 2:
55 | plt.savefig(sys.argv[2])
56 | else:
57 | plt.show()
58 |
59 |
--------------------------------------------------------------------------------
/scripts/ice_continuity.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from plotting.readers import ShelfPlumeCryosphere
4 | from plotting.calculus import Differentiator
5 | from plotting.melt import OneEquationMelt
6 | import matplotlib.pyplot as plt
7 | from matplotlib.backends.backend_pdf import PdfPages
8 | import sys
9 |
10 |
11 | if len(sys.argv) > 1:
12 | cryo = ShelfPlumeCryosphere(sys.argv[1])
13 | else:
14 | cryo = ShelfPlumeCryosphere('isoft-0200.h5')
15 |
16 | x = cryo.grid
17 | diff = Differentiator(x.size, x[-1], x[0])
18 | D = cryo.D
19 | Uvec = cryo.Uvec
20 | U = cryo.U
21 | T = cryo.T
22 | S = cryo.S
23 | h = cryo.h
24 | u = cryo.u
25 | m = OneEquationMelt(0.0182, 4.86e-4, -2035.3, -54.92)
26 | lambd = cryo.lambd
27 |
28 | plt.figure()
29 | u_dh = u*diff(h)
30 | h_du = h*diff(u)
31 | m = -cryo.lambd*m(Uvec, cryo.b, T, S, D)
32 | dh_dt = m - u_dh - h_du
33 | plt.plot(x, dh_dt, label=r'$h_t$')
34 | plt.plot(x, u_dh, label=r'$uh_x$')
35 | plt.plot(x, h_du, label=r'$u_x h$')
36 | plt.plot(x, m, label=r'$-\lambda m$')
37 | plt.xlabel(r'$x$')
38 | plt.legend(loc=0)
39 | plt.tight_layout()
40 | if len(sys.argv) > 2:
41 | plt.savefig(sys.argv[2])
42 | else:
43 | plt.show()
44 |
--------------------------------------------------------------------------------
/scripts/internal_layers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from plotting.readers import ShelfPlumeCryosphere
3 | from plotting.layers import compute_layers
4 |
5 | import numpy as np
6 | import numpy.ma as ma
7 | import matplotlib
8 | import matplotlib.pyplot as plt
9 |
10 | import sys
11 |
12 | if len(sys.argv) > 1:
13 | cryo = ShelfPlumeCryosphere(sys.argv[1])
14 | else:
15 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
16 |
17 | nz = 1200
18 | conts = 16
19 |
20 | x, z, k = compute_layers(cryo, nz)
21 | #plt.plot(cryo.s[-1] - z[:,-1], k[:, -1], label=r'$k$')
22 | #plt.plot(cryo.s[-1] - z[:,-1], 1/np.sqrt(1.05 - cryo.s[-1] + z[:,-1])
23 | # - 1/np.sqrt(1.05), label=r'$k$')
24 | #plt.legend(loc=0)
25 | #plt.show()
26 |
27 | plt.figure(figsize=(8,6))
28 | plt.plot(cryo.grid, cryo.s, 'k')
29 | plt.plot(cryo.grid, cryo.b, 'k')
30 | plt.contour(x, z, k, conts, linewidths=0.5, colors='k')
31 | plt.contourf(x, z, k, conts, cmap='PuBu')
32 | plt.xlabel('$x$')
33 | plt.ylabel('$z$')
34 | plt.colorbar(orientation='horizontal', label=r'$k$')
35 | plt.tight_layout()
36 | if len(sys.argv) > 2:
37 | plt.savefig(sys.argv[2])
38 | else:
39 | plt.show()
40 |
--------------------------------------------------------------------------------
/scripts/plume_momentum.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from plotting.readers import ShelfPlumeCryosphere
4 | from plotting.melt import OneEquationMelt, Dallaston2015Melt
5 | from plotting.entrainment import Jenkins1991Entrainment
6 | from plotting.eos import LinearEos
7 | from plotting.calculus import Differentiator
8 | from plotting.dimensionless_nums import froude
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | import sys
12 |
13 | if len(sys.argv) > 1:
14 | cryo = ShelfPlumeCryosphere(sys.argv[1])
15 | else:
16 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
17 |
18 | x = cryo.grid
19 | diff = Differentiator(x.size, x[-1], x[0])
20 | eos = LinearEos(3.05e5, 1.409e-6, 1.336e-5, 0., 0.)
21 | D = cryo.D
22 | Uvec = cryo.Uvec
23 | U = cryo.U
24 | T = cryo.T
25 | S = cryo.S
26 | b = cryo.b
27 | mu = cryo.mu
28 | nu = cryo.nu
29 | delta = cryo.delta
30 | T_a = 0.0
31 | S_a = 0.0
32 | rho = eos(T, S)
33 | rho_a = eos(T_a, S_a)
34 |
35 | plt.figure(figsize=(5,5))
36 | plt.plot(x, diff(D*U**2), label=r'$(DU^2)_x$')
37 | plt.plot(x, D*(rho_a - rho)*diff(b),
38 | label=r'$D(\rho_a - \rho)b_x$')
39 | plt.plot(x, D*(rho_a - rho)*diff(-delta*D),
40 | label=r'$-D(\rho_a - \rho)\delta D_x$')
41 | plt.plot(x, nu*diff(D*diff(U)), label=r'$\nu (DU_{x})_{x}$')
42 | plt.plot(x, -mu*np.abs(U)*U, label=r'$-\mu |U|U$')
43 | plt.plot(x, delta/2*D**2*diff(rho), label=r'$\frac{\delta D^2}{2}\rho_x$')
44 | #plt.plot(x, diff(D*U**2) - D*(rho_a - rho)*diff(b - delta*D) -
45 | # nu*diff(D*diff(U)) + mu*abs(U)*U - delta/2*D**2*diff(rho),
46 | # label='Sum')
47 | plt.legend(prop={'size': 'medium'})
48 | plt.xlabel('$x$')
49 | plt.tight_layout()
50 |
51 | if len(sys.argv) > 2:
52 | plt.savefig(sys.argv[2])
53 | else:
54 | plt.show()
55 |
56 |
--------------------------------------------------------------------------------
/scripts/shock.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 | from plotting.readers import ShelfPlumeCryosphere
5 | from plotting.dimensionless_nums import froude
6 | from plotting.eos import LinearEos
7 | import matplotlib.pyplot as plt
8 | from numpy import sqrt
9 |
10 | eos = LinearEos(3.05e5, 1.409e-6, 1.336e-5, 0., 0.)
11 | T_a = 0.0
12 | S_a = 0.0
13 | rho_a = eos(T_a, S_a)
14 | U_0 = 0.196
15 | D_0 = 43.2
16 | rho_0 = 1030.
17 | rho_s = 3.38e-3
18 | g = 9.8
19 | coef = U_0/sqrt(g*rho_s*D_0/rho_0)
20 |
21 |
22 | if len(sys.argv) > 1:
23 | cryo = ShelfPlumeCryosphere(sys.argv[1])
24 | else:
25 | cryo = ShelfPlumeCryosphere('isoft-0000.h5')
26 |
27 | x = cryo.grid
28 | rho = eos(cryo.T, cryo.S)
29 | plt.figure(figsize=(5,5))
30 | plt.plot(x, cryo.D, label='$D$')
31 | plt.plot(x, cryo.U, label='$U$')
32 | plt.plot(x, froude(coef, cryo.Uvec, rho_a - rho, cryo.D), label=r'$\rm Fr$')
33 | plt.legend(prop={'size': 'medium'})
34 |
35 | plt.xlabel('$x$')
36 | plt.tight_layout()
37 | if len(sys.argv) > 2:
38 | plt.savefig(sys.argv[2])
39 | else:
40 | plt.show()
41 |
--------------------------------------------------------------------------------
/src/ambient.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! ambient.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module ambient_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: April 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract derived type which can be subtyped in order to
34 | ! specify the temperature and salinity of the ambient ocean.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field
38 | implicit none
39 | private
40 |
41 | type, abstract, public :: ambient_conditions
42 | !* Author: Chris MacMackin
43 | ! Date: April 2016
44 | !
45 | ! An abstract type to which procedures for getting the ambient ocean
46 | ! conditions are to be specified. The descendent types can contain
47 | ! whatever data is needed to compute the result.
48 | !
49 | contains
50 | procedure(get_property), deferred :: ambient_temperature
51 | !! Returns the ambient ocean temperature
52 | procedure(get_property), deferred :: ambient_salinity
53 | !! Returns the ambient ocean temperature
54 | end type ambient_conditions
55 |
56 | abstract interface
57 | function get_property(this, depth, t) result(property)
58 | import :: r8
59 | import :: ambient_conditions
60 | import :: scalar_field
61 | class(ambient_conditions), intent(in) :: this
62 | class(scalar_field), intent(in) :: depth
63 | !! A field containing the depths at which the ambient conditions
64 | !! are to be calculated.
65 | real(r8), intent(in) :: t
66 | !! The time at which the ambient conditions are to be calculated.
67 | class(scalar_field), pointer :: property
68 | !! A field containing the ambient conditions at the depth specified
69 | !! for each location.
70 | end function get_property
71 | end interface
72 |
73 | end module ambient_mod
74 |
--------------------------------------------------------------------------------
/src/ambient/uniform.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! uniform.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module uniform_ambient_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: November 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides a derived type specifying uniform ambient temperature and
34 | ! salinity conditions beneath an ice shelf.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, uniform_scalar_field
38 | use ambient_mod, only: ambient_conditions
39 | implicit none
40 | private
41 |
42 |
43 | type, extends(ambient_conditions), public :: uniform_ambient_conditions
44 | !* Author: Chris MacMackin
45 | ! Date: April 2016
46 | !
47 | ! An derived type with procedures for getting the ambient ocean
48 | ! conditions. This implementation takes these conditions to be
49 | ! everywhere uniform.
50 | !
51 | private
52 | type(uniform_scalar_field) :: temperature
53 | type(uniform_scalar_field) :: salinity
54 | contains
55 | procedure :: ambient_temperature => uniform_temperature
56 | !! Returns the ambient ocean temperature
57 | procedure :: ambient_salinity => uniform_salinity
58 | !! Returns the ambient ocean temperature
59 | end type uniform_ambient_conditions
60 |
61 | interface uniform_ambient_conditions
62 | module procedure constructor
63 | end interface uniform_ambient_conditions
64 |
65 | contains
66 |
67 | function constructor(temperature, salinity) result(this)
68 | !* Author: Chris MacMackin
69 | ! Date: November 2016
70 | !
71 | ! Produces an ambient object which will return the specified
72 | ! salinity and temeprature values.
73 | !
74 | real(r8), intent(in), optional :: temperature
75 | !! The temperature of the ambient ocean. Default is 0.
76 | real(r8), intent(in), optional :: salinity
77 | !! The salinity of the ambient ocean. Default is 0.
78 | type(uniform_ambient_conditions) :: this
79 | if (present(temperature)) then
80 | this%temperature = uniform_scalar_field(temperature)
81 | else
82 | this%temperature = uniform_scalar_field(0.0_r8)
83 | end if
84 | if (present(salinity)) then
85 | this%salinity = uniform_scalar_field(salinity)
86 | else
87 | this%salinity = uniform_scalar_field(0.0_r8)
88 | end if
89 | end function constructor
90 |
91 | function uniform_temperature(this, depth, t) result(property)
92 | !* Author: Chris MacMackin
93 | ! Date: November 2016
94 | !
95 | ! Returns the ambient ocean temperature.
96 | !
97 | class(uniform_ambient_conditions), intent(in) :: this
98 | class(scalar_field), intent(in) :: depth
99 | !! A field containing the depths at which the ambient temperature
100 | !! is to be calculated.
101 | real(r8), intent(in) :: t
102 | !! The time at which the ambient conditions are to be calculated.
103 | class(scalar_field), pointer :: property
104 | !! A field containing the ambient temperature at the depth specified
105 | !! for each location.
106 | call this%temperature%allocate_scalar_field(property)
107 | property = this%temperature
108 | call property%set_temp() ! Shouldn't need to call this, but for
109 | ! some rason being set as non-temporary
110 | ! when assignment subroutine returns.
111 | end function uniform_temperature
112 |
113 | function uniform_salinity(this, depth, t) result(property)
114 | !* Author: Chris MacMackin
115 | ! Date: November 2016
116 | !
117 | ! Returns the ambient ocean salinity.
118 | !
119 | class(uniform_ambient_conditions), intent(in) :: this
120 | class(scalar_field), intent(in) :: depth
121 | !! A field containing the depths at which the ambient salinity
122 | !! is to be calculated.
123 | real(r8), intent(in) :: t
124 | !! The time at which the ambient conditions are to be calculated.
125 | class(scalar_field), pointer :: property
126 | !! A field containing the ambient salinity at the depth specified
127 | !! for each location.
128 | call this%salinity%allocate_scalar_field(property)
129 | property = this%salinity
130 | call property%set_temp() ! Shouldn't need to call this, but for
131 | ! some rason being set as non-temporary
132 | ! when assignment subroutine returns.
133 | end function uniform_salinity
134 |
135 | end module uniform_ambient_mod
136 |
--------------------------------------------------------------------------------
/src/boundary_types.f90:
--------------------------------------------------------------------------------
1 | !
2 | ! boundary_types.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2017 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module boundary_types_mod
24 | !* Author: Christopher MacMackin
25 | ! Date: January 2017
26 | ! License: GPLv3
27 | !
28 | ! Provides parameters which can be used to represent different types
29 | ! of boundary conditions.
30 | !
31 | implicit none
32 | private
33 |
34 | integer, parameter, public :: free_boundary = -1
35 | !! Indicates that the solution may take any value at the boundary.
36 | integer, parameter, public :: dirichlet = 0
37 | !! Indicates that the value of the solution at this boundary is
38 | !! prescribed.
39 | integer, parameter, public :: neumann = 1
40 | !! Indicates that the first derivative of the solution at this
41 | !! boundary is prescribed.
42 | integer, parameter, public :: cauchy = 5
43 | !! Indicates that the value of the solution and its first
44 | !! derivative are both prescribed at this boundary.
45 | integer, parameter, public :: robin = 6
46 | !! Indicates that the linear combination of the solution's value
47 | !! and first derivative is prescribed at this boundary.
48 |
49 | end module boundary_types_mod
50 |
--------------------------------------------------------------------------------
/src/entrainment.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! entrainment.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module entrainment_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract data type to model entrainment into a
34 | ! vertically integrated plume.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field
38 | implicit none
39 | private
40 |
41 | type, abstract, public :: abstract_entrainment
42 | !* Author: Christopher MacMackin
43 | ! Date: October 2016
44 | !
45 | ! An abstract data type for calculating entrainment of ambient
46 | ! ocean water into a vertically integrated [[plume]].
47 | !
48 | contains
49 | procedure(get_entrainment), deferred :: entrainment_rate
50 | !! Returns the entrainment rate for ambient water into the plume.
51 | end type abstract_entrainment
52 |
53 | abstract interface
54 | function get_entrainment(this, velocity, thickness, depth, density_diff, time) &
55 | result(property)
56 | import :: abstract_entrainment
57 | import :: vector_field
58 | import :: scalar_field
59 | import :: r8
60 | class(abstract_entrainment), intent(in) :: this
61 | class(vector_field), intent(in) :: velocity
62 | !! The velocity field of the plume into which fluid is being
63 | !! entrained.
64 | class(scalar_field), intent(in) :: thickness
65 | !! The thickness of the plume into which fluid is being
66 | !! entrained
67 | class(scalar_field), intent(in) :: depth
68 | !! The depth of the upper surface of the plume into which
69 | !! fluid is being entrained
70 | class(scalar_field), intent(in) :: density_diff
71 | !! The difference between the ambient density and the density of
72 | !! the plume into which the ambient fluid is being entrained.
73 | real(r8), intent(in), optional :: time
74 | !! The time at which the entrainment is being calculated. If not
75 | !! present then assumed to be same as previous value passed.
76 | class(scalar_field), pointer :: property
77 | !! The value of the entrainment
78 | end function get_entrainment
79 | end interface
80 |
81 | end module entrainment_mod
82 |
--------------------------------------------------------------------------------
/src/equation_of_state.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! equation_of_state.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module equation_of_state_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: April 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract derived type which can be subtyped in order to
34 | ! implement an equation of state.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field
38 | implicit none
39 | private
40 |
41 | type, abstract, public :: equation_of_state
42 | !* Author: Chris MacMackin
43 | ! Date: April 2016
44 | !
45 | ! An abstract type with a procedure for calculating water density
46 | ! from its temperature and salinity.
47 | !
48 | contains
49 | procedure(get_property), deferred :: water_density
50 | !! Returns the water density for the given temperature and salinity.
51 | procedure(get_property_dx), deferred :: water_density_derivative
52 | !! Returns the derivative of the water density for the given
53 | !! temperature and salinity.
54 | procedure(get_coef), deferred :: haline_contraction
55 | !! Returns a (possibly approximated) haline contraction coefficient.
56 | procedure(get_coef), deferred :: thermal_contraction
57 | !! Returns a (possibly approximated) therma contraction coefficient.
58 | end type equation_of_state
59 |
60 | abstract interface
61 | function get_property(this, temperature, salinity) result(density)
62 | import :: r8
63 | import :: equation_of_state
64 | import :: scalar_field
65 | class(equation_of_state), intent(in) :: this
66 | class(scalar_field), intent(in) :: temperature
67 | !! A field containing the temperature of the water
68 | class(scalar_field), intent(in) :: salinity
69 | !! A field containing the salinity of the water
70 | class(scalar_field), pointer :: density
71 | !! A field containing the density of the water
72 | end function get_property
73 |
74 | function get_property_dx(this, temperature, d_temperature, salinity, &
75 | d_salinity, dir) result(d_density)
76 | import :: r8
77 | import :: equation_of_state
78 | import :: scalar_field
79 | class(equation_of_state), intent(in) :: this
80 | class(scalar_field), intent(in) :: temperature
81 | !! A field containing the temperature of the water
82 | class(scalar_field), intent(in) :: d_temperature
83 | !! A field containing the derivative of the temperature of the
84 | !! water, in teh same direction as `dir`
85 | class(scalar_field), intent(in) :: salinity
86 | !! A field containing the salinity of the water
87 | class(scalar_field), intent(in) :: d_salinity
88 | !! A field containing the derivative of the salinity of the
89 | !! water, in the same direction as `dir`
90 | integer, intent(in) :: dir
91 | !! The direction in which to take the derivative
92 | class(scalar_field), pointer :: d_density
93 | !! A field containing the derivative of the density of the
94 | !! water in direction `dir`
95 | end function get_property_dx
96 |
97 | function get_coef(this, temperature, salinity) result(coef)
98 | import :: r8
99 | import :: equation_of_state
100 | import :: scalar_field
101 | class(equation_of_state), intent(in) :: this
102 | class(scalar_field), intent(in) :: temperature
103 | class(scalar_field), intent(in) :: salinity
104 | class(scalar_field), allocatable :: coef
105 | end function get_coef
106 | end interface
107 |
108 | end module equation_of_state_mod
109 |
--------------------------------------------------------------------------------
/src/melt_relationship.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! melt_relationship.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module melt_relationship_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract data type to model melting of an ice shelf into a
34 | ! vertically integrated plume.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field
38 | implicit none
39 | private
40 |
41 | type, abstract, public :: abstract_melt_relationship
42 | !* Author: Christopher MacMackin
43 | ! Date: October 2016
44 | !
45 | ! An abstract data type for calculating melting of an ice shelf into
46 | ! a vertically integrated [[plume(type)]]. The melt rate, as well as
47 | ! effect on termperature and salinity, are calculated by calling
48 | ! [[abstract_melt_relationship(type):solve_for_melt]] and then accessed
49 | ! using [[abstract_melt_relationship(type):melt_rate]],
50 | ! [[abstract_melt_relationship(type):heat_equation_terms]],
51 | ! [[abstract_melt_relationship(type):salt_equation_terms]].
52 | !
53 | contains
54 | procedure(solve), deferred :: solve_for_melt
55 | procedure(get_scalar), deferred :: salt_equation_terms
56 | !! Returns the terms this melt formulation contributes to the
57 | !! salt equation, after they have been solved for using
58 | !! [[abstract_melt_relationship(type):solve_for_melt]].
59 | procedure(get_scalar), deferred :: heat_equation_terms
60 | !! Returns the terms this melt formulation contributes to the
61 | !! heat equation, after they have been solved for using
62 | !! [[abstract_melt_relationship(type):solve_for_melt]].
63 | procedure(get_scalar), deferred :: melt_rate
64 | !! Returns the melt rate calculated using this formulation,
65 | !! after it has been solved for using
66 | !! [[abstract_melt_relationship(type):solve_for_melt]].
67 | procedure(has_terms), deferred :: has_heat_terms
68 | !! Whether this formulation of melting contributes any terms to
69 | !! a plume's heat equation.
70 | procedure(has_terms), deferred :: has_salt_terms
71 | !! Whether this formulation of melting contributes any terms to
72 | !! a plume's salinity equation.
73 | end type abstract_melt_relationship
74 |
75 | abstract interface
76 | subroutine solve(this, velocity, pressure, temperature, salinity, &
77 | plume_thickness, time)
78 | import :: abstract_melt_relationship
79 | import :: scalar_field
80 | import :: vector_field
81 | import :: r8
82 | class(abstract_melt_relationship), intent(inout) :: this
83 | class(vector_field), intent(in) :: velocity
84 | !! The velocity field of the plume into which fluid is melting.
85 | class(scalar_field), intent(in) :: pressure
86 | !! The water pressure at the interface where the melting occurs.
87 | class(scalar_field), intent(in) :: temperature
88 | !! The temperature of the plume into which fluid is melting.
89 | class(scalar_field), intent(in) :: salinity
90 | !! The salinity of the plume into which fluid is melting.
91 | class(scalar_field), intent(in) :: plume_thickness
92 | !! The thickness of the plume into which fluid is melting.
93 | real(r8), intent(in), optional :: time
94 | !! The time at which the melting is being solved for. If not
95 | !! present then assumed to be same as previous value passed.
96 | end subroutine solve
97 |
98 | function get_scalar(this) result(property)
99 | import :: abstract_melt_relationship
100 | import :: scalar_field
101 | class(abstract_melt_relationship), intent(in) :: this
102 | class(scalar_field), pointer :: property
103 | !! The value of whatever property is being returned.
104 | end function get_scalar
105 |
106 | pure function has_terms(this)
107 | import :: abstract_melt_relationship
108 | class(abstract_melt_relationship), intent(in) :: this
109 | logical :: has_terms
110 | !! Whether this formulation of melting contributes terms to
111 | !! the heat or salinity equations.
112 | end function has_terms
113 | end interface
114 |
115 | end module melt_relationship_mod
116 |
--------------------------------------------------------------------------------
/src/meta.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! meta_parameters.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module meta_mod
24 | !* Author: Chris MacMackin
25 | ! Date: November 2016
26 | ! License: GPLv3
27 | !
28 | ! Provides functions specifying the version of ISOFT, time of compilation, etc.
29 | !
30 | use iso_fortran_env, only: i8 => int64, compiler_version, compiler_options
31 | implicit none
32 |
33 | character(len=3), dimension(12), parameter :: months = ['Jan', &
34 | 'Feb', &
35 | 'Mar', &
36 | 'Apr', &
37 | 'May', &
38 | 'Jun', &
39 | 'Jul', &
40 | 'Aug', &
41 | 'Sep', &
42 | 'Oct', &
43 | 'Nov', &
44 | 'Dec']
45 | character(len=42), parameter :: time_format = '(a3,1x,i2,1x,i4,1x,i2.2,":",'// &
46 | 'i2.2,":",i2.2)'
47 |
48 | interface
49 | module function version()
50 | !* Author: Chris MacMackin
51 | ! Date: December 2016
52 | !
53 | ! Returns the version number for ISOFT.
54 | !
55 | character(len=5) :: version
56 | end function version
57 |
58 | module function compile_time()
59 | !* Author: Chris MacMackin
60 | ! Date: December 2016
61 | !
62 | ! Returns the date and time at which ISOFT was compiled.
63 | !
64 | character(len=20) :: compile_time
65 | end function compile_time
66 |
67 | module function compile_info()
68 | !* Author: Chris MacMackin
69 | ! Date: April 2017
70 | !
71 | ! Returns compiler version and and flags.
72 | !
73 | character(len=1000) :: compile_info
74 | end function compile_info
75 | end interface
76 |
77 | contains
78 |
79 | function current_time()
80 | !* Author: Chris MacMackin
81 | ! Date: November 2016
82 | !
83 | ! Returns the current date and time in the same format as the
84 | ! [[compile_time]] function.
85 | !
86 | character(len=20) :: current_time
87 | integer(i8), dimension(8) :: time_vals
88 | call date_and_time(values=time_vals)
89 | write(current_time,time_format) months(time_vals(2)), time_vals(3), &
90 | time_vals(1), time_vals(5), time_vals(6), &
91 | time_vals(7)
92 | end function current_time
93 |
94 | end module meta_mod
95 |
--------------------------------------------------------------------------------
/src/meta_implementation.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! meta_parameters.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | submodule(meta_mod) meta_implementation_mod
24 | !* Author: Chris MacMackin
25 | ! Date: November 2016
26 | ! License: GPLv3
27 | !
28 | ! Implements functions specifying the version of ISOFT, time of
29 | ! compilation, etc. This is done in a submodule because it should be
30 | ! recompiled with every build (so that compilation time is accurate)
31 | ! but without a submodule that would require every module using it
32 | ! to be recompiled as well.
33 | !
34 | implicit none
35 |
36 | character(len=20), parameter :: compile_time_val = __DATE__//' '//__TIME__
37 | character(len=5), parameter :: version_num = '1.0.0'
38 |
39 | contains
40 |
41 | module function version()
42 | version = version_num
43 | end function version
44 |
45 | module function compile_time()
46 | compile_time = compile_time_val
47 | end function compile_time
48 |
49 | module function compile_info()
50 | character(len=40), parameter :: result_format = &
51 | '("Compiled with ",a," using options ",a)'
52 | write(compile_info,result_format) compiler_version(), compiler_options()
53 | compile_info = 'Compiled with "'//compiler_version()// &
54 | '" using options "'//compiler_options()// &
55 | '"'
56 | end function compile_info
57 |
58 | end submodule meta_implementation_mod
59 |
--------------------------------------------------------------------------------
/src/parameterisations/glens_law.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! glens_law.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2017 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module glens_law_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: April 2017
31 | ! License: GPLv3
32 | !
33 | ! Provides a concrete implementation for the [[abstract_viscosity]]
34 | ! type using Glen's flow law.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field, abs, sqrt
38 | use viscosity_mod, only: abstract_viscosity
39 | implicit none
40 | private
41 |
42 | type, extends(abstract_viscosity), public :: glens_law_viscosity
43 | !* Author: Christopher MacMackin
44 | ! Date: April 2017
45 | !
46 | ! An implementation of Glen's flow law to describe glacier
47 | ! viscosity. It takes the form $$\eta = \frac{1}{2}BD^{1/n-1},$$
48 | ! where \(D = \sqrt{D_{ij}D_{ij}/2\) is the second invarient of
49 | ! the strain rate $$D_{ij} = \frac{1}{2}\left(\frac{\partial
50 | ! u_i}{\partial x_j} + \frac{\partial u_j}{\partial x_i}
51 | ! \right).$$ Here, \(B\) is treated as a constant, although it may
52 | ! be parameterised as a function of temperature.
53 | !
54 | private
55 | real(r8) :: b_val = 1.0_r8
56 | real(r8) :: index = 3._r8
57 | contains
58 | procedure :: ice_viscosity => glens_ice_viscosity
59 | !! Returns the viscosity for the ice.
60 | end type glens_law_viscosity
61 |
62 | interface glens_law_viscosity
63 | module procedure constructor
64 | end interface glens_law_viscosity
65 |
66 | contains
67 |
68 | pure function constructor(b_val, index) result(this)
69 | !* Author: Christopher MacMackin
70 | ! Date: April 2017
71 | !
72 | ! Instantiates an instance of a viscosity object implementing
73 | ! Glen's flow law.
74 | !
75 | real(r8), intent(in) :: b_val
76 | !! The coefficient, \(B\), in Glen's flow law.
77 | real(r8), intent(in) :: index
78 | !! The index, \(n\), in the exponent of Glen's flow law.
79 | type(glens_law_viscosity) :: this
80 | !! The viscosity object being created.
81 | this%b_val = b_val
82 | this%index = index
83 | end function constructor
84 |
85 | function glens_ice_viscosity(this, velocity, temperature, time) &
86 | result(viscosity)
87 | !* Author: Christopher MacMackin
88 | ! Date: April 2017
89 | !
90 | ! Calculates the viscosity of ice using Glen's flow law. See the
91 | ! documentation of the [[glens_law_viscosity]] object for a
92 | ! description of this parameterisation.
93 | !
94 | class(glens_law_viscosity), intent(in) :: this
95 | class(vector_field), intent(in) :: velocity
96 | !! The velocity field of the ice for which the velocity is
97 | !! being calculated
98 | real(r8), intent(in) :: temperature
99 | !! The temperature of the ice for which viscosity is being
100 | !! calculated.
101 | real(r8), intent(in), optional :: time
102 | !! The time at which the viscosity is being calculated. If not
103 | !! present then assumed to be same as previous value passed.
104 | class(scalar_field), pointer :: viscosity
105 | !! The value of the viscosity
106 | call velocity%guard_temp()
107 | if (velocity%dimensions() > 1) then
108 | error stop ('Multidimensional case not implemented')
109 | ! This should all work, but there seems to be yet another
110 | ! compiler bug. At present I don't need the multi-dimensional
111 | ! case anyway.
112 |
113 | ! viscosity => velocity%component_d_dx(1, 1)**2 + &
114 | ! velocity%component_d_dx(2, 2)**2 + &
115 | ! 0.25_r8*(velocity%component_d_dx(1, 2) + &
116 | ! velocity%component_d_dx(2, 1)**2)
117 | ! viscosity => sqrt(viscosity)
118 | else
119 | viscosity => abs(velocity%component_d_dx(1, 1, 1))
120 | end if
121 | viscosity => 0.5_r8*this%b_val*viscosity**(1._r8/this%index - 1._r8)
122 | call velocity%clean_temp()
123 | call viscosity%set_temp()
124 | end function glens_ice_viscosity
125 |
126 | end module glens_law_mod
127 |
--------------------------------------------------------------------------------
/src/parameterisations/jenkins1991_entrainment.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! jenkins1991_entrainment.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module jenkins1991_entrainment_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides a concrete implementation of [[abstract_entrainment]]
34 | ! in the form of the parameterisation used by Jenkins (1991).
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod!, only: scalar_field, vector_field, abs
38 | use entrainment_mod, only: abstract_entrainment
39 | implicit none
40 | private
41 |
42 | type, extends(abstract_entrainment), public :: jenkins1991_entrainment
43 | !* Author: Christopher MacMackin
44 | ! Date: October 2016
45 | !
46 | ! A parameterisation of entrainment (\(e\)) as described by Jenkins
47 | ! (1991): $$e = E_0 |\vec{U}\sin(\theta) \simeq
48 | ! E_0|\vec{U}||\nabla b|.$$ Here, \(E_0\) is a coefficient typically
49 | ! taken to be 0.036 (the default value), \(\vec{U}\) is the velocity
50 | ! of the plume, \(\theta\) is the angle of slope of the ice shelf
51 | ! base, and \(b\) is the basal depth of the ice shelf.
52 | !
53 | private
54 | real(r8) :: coefficient = 1.0_r8
55 | !! The entrainment coefficient $E_0$
56 | contains
57 | procedure :: entrainment_rate => jenkins1991_rate
58 | !! Returns the entrainment rate for ambient water into the plume.
59 | end type jenkins1991_entrainment
60 |
61 | interface jenkins1991_entrainment
62 | module procedure constructor
63 | end interface jenkins1991_entrainment
64 |
65 | contains
66 |
67 | pure function constructor(coefficient) result(this)
68 | real(r8), intent(in) :: coefficient
69 | !! The entrainment coefficient, $E_0$ to be used
70 | type(jenkins1991_entrainment) :: this
71 | !! A new entrainment object
72 | this%coefficient = coefficient
73 | end function constructor
74 |
75 | function jenkins1991_rate(this, velocity, thickness, depth, density_diff, time) &
76 | result(entrainment)
77 | !* Author: Christopher MacMackin
78 | ! Date: October 2016
79 | !
80 | ! $$e = E_0 |\vec{U}\sin(\theta) \simeq E_0|\vec{U}||\nabla b|$$
81 | ! Here, \(E_0\) is a coefficient typically taken to be 0.036 (the
82 | ! default value), \(\vec{U}\) is the velocity of the plume, \(\theta\)
83 | ! is the angle of slope of the ice shelf base, and \(b\) is the
84 | ! basal depth of the ice shelf.
85 | !
86 | ! @Warning
87 | ! The calculation must be performed as
88 | ! ```fortran
89 | ! this%coefficient * depth%d_dx(1) * velocity%norm()
90 | ! ```
91 | ! with the variables in a different order than how the equation is
92 | ! usually formulated. If they are in the correct order then
93 | ! `gfortran` expects the result to be a `vector_field`. It
94 | ! is not clear whether this is due to a bug in `gfortran` or in
95 | ! `factual`.
96 | !
97 | class(jenkins1991_entrainment), intent(in) :: this
98 | class(vector_field), intent(in) :: velocity
99 | !! The velocity field of the plume into which fluid is being
100 | !! entrained.
101 | class(scalar_field), intent(in) :: thickness
102 | !! The thickness of the plume into which fluid is being
103 | !! entrained
104 | class(scalar_field), intent(in) :: depth
105 | !! The depth of the upper surface of the plume into which
106 | !! fluid is being entrained
107 | class(scalar_field), intent(in) :: density_diff
108 | !! The difference between the ambient density and the density of
109 | !! the plume into which the ambient fluid is being entrained.
110 | real(r8), intent(in), optional :: time
111 | !! The time at which the entrainment is being calculated. If not
112 | !! present then assumed to be same as previous value passed.
113 | class(scalar_field), pointer :: entrainment
114 | !! The value of the entrainment
115 | class(vector_field), pointer :: tmp
116 | call velocity%guard_temp(); call thickness%guard_temp()
117 | call depth%guard_temp(); call density_diff%guard_temp()
118 | call depth%allocate_scalar_field(entrainment)
119 | call depth%allocate_vector_field(tmp)
120 | entrainment = velocity%norm()
121 | call entrainment%unset_temp()
122 | tmp = .grad. depth
123 | entrainment = tmp%norm() ! Needed due to ICE when try to put all on one line. TODO: Create minimal example and submit bug report.
124 | entrainment = this%coefficient * entrainment * velocity%norm()
125 | call velocity%clean_temp(); call thickness%clean_temp()
126 | call depth%clean_temp(); call density_diff%clean_temp()
127 | call entrainment%set_temp()
128 | end function jenkins1991_rate
129 |
130 | end module jenkins1991_entrainment_mod
131 |
--------------------------------------------------------------------------------
/src/parameterisations/kochergin1987_entrainment.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! kochergin1987_entrainment.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2018 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module kochergin1987_entrainment_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides a concrete implementation of [[abstract_entrainment]]
34 | ! in the form of the parameterisation described by Kochergin (1987).
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod!, only: scalar_field, vector_field, abs
38 | use entrainment_mod, only: abstract_entrainment
39 | implicit none
40 | private
41 |
42 | type, extends(abstract_entrainment), public :: kochergin1987_entrainment
43 | !* Author: Christopher MacMackin
44 | ! Date: Feburary 2018
45 | !
46 | ! A parameterisation of entrainment (\(e\)) as described by
47 | ! Kochergin (1987): $$e =
48 | ! \frac{c_L^2}{S_m}\sqrt{|\vec{U}|^2+\frac{g'D}{S_m}}.$$ Here,
49 | ! \(c_L\) is an entrainment coefficient, \(\vec{U}\) is the
50 | ! velocity of the plume, \(g'\) is the reduced gravity, and
51 | ! \(S_m\) is the turbulent Schmidt number. The latter-most can be
52 | ! expressed as $$ S_m = \frac{Ri}{0.0725(Ri + 0.186 -
53 | ! \sqrt{Ri^2 - 0.316Ri + 0.0346})} $$, where \(Ri =
54 | ! g'D/|\vec{U}|^2\) is the Richardson number.
55 | !
56 | private
57 | real(r8) :: coefficient = 1.0_r8
58 | !! The entrainment coefficient \(c_L^2x_0/D_0\)
59 | real(r8) :: delta = 0.036_r8
60 | !! The ratio \(D_0/h_0\)
61 | contains
62 | procedure :: entrainment_rate => kochergin1987_rate
63 | !! Returns the entrainment rate for ambient water into the plume.
64 | end type kochergin1987_entrainment
65 |
66 | interface kochergin1987_entrainment
67 | module procedure constructor
68 | end interface kochergin1987_entrainment
69 |
70 | contains
71 |
72 | pure function constructor(coefficient, delta) result(this)
73 | real(r8), intent(in) :: coefficient
74 | !! The entrainment coefficient \(c_L^2x_0/D_0\)
75 | real(r8), intent(in) :: delta
76 | !! The ratio \(D_0/h_0\)
77 | type(kochergin1987_entrainment) :: this
78 | !! A new entrainment object
79 | this%coefficient = coefficient
80 | this%delta = delta
81 | end function constructor
82 |
83 | function kochergin1987_rate(this, velocity, thickness, depth, density_diff, time) &
84 | result(entrainment)
85 | !* Author: Christopher MacMackin
86 | ! Date: Feburary 2018
87 | !
88 | ! $$e = \frac{c_L^2}{S_m}\sqrt{|\vec{U}|^2+\frac{g'D}{S_m}}.$$
89 | ! Here, \(c_L\) is an entrainment coefficient, \(\vec{U}\) is the
90 | ! velocity of the plume, \(g'\) is the reduced gravity, and
91 | ! \(S_m\) is the turbulent Schmidt number. The Schmidt number is a
92 | ! function of the Richardson number \(Ri = g'D/|\vec{U}|^2\):
93 | ! $$ S_m = \frac{Ri}{0.0725(Ri + 0.186 -
94 | ! \sqrt{Ri^2 - 0.316Ri + 0.0346})}. $$
95 | !
96 | class(kochergin1987_entrainment), intent(in) :: this
97 | class(vector_field), intent(in) :: velocity
98 | !! The velocity field of the plume into which fluid is being
99 | !! entrained.
100 | class(scalar_field), intent(in) :: thickness
101 | !! The thickness of the plume into which fluid is being
102 | !! entrained
103 | class(scalar_field), intent(in) :: depth
104 | !! The depth of the upper surface of the plume into which
105 | !! fluid is being entrained
106 | class(scalar_field), intent(in) :: density_diff
107 | !! The difference between the ambient density and the density of
108 | !! the plume into which the ambient fluid is being entrained.
109 | real(r8), intent(in), optional :: time
110 | !! The time at which the entrainment is being calculated. If not
111 | !! present then assumed to be same as previous value passed.
112 | class(scalar_field), pointer :: entrainment
113 | !! The value of the entrainment
114 | class(scalar_field), pointer :: Ri, Sm
115 | call velocity%guard_temp(); call thickness%guard_temp()
116 | call depth%guard_temp(); call density_diff%guard_temp()
117 | call depth%allocate_scalar_field(entrainment)
118 | call entrainment%unset_temp()
119 | call depth%allocate_scalar_field(Ri)
120 | call depth%allocate_scalar_field(Sm)
121 | call Ri%guard_temp(); call Sm%guard_temp()
122 | entrainment = velocity%norm() ! Have to awkwardly split this operation to prevent ICE
123 | Ri = this%delta*density_diff*thickness/(entrainment**2)
124 | Sm = Ri/(0.0725_r8*(Ri + 0.186_r8 - sqrt(Ri**2 - 0.316_r8*Ri + 0.0346_r8)))
125 | entrainment = this%coefficient*entrainment/Sm * sqrt(1._r8 + Ri/Sm)
126 | call velocity%clean_temp(); call thickness%clean_temp()
127 | call depth%clean_temp(); call density_diff%clean_temp()
128 | call Ri%clean_temp(); call Sm%clean_temp()
129 | call entrainment%set_temp()
130 | end function kochergin1987_rate
131 |
132 | end module kochergin1987_entrainment_mod
133 |
--------------------------------------------------------------------------------
/src/parameterisations/newtonian_viscosity.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! newtonian_viscosity.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module newtonian_viscosity_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides a simple concrete implementation for the
34 | ! [[abstract_viscosity]] type, for a Newtonian fluid.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field, uniform_scalar_field
38 | use viscosity_mod, only: abstract_viscosity
39 | implicit none
40 | private
41 |
42 | type, extends(abstract_viscosity), public :: newtonian_viscosity
43 | !* Author: Christopher MacMackin
44 | ! Date: October 2016
45 | !
46 | ! An implementation of Newtonian (constant) viscosity for a glacier.
47 | !
48 | private
49 | real(r8) :: viscosity_value = 1.0_r8
50 | contains
51 | procedure :: ice_viscosity => newtonian_ice_viscosity
52 | !! Returns the viscosity for the ice.
53 | end type newtonian_viscosity
54 |
55 | interface newtonian_viscosity
56 | module procedure constructor
57 | end interface newtonian_viscosity
58 |
59 | contains
60 |
61 | pure function constructor(viscosity_value) result(this)
62 | real(r8), intent(in) :: viscosity_value
63 | !! The numerical value of the viscosity which this type is meant
64 | !! to return.
65 | type(newtonian_viscosity) :: this
66 | !! The viscosity object being created.
67 | this%viscosity_value = viscosity_value
68 | end function constructor
69 |
70 | function newtonian_ice_viscosity(this, velocity, temperature, time) &
71 | result(viscosity)
72 | class(newtonian_viscosity), intent(in) :: this
73 | class(vector_field), intent(in) :: velocity
74 | !! The velocity field of the ice for which the velocity is
75 | !! being calculated
76 | real(r8), intent(in) :: temperature
77 | !! The temperature of the ice for which viscosity is being
78 | !! calculated.
79 | real(r8), intent(in), optional :: time
80 | !! The time at which the viscosity is being calculated. If not
81 | !! present then assumed to be same as previous value passed.
82 | class(scalar_field), pointer :: viscosity
83 | !! The value of the viscosity
84 | type(uniform_scalar_field) :: dummy
85 | call velocity%guard_temp()
86 | call dummy%allocate_scalar_field(viscosity)
87 | viscosity = uniform_scalar_field(this%viscosity_value)
88 | call viscosity%set_temp() ! Shouldn't need to call this, but for some
89 | ! reason being set as non-temporary when
90 | ! assignment subroutine returns.
91 | call velocity%clean_temp()
92 | end function newtonian_ice_viscosity
93 |
94 | end module newtonian_viscosity_mod
95 |
--------------------------------------------------------------------------------
/src/plume_boundary.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! plume_boundary.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module plume_boundary_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: September 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract derived type which can be subtyped in order to
34 | ! specify the boundary conditions for plume types.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field, uniform_scalar_field, &
38 | uniform_vector_field
39 | use boundary_types_mod, only: free_boundary
40 | implicit none
41 | private
42 |
43 | type, public :: plume_boundary
44 | !* Author: Chris MacMackin
45 | ! Date: September 2016
46 | !
47 | ! A type in which procedures for getting the boundary conditions
48 | ! of plumes are to be specified. The descendent types can contain
49 | ! whatever data is needed to compute the result.
50 | !
51 | ! This class effectively provides free boundary conditions. It's
52 | ! type-bound procedures should be overridden to provide case-specific
53 | ! conditions.
54 | !
55 | contains
56 | procedure :: thickness_bound_info => bound_info
57 | !! Indicates the type and depth of the thickness boundary at
58 | !! different locations.
59 | procedure :: velocity_bound_info => bound_info
60 | !! Indicates the type and depth of the thickness boundary at
61 | !! different locations.
62 | procedure :: temperature_bound_info => bound_info
63 | !! Indicates the type and depth of the thickness boundary at
64 | !! different locations.
65 | procedure :: salinity_bound_info => bound_info
66 | !! Indicates the type and depth of the thickness boundary at
67 | !! different locations.
68 | procedure :: thickness_bound => scalar_bound
69 | !! Produces a field containing the boundary conditions for plume
70 | !! thickness at the specified location.
71 | procedure :: velocity_bound => vector_bound
72 | !! Produces a field containing the boundary conditions for plume
73 | !! velocity at the specified location.
74 | procedure :: temperature_bound => scalar_bound
75 | !! Produces a field containing the boundary conditions for plume
76 | !! temperature at the specified location.
77 | procedure :: salinity_bound => scalar_bound
78 | !! Produces a field containing the boundary conditions for plume
79 | !! salinity at the specified location.
80 | procedure :: set_time
81 | !! Specifies the time at which to calculate the boundary
82 | !! conditions.
83 | end type plume_boundary
84 |
85 | contains
86 |
87 | subroutine bound_info(this, location, bound_type, bound_depth)
88 | !* Author: Chris MacMackin
89 | ! Date: March 2017
90 | !
91 | ! Provides information about the type of boundary, and the number
92 | ! of layers of data-points needed to describe it.
93 | !
94 | class(plume_boundary), intent(in) :: this
95 | integer, intent(in) :: location
96 | !! Which boundary information is to be provided for. The
97 | !! boundary will be the one normal to dimension of number
98 | !! `abs(boundary)`. If the argument is negative, then the lower
99 | !! boundary is returned. If positive, then the upper boundary is
100 | !! returned.
101 | integer, intent(out) :: bound_type
102 | !! An integer representing what sort of boundary condition is
103 | !! used. The integer value corresponding to each boundary type is
104 | !! specified in the [[boundary_types_mod]].
105 | integer, intent(out) :: bound_depth
106 | !! The number of layers of data-points needed to specify the
107 | !! boundary condition.
108 | bound_type = free_boundary
109 | bound_depth = 0
110 | end subroutine bound_info
111 |
112 | function scalar_bound(this, location)
113 | !* Author: Chris MacMackin
114 | ! Date: March 2017
115 | !
116 | ! Returns a field containing the boundary values for the specified
117 | ! boundary location.
118 | !
119 | class(plume_boundary), intent(in) :: this
120 | integer, intent(in) :: location
121 | !! Which boundary information is to be provided for. The
122 | !! boundary will be the one normal to dimension of number
123 | !! `abs(boundary)`. If the argument is negative, then the lower
124 | !! boundary is returned. If positive, then the upper boundary is
125 | !! returned.
126 | class(scalar_field), pointer :: scalar_bound
127 | type(uniform_scalar_field) :: dummy
128 | call dummy%allocate_scalar_field(scalar_bound)
129 | scalar_bound = uniform_scalar_field(0.0_r8)
130 | end function scalar_bound
131 |
132 | function vector_bound(this, location)
133 | !* Author: Chris MacMackin
134 | ! Date: March 2017
135 | !
136 | ! Returns a field containing the boundary values for the specified
137 | ! boundary location.
138 | !
139 | class(plume_boundary), intent(in) :: this
140 | integer, intent(in) :: location
141 | !! Which boundary information is to be provided for. The
142 | !! boundary will be the one normal to dimension of number
143 | !! `abs(boundary)`. If the argument is negative, then the lower
144 | !! boundary is returned. If positive, then the upper boundary is
145 | !! returned.
146 | class(vector_field), pointer :: vector_bound
147 | type(uniform_scalar_field) :: dummy
148 | call dummy%allocate_vector_field(vector_bound)
149 | vector_bound = uniform_vector_field([0.0_r8,0.0_r8])
150 | end function vector_bound
151 |
152 | subroutine set_time(this, time)
153 | !* Author: Chris MacMackin
154 | ! Date: May 2017
155 | !
156 | ! Sets the time at which boundary conditions are to be calculated.
157 | !
158 | class(plume_boundary), intent(inout) :: this
159 | real(r8), intent(in) :: time
160 | end subroutine set_time
161 |
162 | end module plume_boundary_mod
163 |
--------------------------------------------------------------------------------
/src/splev.f:
--------------------------------------------------------------------------------
1 | subroutine splev(t,n,c,k,x,y,m,e,ier)
2 | c subroutine splev evaluates in a number of points x(i),i=1,2,...,m
3 | c a spline s(x) of degree k, given in its b-spline representation.
4 | c
5 | c calling sequence:
6 | c call splev(t,n,c,k,x,y,m,e,ier)
7 | c
8 | c input parameters:
9 | c t : array,length n, which contains the position of the knots.
10 | c n : integer, giving the total number of knots of s(x).
11 | c c : array,length n, which contains the b-spline coefficients.
12 | c k : integer, giving the degree of s(x).
13 | c x : array,length m, which contains the points where s(x) must
14 | c be evaluated.
15 | c m : integer, giving the number of points where s(x) must be
16 | c evaluated.
17 | c e : integer, if 0 the spline is extrapolated from the end
18 | c spans for points not in the support, if 1 the spline
19 | c evaluates to zero for those points, if 2 ier is set to
20 | c 1 and the subroutine returns, and if 3 the spline evaluates
21 | c to the value of the nearest boundary point.
22 | c
23 | c output parameter:
24 | c y : array,length m, giving the value of s(x) at the different
25 | c points.
26 | c ier : error flag
27 | c ier = 0 : normal return
28 | c ier = 1 : argument out of bounds and e == 2
29 | c ier =10 : invalid input data (see restrictions)
30 | c
31 | c restrictions:
32 | c m >= 1
33 | c-- t(k+1) <= x(i) <= x(i+1) <= t(n-k) , i=1,2,...,m-1.
34 | c
35 | c other subroutines required: fpbspl.
36 | c
37 | c references :
38 | c de boor c : on calculating with b-splines, j. approximation theory
39 | c 6 (1972) 50-62.
40 | c cox m.g. : the numerical evaluation of b-splines, j. inst. maths
41 | c applics 10 (1972) 134-149.
42 | c dierckx p. : curve and surface fitting with splines, monographs on
43 | c numerical analysis, oxford university press, 1993.
44 | c
45 | c author :
46 | c p.dierckx
47 | c dept. computer science, k.u.leuven
48 | c celestijnenlaan 200a, b-3001 heverlee, belgium.
49 | c e-mail : Paul.Dierckx@cs.kuleuven.ac.be
50 | c
51 | c latest update : march 1987
52 | c
53 | c++ pearu: 11 aug 2003
54 | c++ - disabled cliping x values to interval [min(t),max(t)]
55 | c++ - removed the restriction of the orderness of x values
56 | c++ - fixed initialization of sp to double precision value
57 | c
58 | c ..scalar arguments..
59 | integer n, k, m, e, ier
60 | c ..array arguments..
61 | real*8 t(n), c(n), x(m), y(m)
62 | c ..local scalars..
63 | integer i, j, k1, l, ll, l1, nk1
64 | c++..
65 | integer k2
66 | c..++
67 | real*8 arg, sp, tb, te
68 | c ..local array..
69 | real*8 h(20)
70 | c ..
71 | c before starting computations a data check is made. if the input data
72 | c are invalid control is immediately repassed to the calling program.
73 | ier = 10
74 | c-- if(m-1) 100,30,10
75 | c++..
76 | if (m .lt. 1) go to 100
77 | c..++
78 | c-- 10 do 20 i=2,m
79 | c-- if(x(i).lt.x(i-1)) go to 100
80 | c-- 20 continue
81 | ier = 0
82 | c fetch tb and te, the boundaries of the approximation interval.
83 | k1 = k + 1
84 | c++..
85 | k2 = k1 + 1
86 | c..++
87 | nk1 = n - k1
88 | tb = t(k1)
89 | te = t(nk1 + 1)
90 | l = k1
91 | l1 = l + 1
92 | c main loop for the different points.
93 | do 80 i = 1, m
94 | c fetch a new x-value arg.
95 | arg = x(i)
96 | c check if arg is in the support
97 | if (arg .lt. tb .or. arg .gt. te) then
98 | if (e .eq. 0) then
99 | goto 35
100 | else if (e .eq. 1) then
101 | y(i) = 0
102 | goto 80
103 | else if (e .eq. 2) then
104 | ier = 1
105 | goto 100
106 | else if (e .eq. 3) then
107 | if (arg .lt. tb) then
108 | arg = tb
109 | else
110 | arg = te
111 | endif
112 | endif
113 | endif
114 | c search for knot interval t(l) <= arg < t(l+1)
115 | c++..
116 | 35 if (arg .ge. t(l) .or. l1 .eq. k2) go to 40
117 | l1 = l
118 | l = l - 1
119 | go to 35
120 | c..++
121 | 40 if(arg .lt. t(l1) .or. l .eq. nk1) go to 50
122 | l = l1
123 | l1 = l + 1
124 | go to 40
125 | c evaluate the non-zero b-splines at arg.
126 | 50 call fpbspl(t, n, k, arg, l, h)
127 | c find the value of s(x) at x=arg.
128 | sp = 0.0d0
129 | ll = l - k1
130 | do 60 j = 1, k1
131 | ll = ll + 1
132 | sp = sp + c(ll)*h(j)
133 | 60 continue
134 | y(i) = sp
135 | 80 continue
136 | 100 return
137 | end
138 | c
139 | c
140 | c
141 | subroutine fpbspl(t,n,k,x,l,h)
142 | c subroutine fpbspl evaluates the (k+1) non-zero b-splines of
143 | c degree k at t(l) <= x < t(l+1) using the stable recurrence
144 | c relation of de boor and cox.
145 | c Travis Oliphant 2007
146 | c changed so that weighting of 0 is used when knots with
147 | c multiplicity are present.
148 | c Also, notice that l+k <= n and 1 <= l+1-k
149 | c or else the routine will be accessing memory outside t
150 | c Thus it is imperative that that k <= l <= n-k but this
151 | c is not checked.
152 | c ..
153 | c ..scalar arguments..
154 | real*8 x
155 | integer n,k,l
156 | c ..array arguments..
157 | real*8 t(n),h(20)
158 | c ..local scalars..
159 | real*8 f,one
160 | integer i,j,li,lj
161 | c ..local arrays..
162 | real*8 hh(19)
163 | c ..
164 | one = 0.1d+01
165 | h(1) = one
166 | do 20 j=1,k
167 | do 10 i=1,j
168 | hh(i) = h(i)
169 | 10 continue
170 | h(1) = 0.0d0
171 | do 20 i=1,j
172 | li = l+i
173 | lj = li-j
174 | if (t(li).ne.t(lj)) goto 15
175 | h(i+1) = 0.0d0
176 | goto 20
177 | 15 f = hh(i)/(t(li)-t(lj))
178 | h(i) = h(i)+f*(t(li)-x)
179 | h(i+1) = f*(x-t(lj))
180 | 20 continue
181 | return
182 | end
183 |
--------------------------------------------------------------------------------
/src/viscosity.F90:
--------------------------------------------------------------------------------
1 | !
2 | ! viscosity.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | #ifdef DEBUG
24 | #define pure
25 | #define elemental
26 | #endif
27 |
28 | module viscosity_mod
29 | !* Author: Christopher MacMackin
30 | ! Date: October 2016
31 | ! License: GPLv3
32 | !
33 | ! Provides an abstract data type to model viscosity into a
34 | ! vertically integrated plume.
35 | !
36 | use iso_fortran_env, only: r8 => real64
37 | use factual_mod, only: scalar_field, vector_field
38 | implicit none
39 | private
40 |
41 | type, abstract, public :: abstract_viscosity
42 | !* Author: Christopher MacMackin
43 | ! Date: October 2016
44 | !
45 | ! An abstract data type for calculating viscosity of a vertically
46 | ! integrated [[glacier(type)]].
47 | !
48 | contains
49 | procedure(get_viscosity), deferred :: ice_viscosity
50 | !! Returns the viscosity for the ice.
51 | end type abstract_viscosity
52 |
53 | abstract interface
54 | function get_viscosity(this, velocity, temperature, time) result(property)
55 | import :: abstract_viscosity
56 | import :: vector_field
57 | import :: scalar_field
58 | import :: r8
59 | class(abstract_viscosity), intent(in) :: this
60 | class(vector_field), intent(in) :: velocity
61 | !! The velocity field of the ice for which the velocity is
62 | !! being calculated
63 | real(r8), intent(in) :: temperature
64 | !! The temperature of the ice for which viscosity is being
65 | !! calculated.
66 | real(r8), intent(in), optional :: time
67 | !! The time at which the viscosity is being calculated. If not
68 | !! present then assumed to be same as previous value passed.
69 | class(scalar_field), pointer :: property
70 | !! The value of the viscosity
71 | end function get_viscosity
72 | end interface
73 |
74 | end module viscosity_mod
75 |
--------------------------------------------------------------------------------
/tests/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((nil . ((eval . (set (make-local-variable 'my-project-path)
2 | (file-name-directory
3 | (let ((d (dir-locals-find-file ".")))
4 | (if (stringp d) d (car d))))))
5 | (eval . (message "Project directory set to `%s'." my-project-path))
6 | (eval . (setq flycheck-gfortran-include-path
7 | (mapcar (lambda (p) (expand-file-name p my-project-path))
8 | '("~/.local/include/"
9 | "../mod"
10 | "../factual/mod"
11 | "./mod"))))
12 | (eval . (message "Include directories set to `%s'." flycheck-gfortran-include-path))
13 | (eval . (setq flycheck-gfortran-args
14 | (concat "-J"
15 | (expand-file-name '"./mod" my-project-path))))
16 | (eval . (message "Gfortran arguments set to `%s'." flycheck-gfortran-args))
17 | )))
18 |
--------------------------------------------------------------------------------
/tests/ambient_test.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! nitsol_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | !!$module ambient_test
24 | !!$ use iso_fortran_env, only: r8 => real64
25 | !!$ use pfunit_mod
26 | !!$ use factual_mod, only: scalar_field, vector_field, cheb1d_scalar_field, &
27 | !!$ cheb1d_vector_field
28 | !!$ use ambient_mod, only: ambient_conditions
29 | !!$
30 | !!$ type, extends(testcase), public :: test_amb
31 | !!$ type(ambient_conditions) :: amb1, amb2
32 | !!$ contains
33 | !!$ procedure :: setup
34 | !!$ end type test_amb
35 | !!$
36 | !!$contains
37 | !!$
38 | !!$ subroutine setup(this)
39 | !!$ class(test_amb), intent(inout) :: this
40 | !!$ end subroutine setup
41 | !!$
42 | !!$end module ambient_test
43 |
--------------------------------------------------------------------------------
/tests/bc_test.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! bc_test.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module bc_test
24 | use pfunit_mod
25 | use factual_mod, only: cheb1d_scalar_field, cheb1d_vector_field, uniform_scalar_field, &
26 | uniform_vector_field
27 | use plume_boundary_mod, only: plume_boundary
28 | use glacier_boundary_mod, only: glacier_boundary
29 | use boundary_types_mod, only: free_boundary
30 | use iso_fortran_env, only: r8 => real64
31 | implicit none
32 |
33 | contains
34 |
35 | @Test
36 | subroutine test_defaults()
37 | type(plume_boundary) :: test_plume
38 | type(glacier_boundary) :: test_glacier
39 | type(cheb1d_scalar_field) :: scalar
40 | type(cheb1d_vector_field) :: vector
41 | type(uniform_scalar_field) :: uscalar
42 | type(uniform_vector_field) :: uvector
43 | integer :: i, btype, bdepth
44 |
45 | do i = -3, 3
46 | btype = -500
47 | bdepth = -500
48 | call test_plume%thickness_bound_info(i,btype,bdepth)
49 | @assertEqual(free_boundary, btype, message='Incorrect boundary type returned for plume thickness.')
50 | @assertEqual(0, bdepth, message='Incorrect boundary depth returned for plume thickness.')
51 | uscalar = test_plume%thickness_bound(i)
52 | @assertEqual(0.0_r8, uscalar%get_value(), message='Nonzero thickness boundary field returned.')
53 | end do
54 | do i = -3, 3
55 | btype = -500
56 | bdepth = -500
57 | call test_plume%velocity_bound_info(i,btype,bdepth)
58 | @assertEqual(free_boundary, btype, message='Incorrect boundary type returned for plume velocity.')
59 | @assertEqual(0, bdepth, message='Incorrect boundary depth returned for plume velocity.')
60 | uvector = test_plume%velocity_bound(i)
61 | @assertEqual([0.0_r8,0.0_r8], uvector%get_value(), message='Nonzero velocity boundary field returned.')
62 | end do
63 | do i = -3, 3
64 | btype = -500
65 | bdepth = -500
66 | call test_plume%temperature_bound_info(i,btype,bdepth)
67 | @assertEqual(free_boundary, btype, message='Incorrect boundary type returned for plume temperature.')
68 | @assertEqual(0, bdepth, message='Incorrect boundary depth returned for plume temperature.')
69 | uscalar = test_plume%temperature_bound(i)
70 | @assertEqual(0.0_r8, uscalar%get_value(), message='Nonzero temperature boundary field returned.')
71 | end do
72 | do i = -3, 3
73 | btype = -500
74 | bdepth = -500
75 | call test_plume%thickness_bound_info(i,btype,bdepth)
76 | @assertEqual(free_boundary, btype, message='Incorrect boundary type returned for plume thickness.')
77 | @assertEqual(0, bdepth, message='Incorrect boundary depth returned for plume thickness.')
78 | uscalar = test_plume%salinity_bound(i)
79 | @assertEqual(0.0_r8, uscalar%get_value(), message='Nonzero salinity boundary field returned.')
80 | end do
81 |
82 | @assertEqual([0,0],test_glacier%thickness_lower_bound(),message='Incorrect lower bounds returned for thickness.')
83 | @assertEqual([0,0],test_glacier%thickness_upper_bound(),message='Incorrect upper bounds returned for thickness.')
84 | @assertEqual([0,0],test_glacier%velocity_lower_bound(),message='Incorrect lower bounds returned for velocity.')
85 | @assertEqual([0,0],test_glacier%velocity_upper_bound(),message='Incorrect upper bounds returned for velocity.')
86 | @assertEqual(0,size(test_glacier%boundary_residuals(scalar,vector,scalar,0.0_r8)),message='Non-empty residual.')
87 | end subroutine test_defaults
88 |
89 | end module bc_test
90 |
--------------------------------------------------------------------------------
/tests/coriolis_block_test.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! coriolis_block_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2018 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module coriolis_block_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use factual_mod, only: scalar_field, cheb1d_scalar_field, cheb1d_vector_field
27 | use coriolis_block_mod, only: coriolis_block
28 | implicit none
29 |
30 | integer, parameter :: resolution = 50
31 |
32 | @TestCase
33 | type, extends(testcase), public :: test_block
34 | type(cheb1d_vector_field) :: vel, vel_dx, vel_out, vel_dx_out
35 | real(r8) :: phi, nu
36 | contains
37 | procedure :: setup
38 | end type test_block
39 |
40 | contains
41 |
42 | pure function g(location)
43 | real(r8), dimension(:), intent(in) :: location
44 | real(r8), dimension(:), allocatable :: g
45 | allocate(g(2))
46 | g(1) = exp(location(1)) - location(1)
47 | g(2) = 1._r8 + location(1)
48 | end function g
49 |
50 | pure function g_dx(location)
51 | real(r8), dimension(:), intent(in) :: location
52 | real(r8), dimension(:), allocatable :: g_dx
53 | allocate(g_dx(2))
54 | g_dx(1) = -1._r8
55 | g_dx(2) = 1._r8
56 | end function g_dx
57 |
58 | subroutine setup(this)
59 | class(test_block), intent(inout) :: this
60 | this%phi = -1.e-4_r8
61 | this%nu = 3.69e-5_r8
62 | this%vel = cheb1d_vector_field(resolution, g, 0.0000_r8, 6._r8, 1)
63 | this%vel_dx = cheb1d_vector_field(resolution, g_dx, 0.0000_r8, 6._r8, 1)
64 | this%vel_out = this%vel%d_dx(1) - this%vel_dx
65 | call this%vel_out%set_boundary(1, 1, this%vel%get_boundary(-1, 1))
66 | this%vel_dx_out = [0._r8, 0._r8, this%phi/this%nu] .cross. this%vel
67 | this%vel_dx_out = this%vel_dx%d_dx(1) - this%vel_dx_out
68 | call this%vel_dx_out%set_boundary(1, 1, this%vel_dx%get_boundary(1, 1))
69 | end subroutine setup
70 |
71 | @Test
72 | subroutine test_coriolis_solve(this)
73 | class(test_block), intent(inout) :: this
74 | type(coriolis_block) :: block1
75 | type(cheb1d_scalar_field) :: scal
76 | @assertFalse(all(this%vel%raw()==this%vel_out%raw()),message='Fields to be preconditioned same as expected result.')
77 | @assertFalse(all(this%vel_dx%raw()==this%vel_dx_out%raw()),message='Fields to be preconditioned same as expected result.')
78 | scal = this%vel%component(1)
79 | block1 = coriolis_block(this%phi, this%nu, -1, 1, 1, scal)
80 | call block1%solve_for(this%vel_out, this%vel_dx_out)
81 | @assertEqual(this%vel%raw(),this%vel_out%raw(),tolerance=1e-9_r8,message='Coriolis block returns wrong sol.')
82 | @assertEqual(this%vel_dx%raw(),this%vel_dx_out%raw(),tolerance=1e-9_r8,message='Coriolis block returns wrong sol.')
83 | end subroutine test_coriolis_solve
84 |
85 | end module coriolis_block_test
86 |
--------------------------------------------------------------------------------
/tests/ground_test.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! ground_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module ground_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use test_utils, only: test_field
27 | use ground_mod, only: ground
28 | use factual_mod, only: scalar_field, vector_field, cheb1d_scalar_field, &
29 | cheb1d_vector_field
30 | implicit none
31 |
32 | integer, parameter :: resolution = 50
33 | real(r8), parameter :: big_x = 2.702702703
34 | real(r8), parameter :: ice_density = 0.889320388_r8
35 |
36 | @TestCase
37 | type, extends(testcase), public :: test_ground
38 | type(ground) :: ground1, ground2
39 | integer :: nodes = resolution
40 | real(r8) :: lower1 = 0.0_r8, upper1 = 2.7_r8
41 | real(r8) :: lower2 = -1.0_r8, upper2 = 1.0_r8
42 | real(r8) :: delta, nu, mu, sigma
43 | contains
44 | procedure :: setup
45 | end type test_ground
46 |
47 | contains
48 |
49 | subroutine setup(this)
50 | class(test_ground), intent(inout) :: this
51 | end subroutine setup
52 |
53 | @Test
54 | subroutine test_basal_melt(this)
55 | !! Test that the ground calculates teh correct basal melt rate
56 | !! given how it was initialised.
57 | class(test_ground), intent(inout) :: this
58 | type(cheb1d_scalar_field) :: actual_thickness
59 | type(cheb1d_scalar_field) :: expected_thickness
60 | end subroutine test_basal_melt
61 |
62 | @Test
63 | subroutine test_basal_drag(this)
64 | !! Test that the ground calculates teh correct basal drag coefficient
65 | !! given how it was initialised.
66 | class(test_ground), intent(inout) :: this
67 | type(cheb1d_scalar_field) :: actual_drag
68 | type(cheb1d_scalar_field) :: expected_drag
69 | end subroutine test_basal_drag
70 |
71 | @Test
72 | subroutine test_water_density(this)
73 | !! Test that the ground returns the water density for which it was
74 | !! initialised.
75 | class(test_ground), intent(inout) :: this
76 | end subroutine test_water_density
77 |
78 | @Test
79 | subroutine test_residual(this)
80 | !! Test the residual for the analytical steady state XXX
81 | class(test_ground), intent(inout) :: this
82 | real(r8), dimension(:), allocatable :: actual, expected
83 | type(cheb1d_scalar_field) :: thickness
84 | real(r8) :: density, temperature
85 | end subroutine test_residual
86 |
87 | @Test
88 | subroutine test_update(this)
89 | !! Test two ground objects have the same state vectors after one is
90 | !! updated with the state vector of the other.
91 | class(test_ground), intent(inout) :: this
92 | real(r8), dimension(:), allocatable :: state_vector
93 | end subroutine test_update
94 |
95 | end module ground_test
96 |
--------------------------------------------------------------------------------
/tests/jenkins1991_entrainment.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! jenkins1991_entrainment_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module jenkins1991_entrainment_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use factual_mod, only: scalar_field, vector_field, cheb1d_scalar_field, &
27 | cheb1d_vector_field
28 | use jenkins1991_entrainment_mod, only: jenkins1991_entrainment
29 | implicit none
30 |
31 | integer, parameter :: resolution = 50
32 | real(r8), parameter :: lambda_d = 0.37_r8
33 | real(r8), parameter :: big_x = 1.0_r8/lambda_d
34 |
35 | @TestCase
36 | type, extends(testcase), public :: test_entrain
37 | type(jenkins1991_entrainment) :: ent1, ent2
38 | type(cheb1d_scalar_field) :: thickness, depth, dens_diff
39 | type(cheb1d_vector_field) :: velocity1, velocity2
40 | real(r8) :: time1, time2, coef1, coef2
41 | integer :: numpoints = resolution
42 | contains
43 | procedure :: setup
44 | end type test_entrain
45 |
46 | contains
47 |
48 | pure function depth(location)
49 | real(r8), dimension(:), intent(in) :: location
50 | real(r8) :: depth
51 | depth = 1._r8 - exp(location(1))
52 | end function depth
53 |
54 | pure function depth_dx(location)
55 | real(r8), dimension(:), intent(in) :: location
56 | real(r8) :: depth_dx
57 | depth_dx = -exp(location(1))
58 | end function depth_dx
59 |
60 | pure function thickness(location)
61 | real(r8), dimension(:), intent(in) :: location
62 | real(r8) :: thickness
63 | thickness = location(1)**2
64 | end function thickness
65 |
66 | pure function thickness_dx(location)
67 | real(r8), dimension(:), intent(in) :: location
68 | real(r8) :: thickness_dx
69 | thickness_dx = 2.0_r8*location(1)
70 | end function thickness_dx
71 |
72 | pure function velocity1(location) result(velocity)
73 | real(r8), dimension(:), intent(in) :: location
74 | real(r8), dimension(:), allocatable :: velocity
75 | allocate(velocity(1))
76 | velocity(1) = velocity1_norm(location)
77 | end function velocity1
78 |
79 | pure function velocity1_norm(location) result(velocity)
80 | real(r8), dimension(:), intent(in) :: location
81 | real(r8) :: velocity
82 | velocity = sqrt(1._r8 + big_x - big_x*(1._r8 - location(1)/big_x)**2)
83 | end function velocity1_norm
84 |
85 | pure function velocity2(location) result(velocity)
86 | real(r8), dimension(:), intent(in) :: location
87 | real(r8), dimension(:), allocatable :: velocity
88 | allocate(velocity(2))
89 | velocity(1) = sin(location(1)/6.283185307_r8)
90 | velocity(2) = cos(location(1)/6.283185307_r8)
91 | end function velocity2
92 |
93 | pure function velocity2_norm(location) result(velocity)
94 | real(r8), dimension(:), intent(in) :: location
95 | real(r8), allocatable :: velocity
96 | velocity = 1.0_r8
97 | end function velocity2_norm
98 |
99 | subroutine setup(this)
100 | class(test_entrain), intent(inout) :: this
101 | this%thickness = cheb1d_scalar_field(this%numpoints,thickness,0._r8,big_x)
102 | this%depth = cheb1d_scalar_field(this%numpoints,depth,0._r8,big_x)
103 | this%velocity1 = cheb1d_vector_field(this%numpoints,velocity1,0._r8,big_x)
104 | this%velocity2 = cheb1d_vector_field(this%numpoints,velocity2,0._r8,big_x,extra_dims=1)
105 | this%time1 = 1.0d5
106 | this%time2 = -5.7_r8
107 | this%coef1 = 1.0_r8
108 | this%coef2 = -3.5_r8
109 | this%ent1 = jenkins1991_entrainment(this%coef1)
110 | this%ent2 = jenkins1991_entrainment(this%coef2)
111 | end subroutine setup
112 |
113 | @Test
114 | subroutine test_entrainment_values(this)
115 | class(test_entrain), intent(inout) :: this
116 | type(cheb1d_scalar_field) :: actual, expected
117 | actual = this%ent1%entrainment_rate(this%velocity1,this%thickness,this%depth, &
118 | this%dens_diff,this%time1)
119 | expected = cheb1d_scalar_field(this%numpoints,ent1,0._r8,big_x)
120 | @assertTrue(actual==expected,message='Incorrect entrainment rate calculated.')
121 | actual = this%ent2%entrainment_rate(this%velocity2,this%depth,this%thickness, &
122 | this%dens_diff,this%time2)
123 | expected = cheb1d_scalar_field(this%numpoints,ent2,0._r8,big_x)
124 | @assertTrue(actual==expected,message='Incorrect entrainment rate calculated.')
125 | contains
126 | pure function ent1(location)
127 | real(r8), dimension(:), intent(in) :: location
128 | real(r8) :: ent1
129 | ent1 = this%coef1*velocity1_norm(location)*abs(depth_dx(location))
130 | end function ent1
131 |
132 | pure function ent2(location)
133 | real(r8), dimension(:), intent(in) :: location
134 | real(r8) :: ent2
135 | ent2 = this%coef2*velocity2_norm(location)*abs(thickness_dx(location))
136 | end function ent2
137 | end subroutine test_entrainment_values
138 |
139 | end module jenkins1991_entrainment_test
140 |
--------------------------------------------------------------------------------
/tests/kochergin1987_entrainment.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! kochergin1987_entrainment_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module kochergin1987_entrainment_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use factual_mod, only: scalar_field, vector_field, cheb1d_scalar_field, &
27 | cheb1d_vector_field
28 | use kochergin1987_entrainment_mod, only: kochergin1987_entrainment
29 | implicit none
30 |
31 | integer, parameter :: resolution = 50
32 | real(r8), parameter :: lambda_d = 0.37_r8
33 | real(r8), parameter :: big_x = 1.0_r8/lambda_d
34 |
35 | @TestCase
36 | type, extends(testcase), public :: test_entrain
37 | type(kochergin1987_entrainment) :: ent1, ent2
38 | type(cheb1d_scalar_field) :: thickness, depth, dens_diff
39 | type(cheb1d_vector_field) :: velocity1, velocity2
40 | real(r8) :: time1, time2, coef1, coef2, delta
41 | integer :: numpoints = resolution
42 | contains
43 | procedure :: setup
44 | end type test_entrain
45 |
46 | contains
47 |
48 | pure function depth(location)
49 | real(r8), dimension(:), intent(in) :: location
50 | real(r8) :: depth
51 | depth = 1.1_r8 - exp(location(1))
52 | end function depth
53 |
54 | pure function depth_dx(location)
55 | real(r8), dimension(:), intent(in) :: location
56 | real(r8) :: depth_dx
57 | depth_dx = -exp(location(1))
58 | end function depth_dx
59 |
60 | pure function thickness(location)
61 | real(r8), dimension(:), intent(in) :: location
62 | real(r8) :: thickness
63 | thickness = location(1)**2 + 1.0_r8
64 | end function thickness
65 |
66 | pure function thickness_dx(location)
67 | real(r8), dimension(:), intent(in) :: location
68 | real(r8) :: thickness_dx
69 | thickness_dx = 2.0_r8*location(1)
70 | end function thickness_dx
71 |
72 | pure function velocity1(location) result(velocity)
73 | real(r8), dimension(:), intent(in) :: location
74 | real(r8), dimension(:), allocatable :: velocity
75 | allocate(velocity(1))
76 | velocity(1) = velocity1_norm(location)
77 | end function velocity1
78 |
79 | pure function velocity1_norm(location) result(velocity)
80 | real(r8), dimension(:), intent(in) :: location
81 | real(r8) :: velocity
82 | velocity = sqrt(1._r8 + big_x - big_x*(1._r8 - location(1)/big_x)**2)
83 | end function velocity1_norm
84 |
85 | pure function velocity2(location) result(velocity)
86 | real(r8), dimension(:), intent(in) :: location
87 | real(r8), dimension(:), allocatable :: velocity
88 | allocate(velocity(2))
89 | velocity(1) = sin(location(1)/6.283185307_r8)
90 | velocity(2) = cos(location(1)/6.283185307_r8)
91 | end function velocity2
92 |
93 | pure function velocity2_norm(location) result(velocity)
94 | real(r8), dimension(:), intent(in) :: location
95 | real(r8), allocatable :: velocity
96 | velocity = 1.0_r8
97 | end function velocity2_norm
98 |
99 | pure function rho_diff(location) result(diff)
100 | real(r8), dimension(:), intent(in) :: location
101 | real(r8) :: diff
102 | diff = 0.01_r8*exp(-location(1))
103 | end function rho_diff
104 |
105 | pure function Ri(diff, D, Unorm, delta)
106 | real(r8), intent(in) :: diff, D, Unorm, delta
107 | real(r8) :: Ri
108 | Ri = delta*diff*D/Unorm**2
109 | end function Ri
110 |
111 | pure function Sm(Ri_num)
112 | real(r8), intent(in) :: Ri_num
113 | real(r8) :: Sm
114 | Sm = Ri_num/(0.0725_r8*(Ri_num + 0.186_r8 &
115 | - sqrt(Ri_num**2 - 0.316_r8*Ri_num + 0.0346_r8)))
116 | end function Sm
117 |
118 | subroutine setup(this)
119 | class(test_entrain), intent(inout) :: this
120 | this%thickness = cheb1d_scalar_field(this%numpoints,thickness,0._r8,big_x)
121 | this%depth = cheb1d_scalar_field(this%numpoints,depth,0._r8,big_x)
122 | this%dens_diff = cheb1d_scalar_field(this%numpoints,rho_diff,0._r8,big_x)
123 | this%velocity1 = cheb1d_vector_field(this%numpoints,velocity1,0._r8,big_x)
124 | this%velocity2 = cheb1d_vector_field(this%numpoints,velocity2,0._r8,big_x,extra_dims=1)
125 | this%time1 = 1.0d5
126 | this%time2 = -5.7_r8
127 | this%coef1 = 1.0_r8
128 | this%coef2 = -3.5_r8
129 | this%delta = 0.034
130 | this%ent1 = kochergin1987_entrainment(this%coef1, this%delta)
131 | this%ent2 = kochergin1987_entrainment(this%coef2, this%delta)
132 | end subroutine setup
133 |
134 | @Test
135 | subroutine test_entrainment_values(this)
136 | class(test_entrain), intent(inout) :: this
137 | type(cheb1d_scalar_field) :: actual, expected
138 | actual = this%ent1%entrainment_rate(this%velocity1,this%thickness,this%depth, &
139 | this%dens_diff,this%time1)
140 | expected = cheb1d_scalar_field(this%numpoints,ent1,0._r8,big_x)
141 | @assertTrue(actual==expected,message='Incorrect entrainment rate calculated.')
142 | actual = this%ent2%entrainment_rate(this%velocity2,this%depth,this%thickness, &
143 | this%dens_diff,this%time2)
144 | expected = cheb1d_scalar_field(this%numpoints,ent2,0._r8,big_x)
145 | @assertTrue(actual==expected,message='Incorrect entrainment rate calculated.')
146 | contains
147 | pure function ent1(location)
148 | real(r8), dimension(:), intent(in) :: location
149 | real(r8) :: ent1
150 | real(r8) :: Sm_val, Unorm_val, diff_val, D_val
151 | Unorm_val = velocity1_norm(location)
152 | D_val = thickness(location)
153 | diff_val = rho_diff(location)
154 | Sm_val = Sm(Ri(diff_val, D_val, Unorm_val, this%delta))
155 | ent1 = this%coef1/Sm_val * sqrt(Unorm_val**2 + this%delta*diff_val*D_val/Sm_val)
156 | end function ent1
157 |
158 | pure function ent2(location)
159 | real(r8), dimension(:), intent(in) :: location
160 | real(r8) :: ent2
161 | real(r8) :: Sm_val, Unorm_val, diff_val, D_val
162 | Unorm_val = velocity2_norm(location)
163 | D_val = depth(location)
164 | diff_val = rho_diff(location)
165 | Sm_val = Sm(Ri(diff_val, D_val, Unorm_val, this%delta))
166 | ent2 = this%coef2/Sm_val * sqrt(Unorm_val**2 + this%delta*diff_val*D_val/Sm_val)
167 | end function ent2
168 | end subroutine test_entrainment_values
169 |
170 | end module kochergin1987_entrainment_test
171 |
--------------------------------------------------------------------------------
/tests/newtonian_viscosity.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! newtonian_viscosity.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module newtonian_viscosity_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use factual_mod, only: scalar_field, vector_field, cheb1d_scalar_field, &
27 | cheb1d_vector_field, uniform_scalar_field
28 | use newtonian_viscosity_mod, only: newtonian_viscosity
29 | use test_utils, only: test_field
30 | implicit none
31 |
32 | integer, parameter :: resolution = 50
33 | real(r8), parameter :: lambda_d = 0.37_r8
34 | real(r8), parameter :: big_x = 1.0_r8/lambda_d
35 |
36 | @TestCase
37 | type, extends(testcase), public :: viscosity_test
38 | type(newtonian_viscosity) :: visc1, visc2
39 | type(cheb1d_vector_field) :: velocity1, velocity2
40 | real(r8) :: time1, time2, temp1, temp2
41 | real(r8) :: viscval1, viscval2
42 | integer :: numpoints = resolution
43 | contains
44 | procedure :: setup
45 | end type viscosity_test
46 |
47 | contains
48 |
49 | pure function velocity1(location) result(velocity)
50 | real(r8), dimension(:), intent(in) :: location
51 | real(r8), dimension(:), allocatable :: velocity
52 | allocate(velocity(1))
53 | velocity(1) = sqrt(1._r8 + big_x - big_x*(1._r8 - location(1)/big_x)**2)
54 | end function velocity1
55 |
56 | pure function velocity2(location) result(velocity)
57 | real(r8), dimension(:), intent(in) :: location
58 | real(r8), dimension(:), allocatable :: velocity
59 | allocate(velocity(2))
60 | velocity(1) = sin(location(1)/6.283185307_r8)
61 | velocity(2) = cos(location(1)/6.283185307_r8)
62 | end function velocity2
63 |
64 | subroutine setup(this)
65 | class(viscosity_test), intent(inout) :: this
66 | this%velocity1 = cheb1d_vector_field(this%numpoints,velocity1,0._r8,1._r8)
67 | this%velocity2 = cheb1d_vector_field(this%numpoints,velocity2,0._r8,1._r8,extra_dims=1)
68 | this%time1 = 1.0d5
69 | this%time2 = -5.7_r8
70 | this%temp1 = 1.0_r8
71 | this%temp2 = -3.5_r8
72 | this%viscval1 = 5.0_r8
73 | this%viscval2 = 1.1e4_r8
74 | this%visc1 = newtonian_viscosity(this%viscval1)
75 | this%visc2 = newtonian_viscosity(this%viscval2)
76 | end subroutine setup
77 |
78 | @Test
79 | subroutine test_viscosity_values(this)
80 | class(viscosity_test), intent(inout) :: this
81 | type(uniform_scalar_field) :: expected1, expected2, actual1, actual2
82 | expected1 = uniform_scalar_field(this%viscval1)
83 | expected2 = uniform_scalar_field(this%viscval2)
84 | actual1 = this%visc1%ice_viscosity(this%velocity1,this%temp1,this%time1)
85 | call test_field(expected1, actual1)
86 | actual1 = this%visc1%ice_viscosity(this%velocity2,this%temp1,this%time2)
87 | call test_field(expected1, actual1)
88 | actual2 = this%visc2%ice_viscosity(this%velocity2,this%temp2,this%time2)
89 | call test_field(expected2, actual2)
90 | actual2 = this%visc2%ice_viscosity(this%velocity1,this%temp2,this%time1)
91 | call test_field(expected2, actual2)
92 | end subroutine test_viscosity_values
93 |
94 | end module newtonian_viscosity_test
95 |
--------------------------------------------------------------------------------
/tests/pseudospec_block_test.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! pseudospec_block_test.pf
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2017 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module pseudospec_block_test
24 | use iso_fortran_env, only: r8 => real64
25 | use pfunit_mod
26 | use factual_mod, only: scalar_field, cheb1d_scalar_field, cheb1d_vector_field
27 | use pseudospectral_block_mod, only: pseudospec_block
28 | use boundary_types_mod, only: free_boundary, dirichlet
29 | implicit none
30 |
31 | integer, parameter :: resolution = 50
32 |
33 | @TestCase
34 | type, extends(testcase), public :: test_block
35 | type(cheb1d_scalar_field) :: scal, scal_dx
36 | type(cheb1d_vector_field) :: vec, vec_dx
37 | contains
38 | procedure :: setup
39 | end type test_block
40 |
41 | contains
42 |
43 | pure function f(location)
44 | real(r8), dimension(:), intent(in) :: location
45 | real(r8) :: f
46 | f = location(1)**2 + 3._r8*location(1) - 5._r8
47 | end function f
48 |
49 | pure function f_dx(location)
50 | real(r8), dimension(:), intent(in) :: location
51 | real(r8) :: f_dx
52 | f_dx = 2._r8*location(1) + 3._r8
53 | end function f_dx
54 |
55 | pure function g(location)
56 | real(r8), dimension(:), intent(in) :: location
57 | real(r8), dimension(:), allocatable :: g
58 | allocate(g(2))
59 | g(1) = 1._r8 - location(1) - exp(location(1))
60 | g(2) = sin(location(1))
61 | end function g
62 |
63 | pure function g_dx(location)
64 | real(r8), dimension(:), intent(in) :: location
65 | real(r8), dimension(:), allocatable :: g_dx
66 | allocate(g_dx(2))
67 | g_dx(1) = -1._r8 - exp(location(1))
68 | g_dx(2) = cos(location(1))
69 | end function g_dx
70 |
71 | subroutine setup(this)
72 | class(test_block), intent(inout) :: this
73 | this%scal = cheb1d_scalar_field(resolution, f)
74 | this%scal_dx = cheb1d_scalar_field(resolution, f_dx)
75 | this%vec = cheb1d_vector_field(resolution, g, extra_dims=1)
76 | this%vec_dx = cheb1d_vector_field(resolution, g_dx, extra_dims=1)
77 | end subroutine setup
78 |
79 | @Test
80 | subroutine test_pseudospec_solve_lower_bound(this)
81 | class(test_block), intent(inout) :: this
82 | type(cheb1d_scalar_field) :: actual_scal
83 | type(cheb1d_vector_field) :: actual_vec
84 | type(pseudospec_block) :: block1
85 | integer, parameter :: bound = -1, depth = 1
86 |
87 | block1 = pseudospec_block(this%scal)
88 |
89 | actual_scal = this%scal%d_dx(1)
90 | actual_scal = block1%solve_for(this%scal_dx, bound, this%scal%get_boundary(bound,depth))
91 | @assertEqual(this%scal%raw(),actual_scal%raw(),tolerance=1.e-9_r8,message='Pseudospec block returns wrong sol.')
92 | actual_scal = actual_scal%d_dx(1)
93 | @assertEqual(this%scal_dx%raw(),actual_scal%raw(),tolerance=1e-9_r8,message='Pseudospec block returns wrong sol.')
94 |
95 | actual_vec = this%vec%d_dx(1)
96 | actual_vec = block1%solve_for(this%vec_dx, bound, this%vec%get_boundary(bound,depth))
97 | @assertEqual(this%vec%raw(),actual_vec%raw(),tolerance=1.e-9_r8,message='Pseudospec block returns wrong sol.')
98 | actual_vec = actual_vec%d_dx(1)
99 | @assertEqual(this%vec_dx%raw(),actual_vec%raw(),tolerance=1e-9_r8,message='Pseudospec block returns wrong sol.')
100 | end subroutine test_pseudospec_solve_lower_bound
101 |
102 | @Test
103 | subroutine test_pseudospec_solve_upper_bound(this)
104 | class(test_block), intent(inout) :: this
105 | type(cheb1d_scalar_field) :: actual_scal
106 | type(cheb1d_vector_field) :: actual_vec
107 | type(pseudospec_block) :: block2
108 | integer, parameter :: bound = 1, depth = 1
109 |
110 | block2 = pseudospec_block(this%scal)
111 |
112 | actual_scal = this%scal%d_dx(1)
113 | actual_scal = block2%solve_for(this%scal_dx, bound, this%scal%get_boundary(bound,depth))
114 | @assertEqual(this%scal%raw(),actual_scal%raw(),tolerance=1.e-9_r8,message='Pseudospec block returns wrong sol.')
115 | actual_scal = actual_scal%d_dx(1)
116 | @assertEqual(this%scal_dx%raw(),actual_scal%raw(),tolerance=1e-9_r8,message='Pseudospec block returns wrong sol.')
117 |
118 | actual_vec = this%vec%d_dx(1)
119 | actual_vec = block2%solve_for(this%vec_dx, bound, this%vec%get_boundary(bound, depth))
120 | @assertEqual(this%vec%raw(),actual_vec%raw(),tolerance=1.e-9_r8,message='Pseudospec block returns wrong sol.')
121 | actual_vec = actual_vec%d_dx(1)
122 | @assertEqual(this%vec_dx%raw(),actual_vec%raw(),tolerance=1e-9_r8,message='Pseudospec block returns wrong sol.')
123 | end subroutine test_pseudospec_solve_upper_bound
124 |
125 | end module pseudospec_block_test
126 |
--------------------------------------------------------------------------------
/tests/testSuites.inc:
--------------------------------------------------------------------------------
1 | ADD_TEST_SUITE(bc_test_suite)
2 | ADD_TEST_SUITE(dallaston2015_melt_test_suite)
3 | ADD_TEST_SUITE(jenkins1991_entrainment_test_suite)
4 | ADD_TEST_SUITE(kochergin1987_entrainment_test_suite)
5 | ADD_TEST_SUITE(newtonian_viscosity_test_suite)
6 | ADD_TEST_SUITE(glens_law_test_suite)
7 | ADD_TEST_SUITE(jacobian_block_test_suite)
8 | ADD_TEST_SUITE(preconditioner_test_suite)
9 | ADD_TEST_SUITE(nitsol_test_suite)
10 | ADD_TEST_SUITE(ice_shelf_test_suite)
11 | ADD_TEST_SUITE(finn_diff_block_test_suite)
12 | ADD_TEST_SUITE(pseudospec_block_test_suite)
13 | ADD_TEST_SUITE(coriolis_block_test_suite)
14 | ADD_TEST_SUITE(plume_test_suite)
15 | ADD_TEST_SUITE(asym_plume_test_suite)
16 | ADD_TEST_SUITE(cryosphere_test_suite)
17 |
--------------------------------------------------------------------------------
/tests/test_utils.pf:
--------------------------------------------------------------------------------
1 | !
2 | ! utils.f90
3 | ! This file is part of ISOFT.
4 | !
5 | ! Copyright 2016 Chris MacMackin
6 | !
7 | ! This program is free software; you can redistribute it and/or modify
8 | ! it under the terms of the GNU General Public License as published by
9 | ! the Free Software Foundation; either version 2 of the License, or
10 | ! (at your option) any later version.
11 | !
12 | ! This program is distributed in the hope that it will be useful,
13 | ! but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ! GNU General Public License for more details.
16 | !
17 | ! You should have received a copy of the GNU General Public License
18 | ! along with this program; if not, write to the Free Software
19 | ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 | ! MA 02110-1301, USA.
21 | !
22 |
23 | module test_utils
24 | use pfunit_mod
25 | use factual_mod, only: abstract_field, scalar_field, vector_field
26 | implicit none
27 |
28 | contains
29 |
30 | subroutine test_field(expected, actual)
31 | !! Tests whether two fields are the same
32 | class(abstract_field) :: expected, actual
33 | @assertEqual(expected%domain(),actual%domain(),message='Incorrect domain returned.')
34 | @assertEqual(expected%resolution(),actual%resolution(),message='Incorrect resolution returned.')
35 | select type(expected)
36 | class is(scalar_field)
37 | select type(actual)
38 | class is(scalar_field)
39 | @assertTrue(expected == actual,message='Incorrect field contents')
40 | class default
41 | error stop 1
42 | end select
43 | class is(vector_field)
44 | select type(actual)
45 | class is(vector_field)
46 | @assertTrue(expected == actual,message='Incorrect field contents')
47 | class default
48 | error stop 1
49 | end select
50 | class default
51 | error stop 1
52 | end select
53 | end subroutine test_field
54 |
55 | end module test_utils
56 |
--------------------------------------------------------------------------------