Full Subzero API documentation
This page is still very much WIP!
Grids
Subzero.AbstractRectilinearGrid
— TypeYourGridType{FT} <: AbstractRectilinearGrid{FT}
Each simulation run with Subzero.jl must be run on a grid. A grid defines the points at which the ocean and atmosphere hold velocity data, as well as various other tracers, at grid points. The user must choose an existing subtype of AbstractRectilinearGrid
or implement a new subtype to create a simulation.
Each grid implementation must define the number and dimensions of each grid cell. Right now, this assumes that the ocean and the atmosphere are on the same grid and that the grid is rectangular. We might not want this to be true in the future. Furthermore, each concrete implementation of AbstractRectilinearGrid
that will be used to two-way couple with an ocean or an atmosphere must have a field called floe_locations
that is a matrix of CellFloes
, with one element for each grid point. The user should not worry about CellFloes
, this is up to the developer to make sure that their grid contains and populates this field. Again, in the future, this might be related to the ocean and/or atmosphere rather than the grid. For more information, or if you're interested in working on this, see issue #107.
API
The following methods must be implemented for all subtypes:
_get_grid_extent(grid::AbstractRectilinearGrid)
:
The _get_grid_extent
function takes in a concrete subtype of AbstractRectilinearGrid
and returns the grid's minimum x-value, maximum x-value, minimum y-value, and maximum y-value.
Given the original code was written for RegRectilinearGrid
objects, the dispatch isn't fully implemented and other functions should be added to this list. If you were interested in adding a new subtype of AbstractRectilinearGrid
, see issue #108.
Subzero.RegRectilinearGrid
— TypeRegRectilinearGrid{FT} <: AbstractRectilinearGrid{FT}
A concrete implementation of an AbstractRectilinearGrid
that represents a tessellation of 2-dimensional Euclidean space into n
-by-m
congruent rectangles. Fields that hold float data are of type FT
, a concrete subtype of AbstractFloat
.
Nx::Int
: number of grid cells in the x-directionNy::Int
: number of grid cells in the y-directionΔx::FT
: grid cell widthΔy::FT
: grid cell heightx0::FT
: value of first x grid linexf::FT
: value of final x grid liney0::FT
: value of first y grid lineyf::FT
: value of final y grid linefloe_locations::Matrix{CellFloes}
:Nx + 1
byNy + 1
matrix ofCellFloes
Here is how to construct a RegRectilinearGrid
:
RegRectilinearGrid([FT = Float64]; x0, xf, y0, yf, Nx = nothing, Ny = nothing, Δx = nothing, Δy = nothing)
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
x0::FT
: value of first x grid linexf::FT
: value of final x grid liney0::FT
: value of first y grid lineyf::FT
: value of final y grid lineNx::Int
: number of grid cells in the x-directionNy::Int
: number of grid cells in the y-directionΔx::FT
: grid cell widthΔy::FT
: grid cell height
The user MUST provide x0
, xf
, y0
, and yf
; the user then has the choice to provide Nx
and Ny
OR Δx
and Δy
. If provided Δx
doesn't evenly divide length xf-x0
or Δy
doesn't evenly divide yf-y0
, you won't get full size grid. The grid will be "trimmed" to the nearest full grid square in both directions.
Examples
- Defining a
RegRectilinearGrid
usingNx
andNy
.
julia> grid = RegRectilinearGrid(; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 10)
RegRectilinearGrid{Float64}
⊢x extent (0.0 to 500000.0) with 20 grid cells of size 25000.0 m
∟y extent (0.0 to 500000.0) with 10 grid cells of size 50000.0 m
- Defining a
RegRectilinearGrid
usingΔx
andΔy
.
julia> grid = RegRectilinearGrid(Float32; x0 = -5e5, xf = 5e5, y0 = 0.0, yf = 5e5, Δx = 5e4, Δy = 5e4)
RegRectilinearGrid{Float32}
⊢x extent (-500000.0 to 500000.0) with 20 grid cells of size 50000.0 m
∟y extent (0.0 to 500000.0) with 10 grid cells of size 50000.0 m
- Error due to attemping to define a
RegRectilinearGrid
usingΔx
andNy
.
julia> grid = RegRectilinearGrid(; x0 = -5e5, xf = 5e5, y0 = 0.0, yf = 5e5, Δx = 5e4, Ny = 10)
ERROR: ArgumentError: To create a RegRectilinearGrid, either provide Δx and Δy OR Nx and Ny.
[...]
Directions
Subzero.AbstractDirection
— TypeYourDirection <: AbstractDirection
Each domain within a Subzero.jl Model
must have four (4) boundaries (subtypes of AbstractBoundary
) where each of these boundaries is parametrically typed by the direction of the boundary. The user will first choose one of the four cardinal directions, the subtypes of AbstractDirection
:
This abstract type is not meant to be extended by the user, unless the user wants to move away from a rectangular domain with assigned cardinal directions for each wall. This would a more major redesign and the user should check out the developer documentation.
_API
_boundary_info_from_extent(D::Type{AbstractDirection}, FT::Type{AbstractFloat}, x0, xf, y0, yf)
The _boundary_info_from_extent
function finds the values for the poly
and val
fields of a subtype of AbstractBoundary
of direction D
given a region of the extent defined by x0
, xf
, y0
, and yf
given that these are the minimum and maximum x
and y
values of the region desired. The returned extent will have values of type FT
.
Subzero.North
— TypeNorth<:AbstractDirection
A simple subtype of AbstractDirection
used for parametrically typing a subtype of AbstractBoundary
if that boundary is the northern boundary in a rectangular domain.
Subzero.South
— TypeSouth<:AbstractDirection
A simple subtype of AbstractDirection
used for parametrically typing a subtype of AbstractBoundary
if that boundary is the southern boundary in a rectangular domain.
Subzero.East
— TypeEast<:AbstractDirection
A simple subtype of AbstractDirection
used for parametrically typing a subtype of AbstractBoundary
if that boundary is the eastern boundary in a rectangular domain.
Subzero.West
— TypeWest<:AbstractDirection
A simple subtype of AbstractDirection
used for parametrically typing a subtype of AbstractBoundary
if that boundary is the western boundary in a rectangular domain.
Boundaries
Subzero.AbstractBoundary
— TypeAbstractBoundary{D, FT} <: AbstractDomainElement{FT}
When running a Subzero simulation, each simulation has a rectangular domain that contains the ice pack. This domain is made up for four boundaries, one in each of the cardinal directions: North
, South
, East
, and West
. The user will pick four boundary types, each concrete subtypes of AbstractBoundary
and assign each of them a direction, D
and a float type FT
, by typing them parametrically.
The user can pick from the currently implemented concrete subtypes of AbstractBoundary
(OpenBoundary
, CollisionBoundary
, PeriodicBoundary
, and MovingBoundary
) or implement their own.
For now, each boundary wall is assumed to be a rectangle. For correct behavior, the corners of each of the boundary rectangles should overlap to make sure that no floes reach outside of the domain without touching a boundary. These rectangles should be represented as polygons with the poly
field within each boundary. Furthermore, each boundary should have a val
field that represents either the line y = val
(for North
and South
walls) or x = val
(for East
and West
walls) that denotes the line that marks the "inner-most" edge of the domain. If an ice floe passes over the boundary's val
line, the floe interacts with the boundary.
________________
|__|____val___|__| <- North coordinates include corners
| | | |
| | | | <- East and west coordinates ALSO include corners
| | | |
For ease of use, each boundary should have a constructor option where the user can provide an AbstractRectilinearGrid
concrete type to define a boundary where val
is at the edge of the grid so that the boundaries form a border right around the grid.
API
In addition to the below function, any new AbstractBoundary
subtype must also implement AbstractDomainElement
API functions.
_update_boundary!(boundary::AbstractBoundary, Δt::Int)
_periodic_compat(boundary1::AbstractBoundary, boundary2::AbstractBoundary)
The _update_boundary!
function updates a boundary's position (by changing the val
and poly
fields) at every timestep. Currently, only MovingBoundary
elements are updated, and their update depends on the length of the simulation's timestep, Δt
.
The _periodic_compat
function determines if a pair of functions are "periodically compatible", that is, could they form a pair on opposite edges of a domain to create a set of functioning periodic boundaries. It is called when creating a Domain
.
Subzero.OpenBoundary
— TypeOpenBoundary{D, FT} <: AbstractBoundary{D, FT}
A concrete subtype of AbstractBoundary
that removes a floe from the simulation if any of the floe's vertices overlap with the OpenBoundary
. This is meant to simulate the floe floating out of the simulation. The floe is immeditaly removed as there might not be ocean u
and v
values outside of the domian and thus we wouldn't know how to evolve the floe's trajectory. This boundary is direction D
(AbstractDirection
) of the domain and has field data of type FT <: AbstractFloat
.
Fields
poly::StaticQuadrilateral{FT}
: rectangular polygon representing
the shape and location of the boundary on a given timestep with points of float type FT
.
val::FT
: number of float typeFT
representing either the liney = val
(for North
and South
walls) or x = val
(for East
and West
walls) that denotes the
line marking the inner-most edge of the domain.
Here is how to construct an OpenBoundary
:
OpenBoundary(D, [FT = Float64]; grid = nothing, x0 = nothing, xf = nothing, y0 = nothing, yf = nothing)
The user must specify which AbstractDirection
the boundary is. The user also has an opportunity to specify which float type Float64
or Float32
will be used for field data. The user then must either provide an grid object (AbstractRectilinearGrid
) for the domain to align with the grid edges, or provide four values (x0
, xf
, y0
, and yf
) that define the x and y-extents the user wants for the domain.
If the user chooses to specify the domain extents, they still must be within the grid in order to make a valid Model
.
Positional arguments
D::Type{<:AbstractDirection}
: subtype of AbstractDirection used to represent
if a boundary is the North, South, East, or West boundary of a simulation
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
grid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulationx0::FT
: minimum x-value of domain extent (region floes will be contained within)xf::FT
: maximum x-value of domain extent (region floes will be contained within)y0::FT
: minimum y-value of domain extent (region floes will be contained within)yf::FT
: maximum y-value of domain extent (region floes will be contained within)
The user must either provide a grid
OR all four (4) of x0
, xf
, y0
, and yf
.
Examples
- Defining a Northern
OpenBoundary
with Float64 (default type) data using thegrid
keyword.
julia> g = RegRectilinearGrid(x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 20);
julia> OpenBoundary(North; grid = g)
OpenBoundary{North, Float64}
⊢polygon points are defined by the following set: (-250000.0, 750000.0), (750000.0, 500000.0), (750000.0, 750000.0), (-250000.0, 500000.0)
∟val is 500000.0
- Defining a Southern
OpenBoundary
with Float32 data using thex0
,xf
,y0
andyf
keywords.
julia> OpenBoundary(South, Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5)
OpenBoundary{South, Float32}
⊢polygon points are defined by the following set: (750000.0f0, 0.0f0), (750000.0f0, -250000.0f0), (-250000.0f0, 0.0f0), (-250000.0f0, -250000.0f0)
∟val is 0.0
Subzero.PeriodicBoundary
— TypePeriodicBoundary <: AbstractBoundary
A concrete subtype of AbstractBoundary
that moves a floe from one side of the domain to the opposite side of the domain (North
to South
and East
to West
and visa versa) when its centroid crosses the PeriodicBoundary
, bringing the floe back into the domain. Due to this behavior, PeriodicBoundary
pairs are required to form a valid domain. This boundary is direction D
(AbstractDirection
) of the domain and has field data of type FT <: AbstractFloat
.
Fields
poly::StaticQuadrilateral{FT}
: rectangular polygon representing
the shape and location of the boundary on a given timestep with points of float type FT
.
val::FT
: number of float typeFT
representing either the liney = val
(for North
and South
walls) or x = val
(for East
and West
walls) that denotes the
line marking the inner-most edge of the domain.
Here is how to construct an PeriodicBoundary
:
PeriodicBoundary(D, [FT = Float64]; grid = nothing, x0 = nothing, xf = nothing, y0 = nothing, yf = nothing)
The user must specify which AbstractDirection
the boundary is. The user also has an opportunity to specify which float type Float64
or Float32
will be used for field data. The user then must either provide an grid object (AbstractRectilinearGrid
) for the domain to align with the grid edges, or provide four values (x0
, xf
, y0
, and yf
) that define the x and y-extents the user wants for the domain.
If the user chooses to specify the domain extents, they still must be within the grid in order to make a valid Model
.
Positional arguments
D::Type{<:AbstractDirection}
: subtype of AbstractDirection used to represent
if a boundary is the North, South, East, or West boundary of a simulation
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
grid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulationx0::FT
: minimum x-value of domain extent (region floes will be contained within)xf::FT
: maximum x-value of domain extent (region floes will be contained within)y0::FT
: minimum y-value of domain extent (region floes will be contained within)yf::FT
: maximum y-value of domain extent (region floes will be contained within)
The user must either provide a grid
OR all four (4) of x0
, xf
, y0
, and yf
.
Examples
- Defining an Eastern
PeriodicBoundary
with Float32 data using thegrid
keyword.
julia> g = RegRectilinearGrid(Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 20);
julia> PeriodicBoundary(East; grid = g)
PeriodicBoundary{East, Float64}
⊢polygon points are defined by the following set: (750000.0, -250000.0), (500000.0, -250000.0), (750000.0, 750000.0), (500000.0, 750000.0)
∟val is 500000.0
- Defining a Western
PeriodicBoundary
with Float64 data using thex0
,xf
,y0
andyf
keywords.
julia> PeriodicBoundary(West, Float64; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5)
PeriodicBoundary{West, Float64}
⊢polygon points are defined by the following set: (0.0, -250000.0), (-250000.0, 750000.0), (0.0, 750000.0), (-250000.0, -250000.0)
∟val is 0.0
Subzero.CollisionBoundary
— TypeCollisionBoundary <: AbstractBoundary
A concrete subtype of AbstractBoundary
that calculates collision forces of a floe against the boundary if any of the floe's vertices overlap with the CollisionBoundary
. This is meant to simulate any barrier that might stop a floe from flowing into or out of a given region, like the edges of a cove. With this type of wall, a CollisionBoundary
is treated as an immovable, unbreakable floe for the purposes of calculations. This boundary is direction D
(AbstractDirection
) of the domain and has field data of type FT <: AbstractFloat
.
Fields
poly::StaticQuadrilateral{FT}
: rectangular polygon representing
the shape and location of the boundary on a given timestep with points of float type FT
.
val::FT
: number of float typeFT
representing either the liney = val
(for North
and South
walls) or x = val
(for East
and West
walls) that denotes the
line marking the inner-most edge of the domain.
Here is how to construct an CollisionBoundary
:
CollisionBoundary(D, [FT = Float64]; grid = nothing, x0 = nothing, xf = nothing, y0 = nothing, yf = nothing)
The user must specify which AbstractDirection
the boundary is. The user also has an opportunity to specify which float type Float64
or Float32
will be used for field data. The user then must either provide an grid object (AbstractRectilinearGrid
) for the domain to align with the grid edges, or provide four values (x0
, xf
, y0
, and yf
) that define the x and y-extents the user wants for the domain.
If the user chooses to specify the domain extents, they still must be within the grid in order to make a valid Model
.
Positional arguments
D::Type{<:AbstractDirection}
: subtype of AbstractDirection used to represent
if a boundary is the North, South, East, or West boundary of a simulation
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
grid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulationx0::FT
: minimum x-value of domain extent (region floes will be contained within)xf::FT
: maximum x-value of domain extent (region floes will be contained within)y0::FT
: minimum y-value of domain extent (region floes will be contained within)yf::FT
: maximum y-value of domain extent (region floes will be contained within)
The user must either provide a grid
OR all four (4) of x0
, xf
, y0
, and yf
.
Examples
- Defining an Northern
CollisionBoundary
with Float64 data using thegrid
keyword.
julia> g = RegRectilinearGrid(Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 20);
julia> CollisionBoundary(North; grid = g)
CollisionBoundary{North, Float64}
⊢polygon points are defined by the following set: (-250000.0, 750000.0), (750000.0, 500000.0), (750000.0, 750000.0), (-250000.0, 500000.0)
∟val is 500000.0
- Defining a Western
CollisionBoundary
with Float64 data using thex0
,xf
,y0
andyf
keywords.
julia> CollisionBoundary(West, Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5)
CollisionBoundary{West, Float32}
⊢polygon points are defined by the following set: (0.0f0, -250000.0f0), (-250000.0f0, 750000.0f0), (0.0f0, 750000.0f0), (-250000.0f0, -250000.0f0)
∟val is 0.0
Subzero.MovingBoundary
— TypeMovingBoundary <: AbstractBoundary
A concrete subtype of AbstractBoundary
that can provide a compressive or shear force on the floes within the domain by moving parallel or perpendicular to the domain. The MovingBoundary
calcuates the forces on the floe exactly the same as a CollisionBoundary
, by acting as a immovable, unbreakable floe. This boundary is direction D
(AbstractDirection
) of the domain and has field data of type FT <: AbstractFloat
.
If a North
or South
wall has a non-zero v
velocity this will provide a compressive stress as the floe moves towards the center of the domain along the vector x = cx
where (cx, cy)
is the centroid of the domain (or decompresses with opposite sign velocities). The poly
and val
fields are updated to represent the movement of the domain at the user-provided velocities, u
and v
m/s.
Alternatively, if a North
or South
wall has a non-zero u
velocity this will provide a shear stress as the domain "moves" along the line y = y0
(for North
) or y = yf
(for South). In this case, this only changes the frictional forces and we do not need up actually change the poly
or val
fields.
Fields
poly::StaticQuadrilateral{FT}
: rectangular polygon representing
the shape and location of the boundary on a given timestep with points of float type FT
.
val::FT
: number of float typeFT
representing either the liney = val
(for North
and South
walls) or x = val
(for East
and West
walls) that denotes the
line marking the inner-most edge of the domain.
u::FT
: boundary's u-velocityv::FT
: boundary's v-velocity
Here is how to construct an MovingBoundary
:
MovingBoundary(D, [FT = Float64]; u = 0.0, v = 0.0, grid = nothing, x0 = nothing, xf = nothing, y0 = nothing, yf = nothing)
The user must specify which AbstractDirection
the boundary is. The user also has an opportunity to specify which float type Float64
or Float32
will be used for field data. The user then must either provide an grid object (AbstractRectilinearGrid
) for the domain to align with the grid edges, or provide four values (x0
, xf
, y0
, and yf
) that define the x and y-extents the user wants for the domain.
If the user chooses to specify the domain extents, they still must be within the grid in order to make a valid Model
.
Positional arguments
D::Type{<:AbstractDirection}
: subtype of AbstractDirection used to represent
if a boundary is the North, South, East, or West boundary of a simulation
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
u::FT
: boundary's u-velocityv::FT
: boundary's v-velocitygrid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulationx0::FT
: minimum x-value of domain extent (region floes will be contained within)xf::FT
: maximum x-value of domain extent (region floes will be contained within)y0::FT
: minimum y-value of domain extent (region floes will be contained within)yf::FT
: maximum y-value of domain extent (region floes will be contained within)
The user must either provide a grid
OR all four (4) of x0
, xf
, y0
, and yf
. If the user does not provide values for u
or v
, the boundary will not move.
Examples
- Defining an Northern
MovingBoundary
with Float64 data using thegrid
keyword. Assigning u-velocity of 0.5m/s.
julia> g = RegRectilinearGrid(Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 20);
julia> MovingBoundary(North; u = 0.5, grid = g)
MovingBoundary{North, Float64}
⊢polygon points are defined by the following set: (-250000.0, 750000.0), (750000.0, 500000.0), (750000.0, 750000.0), (-250000.0, 500000.0)
⊢val is 500000.0
⊢u-velocity of 0.5 m/s
∟v-velocity of 0.0 m/s
- Defining a Southern
MovingBoundary
with Float32 data using thex0
,xf
,y0
andyf
keywords.
Assigning u-velocity of 0.3 m/s and v-velocity of 0.25 m/s
julia> MovingBoundary(South, Float32; u = 0.3, v = 0.25, x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5)
MovingBoundary{South, Float32}
⊢polygon points are defined by the following set: (750000.0f0, 0.0f0), (750000.0f0, -250000.0f0), (-250000.0f0, 0.0f0), (-250000.0f0, -250000.0f0)
⊢val is 0.0
⊢u-velocity of 0.3 m/s
∟v-velocity of 0.25 m/s
Topography
Subzero.TopographyElement
— TypeTopographyElement{FT}<:AbstractDomainElement{FT}
A concrete subtype of AbstractDomainElement
that represent topography in a given simulation. These TopographyElement
objects will be treated as be treated as islands or coastline within the model in that they will not move or break due to floe interactions, but they will affect floes that collide with them.
Similar to Floe
objects, we store the shape of each TopographyElement
with a poly
field. We also store the centroid
and the shape's maximum radius (rmax
) in order to calculate simple interaction checks with Floes.
Fields
poly::Polys{FT}
: Polygon used to represent the shape of a floe or topographycentroid::Vector{FT}
: Two-element vector meant to represent the (x, y) point that is the centroid of either a floe or topographyrmax::FT
: Float length representing the maximum radius of a floe or topography from the centroid to any given vertex
Here is how to construct an TopographyElement
:
TopographyElement([FT = Float64]; poly::Polys)
The user must provide a GeoInterface
polygon of the specific type defined by Polys
. The user also has an opportunity to specify which float type Float64
or Float32
will be used for field data.
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
poly::Polys{FT}
: Polygon used to represent the shape of a floe or topography
Note: The user should NOT be using this constructor. The user should create a topography
field using initialize_topography_field
rather than creating individual topography elements. This allows the abstraction of the use of the StructArrays
package away from the user.
Examples
julia> import GeoInterface as GI;
julia> poly = GI.Polygon([[(0.0, 0.0), (0.0, 1e3), (1e3, 1e3), (1e3, 0.0), (0.0, 0.0)]]);
julia> TopographyElement(Float64; poly)
TopographyElement{Float64}
⊢centroid is (500.0, 500.0) in meters
∟maximum radius is 707.1067811865476 meters
julia> TopographyElement(Float32; poly)
TopographyElement{Float32}
⊢centroid is (500.0, 500.0) in meters
∟maximum radius is 707.1068 meters
Subzero.initialize_topography_field
— Functioninitialize_topography_field([::Type{FT} = Float64]; polys = nothing, coords = nothing)
This function allows for easy initialization of a field of [TopographyElement(@ref)s and collects them into a
StructArrayso that they can be passed into a [
Model`](@ref).
This is the suggested way to create a topography field for a simulation. Do NOT construct individual TopographyElement
objects as that method does not correct the field as a whole to ensure no topography polygons overlap and does not create a struct array that can be passed to a Model
.
The user can create a topography field by either passing a list of polygons or by passing a list of coordiantes, which will then be made into polygons.
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
polys::Vector{<:Polygon}
: list of polygons meant to represent a field of floes or topography elements. Polygons can be any polygon type that supports GeoInterface.coords::Vector{<:PolyVec}
: list of polygon coordinates meant to represent a field of floes or topography elements. PolyVec refers to a Vector{Vector{<:Points}} where the points can be tuples, vectors, or static vectors and the innermost vector refers to each ring of the polygon.
Topography field elements must not be intersecting, so the corrections within this function may lead to a different number of polygons than input to make a valid topography field.
Examples
- Defining a topography field with coordinates
julia> coords = [[[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]], [[(10.0, 0.0), (10.0, 1.0), (11.0, 1.0), (10.0, 0.0)]]];
julia> initialize_topography_field(Float64; coords)
2-element TopographyField{Float64} list:
TopographyElement{Float64}
⊢centroid is (0.3333333333333333, 0.6666666666666666) in meters
∟maximum radius is 0.74535599249993 meters
TopographyElement{Float64}
⊢centroid is (10.333333333333334, 0.6666666666666666) in meters
∟maximum radius is 0.7453559924999301 meters
- Defining a topography field with polygons
julia> import GeoInterface as GI;
julia> polys = [GI.Polygon(c) for c in coords];
julia> initialize_topography_field(Float32; polys)
2-element TopographyField{Float32} list:
TopographyElement{Float32}
⊢centroid is (0.33333334, 0.6666667) in meters
∟maximum radius is 0.745356 meters
TopographyElement{Float32}
⊢centroid is (10.333333, 0.6666667) in meters
∟maximum radius is 0.74535626 meters
- Creating an empty topography field without polys or coords
julia> initialize_topography_field(Float64)
0-element TopographyField{Float64} list
Domain
Subzero.Domain
— TypeDomain{FT, NB, SB, EB, WB, TT}
A simulation Domain
holds four (4) boundary elements (concrete subtypes of AbstractBoundary
) and a list (potentially empty) of TopographyElement
objects. There must be a wall typed by each cardinal direction, and each wall must hold data of the same float type FT
. Additionally, the North
boundary should be "higher" (on the y-axis) than the South
boundary and the East
should be futher to the "right" (on the x-axis) than the West
boundary. Aditionally, the boundary walls should be overlapping as detailed in AbstractBoundary
.
Additionally, the set of walls must be periodically compatible. This means that pairs of opposite boundaries (North
and South
AND East
and West
) both need to be periodic if one of them is periodic. This is because if a floe exits a periodic boundary, it must be able to re-enter the opposite boundary to fulfill its definition of periodic.
Fields
north::NB
: Northern boundary whereNB <: AbstractBoundary{North, FT}
south::SB
: Southern boundary whereSB <: AbstractBoundary{South, FT}
east::EB
: Eastern boundary whereEB <: AbstractBoundary{East, FT}
west::WB
: Western boundary whereWB <: AbstractBoundary{West, FT}
topography::tT
: Field of topography elements whereTT <: TopographyField{FT}
- All
FT
values above must be the same float type to form a valid domain. - The code depends on the boundaries forming a rectangle oriented along the cartesian grid. Other shapes/orientations are not supported at this time.
Here is how to construct an MovingBoundary
:
Domain(; north::NB, south::SB, east::EB, west::WB, topography::TT = nothing)
The user must provide the four (4) boundaries. They then have an option to provide topography. If no topography is provided, an empty TopographyField
will be created with the same FT
as the first of the boundaries (which should be shared amoung boundaries).
Keyword arguments
north::NB
: domain's northern boundarysouth::SB
: domain's southern boundaryeast::EB
: domain's eastern boundarywest::WB
: domain's western boundarytopography::TT
: domain's topography field, if there is one, else an empty field is created
Examples
- Creating a
Domain
with NOtopography
julia> grid = RegRectilinearGrid(Float32; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 20);
julia> north = OpenBoundary(North, Float64; grid);
julia> south = OpenBoundary(South, Float64; grid);
julia> east = PeriodicBoundary(East, Float64; grid);
julia> west = PeriodicBoundary(West, Float64; grid);
julia> Domain(; north, south, east, west)
Domain
⊢Northern boundary of type OpenBoundary{North, Float64}
⊢Southern boundary of type OpenBoundary{South, Float64}
⊢Eastern boundary of type PeriodicBoundary{East, Float64}
⊢Western boundary of type PeriodicBoundary{West, Float64}
∟0-element TopograpahyElement{Float64} list
- Creating a
Domain
withtopography
julia> import GeoInterface as GI;
julia> topo_polys = [GI.Polygon([[(1e4, 1e4), (1e4, 3e4), (3e4, 1e4), (1e4, 1e4)]]), GI.Polygon([[(8e4, 8e4), (8e4, 9e4), (9e4, 9e4), (9e4, 8e4), (8e4, 8e4)]])];
julia> topography = initialize_topography_field(; polys = topo_polys);
julia> Domain(; north, south, east, west, topography)
Domain
⊢Northern boundary of type OpenBoundary{North, Float64}
⊢Southern boundary of type OpenBoundary{South, Float64}
⊢Eastern boundary of type PeriodicBoundary{East, Float64}
⊢Western boundary of type PeriodicBoundary{West, Float64}
∟2-element TopograpahyElement{Float64} list
Ocean
Subzero.Ocean
— TypeOcean{FT}
Each simulation needs ocean values on the grid to drive the ice floes. The Ocean
struct holds the needed fields to perform coupling, including vector fields (like u-velocity, v-velocity, x-stress, and y-stress) and tracer fields (like temperature, heatflux, and dissolved ice mass). We also hold and calculate the sea ice fraction in any given grid cell.
Right now, all values are stored on grid points as defined by an AbstractRectilinearGrid
. This should eventually change to a c-grid. If interested in this change, see issue #30. This will require a big change in behavior, but is an important change for consistency and to match with Oceananigans for two-way coupling.
Coupling beyond velocity (for example thermodynamically) is not very well developed. If you are interested in working on this see issue #9. The hflx_factor
field is related to thermodynamic coupling.
Fields
u::Matrix{FT}
: ocean u-velocities in the x-direction on each grid pointv::Matrix{FT}
: ocean v-velocities in the y-direction on each grid pointtemp::Matrix{FT}
: ocean temperature on each grid pointhflx_factor::Matrix{FT}
: factor to calculate the ocean-atmosphere heat flux for a grid cellscells::Matrix{CellStresses}
: used to accumulate stresses on the ocean per grid cell from floes in cell (seeCellStresses
)τx::Matrix{FT}
: stress on the ocean in the x-direction on each grid pointτy::Matrix{FT}
: stress on the ocean in the y-direction on each grid pointsi_frac::Matrix{FT}
: fraction of area in each grid cell (centered on grid points) that is covered in sea-ice (between 0-1)dissolved::Matrix{FT}
: total mass from ice floes dissolved in each grid cell (centered on grid points)
If a periodic boundary is used in the domain, the last grid cell in that direction will not be used as it is equivalent to the first grid cell. Thus, for all of these fields, the first and last value in the x and/or y direction should be equal if the east-west or north-south boundary pair are periodic respectively. In the future, it might be worth having the ocean re-sizing to not included this repeated point dispatching off of the boundary types.
For each of the fields, the rows represent the x
-values of the grid, while the columns represent the y
-values of the grid. This makes it easy to index into the grid for a point (x, y)
, although it does mean that the fields provided don't "look" like the grid if plotted directly and instead need to be transposed.
Here is how to construct an Ocean
:
Ocean([FT = Float64]; u, v, temp, grid = nothing)
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
u::Union{Real, Matrix{Real}}
: user can either provide a single u-velocity value (a constant field the size of thegrid
will be created) or provide a matrix field of u-velocity valuesv::Union{Real, Matrix{Real}}
: user can either provide a single v-velocity value (a constant field the size of thegrid
will be created) or provide a matrix field of v-velocity valuestemp::Union{Real, Matrix{Real}}
: user can either provide a single temperature value (a constant field the size of thegrid
will be created) or provide a matrix field of temperature valuesgrid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulation
The user MUST provide u
, v
, and temp
, although they have the option of these being Real
values or Matrix{<:Real}
. If the user chooses to provide Real
values for any of those fields, then a grid
IS required to create a constant field of the correct size. If user chooses to provide a field, they should be of size (grid.Nx + 1, grid.Ny + 1)
and the user doesn't require a grid
input.
Examples
- Creating
Ocean
with constantu
-velocity,v-velocity
, andtemp
julia> grid = RegRectilinearGrid(; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 10);
julia> Ocean(; u = 0.5, v = 0.25, temp = 0.0, grid)
Ocean{Float64}
⊢Vector fields of dimension (21, 11)
⊢Tracer fields of dimension (21, 11)
⊢Average u-velocity of: 0.5 m/s
⊢Average v-velocity of: 0.25 m/s
∟Average temperature of: 0.0 C
- Creating
Ocean
with user-definedu
andv
and a constanttemp
withFloat32
data
julia> import Random.Xoshiro;
julia> seeded_rng = Xoshiro(100);
julia> u_field = rand(seeded_rng, grid.Nx + 1, grid.Ny + 1);
julia> v_field = rand(seeded_rng, grid.Nx + 1, grid.Ny + 1);
julia> Ocean(Float32; u = u_field, v = v_field, temp = 0.0, grid)
Ocean{Float32}
⊢Vector fields of dimension (21, 11)
⊢Tracer fields of dimension (21, 11)
⊢Average u-velocity of: 0.4856711 m/s
⊢Average v-velocity of: 0.5319979 m/s
∟Average temperature of: 0.0 C
- Trying to create an
Ocean
with a constant field and NO grid
julia> Ocean(; u = 0.2, v = 0.1, temp = 0.0)
ERROR: ArgumentError: To create a matrix from the constant value provided, you must provide a grid.
[...]
Atmosphere
Subzero.Atmos
— TypeAtmos{FT}
Each simulation needs atmosphere/wind values on the grid to drive the ice floes. The Atmos
struct holds the needed fields to perform coupling of vector fields (u-velocity and v-velocity) and tracer fields (temperature).
Right now, all values are stored on grid points as defined by an AbstractRectilinearGrid
. This should eventually change to a c-grid. If interested in this change, see issue #30.
Coupling beyond velocity (for example thermodynamically) is not very well developed. If you are interested in working on this see issue #9. The hflx_factor
field is related to thermodynamic coupling.
Fields
u::Matrix{FT}
: atmosphere/wind u-velocities in the x-direction on each grid pointv::Matrix{FT}
: atmosphere/wind v-velocities in the y-direction on each grid pointtemp::Matrix{FT}
: atmosphere/wind temperature on each grid point
If a periodic boundary is used in the domain, the last grid cell in that direction will not be used as it is equivalent to the first grid cell. Thus, for all of these fields, the first and last value in the x and/or y direction should be equal if the east-west or north-south boundary pair are periodic respectively. In the future, it might be worth having the atmos re-sizing to not included this repeated point dispatching off of the boundary types.
For each of the fields, the rows represent the x
-values of the grid, while the columns represent the y
-values of the grid. This makes it easy to index into the grid for a point (x, y)
, although it does mean that the fields provided don't "look" like the grid if plotted directly and instead need to be transposed.
Here is how to construct an Atmos
:
Atmos([FT = Float64]; u, v, temp, grid = nothing)
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
u::Union{Real, Matrix{Real}}
: user can either provide a single u-velocity value (a constant field the size of thegrid
will be created) or provide a matrix field of u-velocity valuesv::Union{Real, Matrix{Real}}
: user can either provide a single v-velocity value (a constant field the size of thegrid
will be created) or provide a matrix field of v-velocity valuestemp::Union{Real, Matrix{Real}}
: user can either provide a single temperature value (a constant field the size of thegrid
will be created) or provide a matrix field of temperature valuesgrid::AbstractRectilinearGrid
: subtype of AbstractRectilinearGrid representing the grid used within a simulation
The user MUST provide u
, v
, and temp
, although they have the option of these being Real
values or Matrix{<:Real}
. If the user chooses to provide Real
values for any of those fields, then a grid
IS required to create a constant field of the correct size. If user chooses to provide a field, they should be of size (grid.Nx + 1, grid.Ny + 1)
and the user doesn't require a grid
input.
Examples
- Creating
Atmos
with constantu
-velocity,v-velocity
, andtemp
julia> grid = RegRectilinearGrid(; x0 = 0.0, xf = 5e5, y0 = 0.0, yf = 5e5, Nx = 20, Ny = 10);
julia> Atmos(; u = 0.5, v = 0.25, temp = 0.0, grid)
Atmos{Float64}
⊢Vector fields of dimension (21, 11)
⊢Tracer fields of dimension (21, 11)
⊢Average u-velocity of: 0.5 m/s
⊢Average v-velocity of: 0.25 m/s
∟Average temperature of: 0.0 C
- Creating
Atmos
with user-definedu
andv
and a constanttemp
withFloat32
data
julia> import Random.Xoshiro;
julia> seeded_rng = Xoshiro(200);
julia> u_field = rand(seeded_rng, grid.Nx + 1, grid.Ny + 1);
julia> v_field = rand(seeded_rng, grid.Nx + 1, grid.Ny + 1);
julia> Atmos(Float32; u = u_field, v = v_field, temp = 0.0, grid)
Atmos{Float32}
⊢Vector fields of dimension (21, 11)
⊢Tracer fields of dimension (21, 11)
⊢Average u-velocity of: 0.4965465 m/s
⊢Average v-velocity of: 0.5340565 m/s
∟Average temperature of: 0.0 C
- Trying to create an
Atmos
with a constant field and NO grid
julia> Atmos(; u = 0.2, v = 0.1, temp = 0.0)
ERROR: ArgumentError: To create a matrix from the constant value provided, you must provide a grid.
[...]
Model
Subzero.Model
— TypeModel which holds grid, ocean, atmos structs, each with the same underlying float type (either Float32 of Float64) and size. It also holds the domain information, which includes the topography and the boundaries. It holds a StructArray of floe structs, again each relying on the same underlying float type. Finally, it also holds the maximum floe id used thus far in the simulation. This should be the length of the floes array at the beginning of the run.
Developer-Used Types
Subzero.CellFloes
— TypeCellFloes{FT}
A CellFloes
struct represents a single grid cell and accumulates the indices of floes with area in that grid cell. This is used to accumulate the forces of floes in a grid cell on the ocean below it. Due to the prevalence of periodic boundaries, the grid cell represented by a CellFloes
object are centered on grid points (translated by Δx/2
and Δy/2
in the x and y directions for a RegRectilinearGrid
), rather than on the grid cells defined by the grid object itself. Floes are recorded with their index in the list of floes. Furthermore, in a model with periodic boundaries, some floes may be in multiple grid cells on different edges of the domain if they pass through a periodic boundary. In these cases, the floe "linked" to with its index must be translated by a vector to get its "ghost" on the other side of the domain. This non-integer translation data is of float type FT
. CellFloes
are used with CellStresses
objects, which aggregate the stress from each of the floes recorded in a given CellFloes
object.
Fields
floeidx
: see keyword argumentsΔx
: see keyword argumentsΔy
: see keyword arguments
Here is how to construct a CellFloes
object:
CellFloes([FT = Float64]; floeidx = nothing, Δx = nothing, Δy = nothing)
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
floeidx::Vector{Int}
: vector of floe indicies in the list of model floes for floes with area in the gridΔx::Vector{FT}
: vector of x-translations for a floe at the corresponding index of the
floeidx
vector to be in the cell represented with the CellFloes
object.
Δy::Vector{FT}
: vector of y-translations for a floe at the corresponding index of the
floeidx
vector to be in the cell represented with the CellFloes
object.
If no keyword arguments are provide by the user, an CellFloes
object with empty fields will be created. This is the standard useage of these objects and they are added to during the coupling step. If keyword arguments are provided, then all three must be provided and each vector must be the same size.
Subzero.CellStresses
— TypeCellStresses{FT}
Struct to collect stress from ice floes on ocean grid cells. One CellStresses
corresponds to one grid cell and is "linked" to a corresponding CellFloes
object. The CellFloes
object records which floes are in a given grid cell and the CellStresses
aggregates the total stress felt on the ocean from the ice (on each of the floe's sub-floe points generated by a subtype of AbstractSubFloePointsGenerator
) in a given grid cell. This is needed ONLY for two-way coupling. Due to the prevalence of periodic boundaries, the grid cell represented by a CellStresses
object are centered on grid points (translated by Δx/2
and Δy/2
in the x and y directions for a RegRectilinearGrid
), rather than on the grid cells defined by the grid object itself. A CellStresses
object holds a list of stresses on the ocean where each element is from a single floe within grid cell. The ith element of the τx
and τy
fields are measures of the total stress from the ith floe in the cell and the npoints
field is a measure of how many of the ith floe's sub-floe points contributed to the running total of the stress. Again, each element in the list corresponds to one floe, which is denoted in the corresponding CellFloes
matrix within the grid.
Fields
τx::Vector{FT}
: list of total x-stress caused by each floe in a cell on the oceanτy::Vector{FT}
: list of total y-stress caused by each floe in a cell on the oceannpoints::Vector{Int}
: list of total number of sub-floe points contributing to each individial floe's stress
Here is how to construct a CellStresses
object:
CellStresses([FT = Float64]; τx = nothing, τy = nothing, npoints = nothing)
Positional arguments
FT::Type{<:AbstractFloat}
: Float type used to run the simulation, eitherFloat64
(default) orFloat32
.
Keyword arguments
τx::Vector{FT}
: list of total x-stress caused by each floe in a cell on the oceanτy::Vector{FT}
: list of total y-stress caused by each floe in a cell on the oceannpoints::Vector{Int}
: list of total number of sub-floe points contributing to each individial floe's stress
If no keyword arguments are provide by the user, an CellStresses
object with empty fields will be created. This is the standard useage of these objects and they are added to during the coupling step. If keyword arguments are provided, then all three must be provided and each vector must be the same size.
Subzero.TopographyField
— TypeTopographyField{FT}
Alias for StructArray type with TopographyElement elements with data of type FT
.
This is not designed to be used by users, but is useful for dispatch by developers.