├── .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 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1422482.svg)](https://doi.org/10.5281/zenodo.1422482) 4 | [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](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 | ![A cartoon diagram of an ice shelf and meltwater plume.](|media|/iceshelf.svg) 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 | ![A UML class diagram illustrating the relationships between the key 21 | derived types used in ISOFT. Components and methods of the types are not 22 | shown for reasons of space.](|media|/inheritance_diagram.png) 23 | 24 | ![UML class diagram for the cryosphere derived type, providing a simplified description of the methods associated with it.](|media|/cryo.png) 25 | 26 | ![UML class diagrams for the basal surface derived type, providing a simplified description of the methods associated with it.](|media|/basal.png) 27 | 28 | ![UML class diagrams for the glacier derived type, providing a simplified description of the methods associated with it.](|media|/glacier.png) 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 | ![A UML object diagram displaying the state of a typical simulation 55 | immediately after it has been initialised. Not all components are shown 56 | in each object for reasons of space.](|media|/object_tree.png) 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 | ![A UML sequence diagram illustrating the operations involved in each 90 | time step of ISOFT. Some of the argument lists have been simplified for 91 | reasons of space.](|media|/isoft_sequence.png) 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 | ![UML class diagram for the preconditioner used with the ice shelf solver.](|media|/precond.png) 47 | 48 | ![UML class diagram for the preconditioner inverting the finite-difference operator.](|media|/precond_001.png) 49 | 50 | ![UML class diagram for the preconditioner inverting the pseudo-spectral differentiation operator.](|media|/precond_002.png) 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 | ![A UML class diagram for the viscosity type. Subtypes implement all 25 | inherited abstract methods, but this is not shown for reasons of 26 | space.](|media|/viscosity.png) 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 | ![A UML class diagram for the entrainment type. Subtypes implement the 63 | inherited abstract method, but this is not shown for reasons of 64 | space.](|media|/entrainment.png) 65 | 66 | ![A UML class diagram for the melt type. Subtypes implement the 67 | inherited abstract method, but this is not shown for reasons of 68 | space.](|media|/melt.png) 69 | 70 | ![A UML class diagram for the equation of state type. Subtypes implement 71 | all inherited abstract methods, but this is not shown for reasons of 72 | space.](|media|/eos.png) 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 | ![A UML class diagram for the glacier boundary type. Subtypes implement 93 | all inherited abstract methods, but this is not shown for reasons of 94 | space.](|media|/icebound.png) 95 | 96 | ![A UML class diagram for the plume boundary type. Subtypes implement 97 | all inherited abstract methods, but this is not shown for reasons of 98 | space.](|media|/plumebound.png) 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 | ![A UML class diagram of the ambient conditions type. The subtype 124 | implements all inherited abstract methods, but this is not shown for 125 | reasons of space.](|media|/ambient.png) 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 | ![A contour plot of internal layers within an ice shelf.](|media|/layers.svg) 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 | --------------------------------------------------------------------------------