A data type representing a submatrix of a Jacobian. Specifically, it represents the commonly occurring operation where is the differentiation operator in the -direction. Optionally, there can be an additional differentiation operator on the right-hand-side of this.
This data type is useful when constructing a preconditioner. A preconditioner, , should approximate the inverse Jacobian of a problem being solved. Rather than directly computing the inverse Jacobian, it is more efficient to approximate it. If is the vector being preconditioned, and is the result of applying the preconditioner, then Thus, the preconditioner can be applied by approximately solving this system for . Linearising , this system can be solved efficiently using Picard iteration.
Say that the Jacobian is an system of these blocks (each labeled as ) and that the vector being preconditioned constists of scalar fields (). Then the estimate of the solution for the field in the preconditioned vector () is the solution to Depending on the type of fields being used and the direction in which derivatives are being taken, may be tridiaganol, meaning it can be solved efficiently.
For this purpose, a type-bound multiplication operator is
provided for the Jacobian block type, which can be used when
evaluating the right hand side of the linear system. There is
also a solve_for
method, which takes as an argument the field
representing the right hand side of the system.
Boundary conditions are a complicated issue. When constructing
an instance of this type, locations in the raw field
representation can be specified for which boundary conditions
are given. By default these are all Dirichlet conditions, but
this behaviour may be overridden to be Neumann conditions. Note
that Neumann conditions result in approximately an order of
magnitude lower accuracy. When using the Jacobian block for
multiplation, the type of the boundary does not matter; the
value at the boundary in the result will be set to 0 or to
values produced by an optional user-provided function. When
using the solve_for
method, the tridiagonal matrix will be
altered in the specified locations to reflect the correct
boundary conditions. This effectively mimics the sorts of
Jacobian rows typically produced by boundary conditions.
Type | Visibility | Attributes | Name | Initial | |||
---|---|---|---|---|---|---|---|
integer, | private | :: | direction | The direction in which any derivatives are taken. |
|||
integer, | private | :: | extra_derivative | = | no_extra_derivative | The direction in which to apply a differentiation on the right-hand-side of the Jacobian block operator. Defaults none. |
|
class(scalar_field), | private, | allocatable | :: | contents | The value, , to which the Jacobian block operation is beiing applied. |
||
real(kind=r8), | private | :: | coef | = | 1._r8 | Optional coefficient by which the the term in the operator will be multiplied. |
|
procedure(jacobian_block_bounds), | private, | pointer, nopass | :: | get_boundaries | A subroutine which determines the expected boundary conditions (and their location in the raw array) for the solution of the Jacobian block. |
||
class(scalar_field), | private, | allocatable | :: | derivative | The cached derivative of |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | diagonal | The diagonal of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | super_diagonal | The super-diagonal of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | sub_diagonal | The sub-diagonal of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | l_multipliers | Multipliers defining the L matrix in the LU factorisation of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | u_diagonal | The diagonal of the U matrix in the LU factorisation of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | u_superdiagonal1 | The first superdiagonal of the U matrix in the LU factorisation of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | u_superdiagonal2 | The second superdiagonal of the U matrix in the LU factorisation of the tridiagonal matrix representation of this block. |
||
integer, | private, | dimension(:), allocatable | :: | pivots | Pivot indicies from the LU factorisation of the tridiagonal matrix representation of this block. |
||
real(kind=r8), | private, | dimension(:), allocatable | :: | boundary_vals | Expected boundary values for the solution to the Jacobian block. |
||
integer, | private, | dimension(:), allocatable | :: | boundary_locs | Locations in the raw arrays which are used to specify boundary conditions. |
||
integer, | private, | dimension(:), allocatable | :: | boundary_types | The types of boundary conditions, specified using the parameters found in boundary_types_mod. |
||
real(kind=r8), | private | :: | real_increment | A scalar value which is to be added to this Jacobian block (i.e. to the diagonal). |
|||
class(scalar_field), | private, | allocatable | :: | field_increment | A scalar field which is to be added to this Jacobian block (i.e. to the diagonal). |
||
type(jacobian_block), | private, | pointer | :: | block_increment | => | null() | Another Jacobian block which is to be added to this one |
integer, | private | :: | has_increment | = | 0 | Indicates whether or not there has been an increment added to this block. If not, then 0. If a scalar real value has been added, then 1. If a scalar value has been added, then 2. |
Build a block in a Jacobian matrix, with the form where is a scalar field and is the differentiation operator in the -direction. Additionally, a further differentiation operator may be added to the right hand side of this matrix block. Optional arguments allow for handling of boundary conditions. See the end of the documentation of the jacobian_block type for a description of how boundary conditions are treated.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(scalar_field), | intent(in) | :: | source_field | A scalar field () making up this block of the Jacobian |
||
integer, | intent(in) | :: | direction | The direction in which field derivatives are taken. |
||
integer, | intent(in), | optional | :: | extra_derivative | If present, specifies the direction of a differentiation operator to be added to the right hand side of this matrix block. |
|
integer, | intent(in), | optional | dimension(:) | :: | boundary_locs | The locations in the raw representation of |
integer, | intent(in), | optional | dimension(:) | :: | boundary_types | Integers specifying the type of boundary condition. The type
of boundary condition corresponding to a given integer is
specified in boundary_types_mod. Only Dirichlet and
Neumann conditions are supported. Defaults to Dirichlet. The
order in which they are stored must match that of
|
procedure(jacobian_block_bounds), | optional | :: | boundary_operations | A function specifying the values to place at the boundaries
of the result when using the Jacobian block for
multiplication. By default, all boundaries are set to 0. The
order in which the resulting values are stored should match
that of |
||
real(kind=r8), | intent(in), | optional | :: | coef | An optional coefficient by which the the term in the operator will be multipled. Default value is 1. |
A new Jacobian block
Provides a matrix multiplication operator between a Jacobian block and a scalar field (which corresponds to a state vector).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(scalar_field), | intent(in) | :: | rhs | A field corresponding to a state vector being multiplied by the Jacobian block. |
Produces a Jacobian block which has been offset by some constant increment.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
real(kind=r8), | intent(in) | :: | rhs | A scalar which should be added to this block |
Produces a Jacobian block which has been offset by a scalar field.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(scalar_field), | intent(in) | :: | rhs | A scalar which should be added to this block |
Produces a Jacobian block which is the sum of two existing
blocks. Boundary conditions are set by the first operand
(this
).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(jacobian_block), | intent(in), | target | :: | rhs | A second block which should be added to this block |
Copies the contents of the rhs
Jacobian block into this
one. It will safely deallocate any data necessary.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(out) | :: | this | |||
type(jacobian_block), | intent(in) | :: | rhs |
Computes the tridiagonal matrix used to solve for this Jacobian block.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
real(kind=r8), | intent(out), | dimension(:), allocatable | :: | diagonal | ||
real(kind=r8), | intent(out), | dimension(:), allocatable | :: | subdiagonal | ||
real(kind=r8), | intent(out), | dimension(:), allocatable | :: | superdiagonal |
Provides a matrix multiplication operator between a Jacobian block and a scalar field (which corresponds to a state vector).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(scalar_field), | intent(in) | :: | rhs | A field corresponding to a state vector being multiplied by the Jacobian block. |
Produces a Jacobian block which has been offset by some constant increment.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
real(kind=r8), | intent(in) | :: | rhs | A scalar which should be added to this block |
Produces a Jacobian block which has been offset by a scalar field.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(scalar_field), | intent(in) | :: | rhs | A scalar which should be added to this block |
Produces a Jacobian block which is the sum of two existing
blocks. Boundary conditions are set by the first operand
(this
).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(in) | :: | this | |||
class(jacobian_block), | intent(in), | target | :: | rhs | A second block which should be added to this block |
Copies the contents of the rhs
Jacobian block into this
one. It will safely deallocate any data necessary.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(out) | :: | this | |||
type(jacobian_block), | intent(in) | :: | rhs |
Solves the linear(ised) system represented by this Jacobian block, for a given right hand side state vector (represented by a scalar field).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(jacobian_block), | intent(inout) | :: | this | |||
class(scalar_field), | intent(in) | :: | rhs | The right hand side of the linear(ised) system. |
type, public :: jacobian_block
!* Author: Chris MacMackin
! Date: December 2016
!
! A data type representing a submatrix of a
! Jacobian. Specifically, it represents the commonly occurring
! operation $$ \frac{\partial F}{\partial x_i} + F\Delta_i, $$
! where \( \Delta_i \) is the differentiation operator in the
! \(i\)-direction. Optionally, there can be an additional
! differentiation operator on the right-hand-side of this.
!
! This data type is useful when constructing a preconditioner. A
! preconditioner, \(A\), should approximate the inverse Jacobian of
! a problem being solved. Rather than directly computing the
! inverse Jacobian, it is more efficient to approximate it. If
! \(d\) is the vector being preconditioned, and \(z\) is the
! result of applying the preconditioner, then $$ z = J^{-1}d
! \Rightarrow Jz = d.$$ Thus, the preconditioner can be applied
! by approximately solving this system for \(z\). Linearising
! \(J\), this system can be solved efficiently using Picard
! iteration.
!
! Say that the Jacobian is an \(n\times n\) system of these blocks
! (each labeled as \(J_{j,k}\)) and that the vector being
! preconditioned constists of \(n\) scalar fields (\(d_j\)). Then
! the \(m^{th}\) estimate of the solution for the \(j^{th}\) field
! in the preconditioned vector (\(z^m_j\)) is the solution to $$
! J_{j,j}z^m_j = d_j - \sum_{\substack{k=1\ k\ne j}}^n
! J_{j,k}z^{m-1}_k. $$ Depending on the type of fields being used
! and the direction in which derivatives are being taken,
! \(J_{j,j}\) may be tridiaganol, meaning it can be solved
! efficiently. <!--Otherwise, it can be approximated that $$
! J_{j,j}z^m_j = \left(\frac{\partial F}{\partial x_i} +
! F\Delta_i\right)z^{m} \simeq \frac{\partial F}{\partial
! x_i}z^m_j + F\frac{\partial z^{m-1}_j}{\partial x_i}. $$ The
! first term in this approximation corresponds to a diagonal
! matrix (which can be solved trivially), while the second term is
! known and can be subtracted from the right-hand-side of the
! linear system.-->
!
! For this purpose, a type-bound multiplication operator is
! provided for the Jacobian block type, which can be used when
! evaluating the right hand side of the linear system. There is
! also a `solve_for` method, which takes as an argument the field
! representing the right hand side of the system. <!--Before
! applying the latter operator, the `update_estimate` method must
! be called. This allows for the term involving derivative of the
! current estimate of the solution to be subtracted from the
! right-hand-side of the linear system, in cases where the system
! can not be expressed as a tridiagonal matrix.-->
!
! Boundary conditions are a complicated issue. When constructing
! an instance of this type, locations in the raw field
! representation can be specified for which boundary conditions
! are given. By default these are all Dirichlet conditions, but
! this behaviour may be overridden to be Neumann conditions. Note
! that Neumann conditions result in approximately an order of
! magnitude lower accuracy. When using the Jacobian block for
! multiplation, the type of the boundary does not matter; the
! value at the boundary in the result will be set to 0 or to
! values produced by an optional user-provided function. When
! using the `solve_for` method, the tridiagonal matrix will be
! altered in the specified locations to reflect the correct
! boundary conditions. This effectively mimics the sorts of
! Jacobian rows typically produced by boundary conditions.
!
private
integer :: direction
!! The direction in which any derivatives are taken.
integer :: extra_derivative = no_extra_derivative
!! The direction in which to apply a differentiation on the
!! right-hand-side of the Jacobian block operator. Defaults
!! none.
class(scalar_field), allocatable :: contents
!! The value, \(A\), to which the Jacobian block operation is
!! beiing applied.
real(r8) :: coef = 1._r8
!! Optional coefficient by which the the \(\partial F/\partial x
!! \) term in the operator will be multiplied.
procedure(jacobian_block_bounds), pointer, nopass :: &
get_boundaries
!! A subroutine which determines the expected boundary conditions
!! (and their location in the raw array) for the solution of the
!! Jacobian block.
class(scalar_field), allocatable :: derivative
!! The cached derivative of `contents`
real(r8), dimension(:), allocatable :: diagonal
!! The diagonal of the tridiagonal matrix representation of this
!! block.
real(r8), dimension(:), allocatable :: super_diagonal
!! The super-diagonal of the tridiagonal matrix representation
!! of this block.
real(r8), dimension(:), allocatable :: sub_diagonal
!! The sub-diagonal of the tridiagonal matrix representation of
!! this block.
real(r8), dimension(:), allocatable :: l_multipliers
!! Multipliers defining the L matrix in the LU factorisation of
!! the tridiagonal matrix representation of this block.
real(r8), dimension(:), allocatable :: u_diagonal
!! The diagonal of the U matrix in the LU factorisation of
!! the tridiagonal matrix representation of this block.
real(r8), dimension(:), allocatable :: u_superdiagonal1
!! The first superdiagonal of the U matrix in the LU
!! factorisation of the tridiagonal matrix representation of
!! this block.
real(r8), dimension(:), allocatable :: u_superdiagonal2
!! The second superdiagonal of the U matrix in the LU
!! factorisation of the tridiagonal matrix representation of
!! this block.
integer, dimension(:), allocatable :: pivots
!! Pivot indicies from the LU factorisation of the tridiagonal
!! matrix representation of this block.
real(r8), dimension(:), allocatable :: boundary_vals
!! Expected boundary values for the solution to the Jacobian
!! block.
integer, dimension(:), allocatable :: boundary_locs
!! Locations in the raw arrays which are used to specify
!! boundary conditions.
integer, dimension(:), allocatable :: boundary_types
!! The types of boundary conditions, specified using the
!! parameters found in [[boundary_types_mod]].
real(r8) :: real_increment
!! A scalar value which is to be added to this Jacobian block
!! (i.e. to the diagonal).
class(scalar_field), allocatable :: field_increment
!! A scalar field which is to be added to this Jacobian block
!! (i.e. to the diagonal).
type(jacobian_block), pointer :: block_increment => null()
!! Another Jacobian block which is to be added to this one
integer :: has_increment = 0
!! Indicates whether or not there has been an increment added to
!! this block. If not, then 0. If a scalar real value has been
!! added, then 1. If a scalar value has been added, then 2.
contains
private
procedure :: jacobian_block_multiply
procedure :: jacobian_block_add_real
procedure :: jacobian_block_add_field
procedure :: jacobian_block_add_block
procedure :: jacobian_block_assign
procedure :: get_tridiag => jacobian_block_get_tridiag
generic, public :: operator(*) => jacobian_block_multiply
generic, public :: operator(+) => jacobian_block_add_real, &
jacobian_block_add_field, &
jacobian_block_add_block
generic, public :: assignment(=) => jacobian_block_assign
procedure, public :: solve_for => jacobian_block_solve
end type jacobian_block