Skip to content

Commit 130d1e8

Browse files
committed
Renaming some things
1 parent 78b50c5 commit 130d1e8

6 files changed

Lines changed: 110 additions & 84 deletions

File tree

src/domain/create_domain.jl

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ Domain{T, N, M}
55
66
Data structure describing the computational domain for the simulation.
77
8-
$(FIELDS)
98
"""
109
struct Domain{T, N, M}
1110
# The mask describing where the points are located on the grid.
@@ -27,56 +26,78 @@ struct Domain{T, N, M}
2726
end
2827

2928
"""
30-
Construct a Domain using vectors of longitudes and latitudes, a land_area_fraction array and a surface class mapping. The shape of the longitudes by latitudes should match the trailing dimensions of the land_area_fraction array. This signature is used for gridded domains.
29+
Construct a Domain using vectors of longitudes and latitudes, a vegetation_area_fraction array and a surface class mapping. The shape of the longitudes by latitudes should match the trailing dimensions of the vegetation_area_fraction array. This signature is used for gridded domains.
3130
3231
If the lon_bnds and lat_bnds are not supplied, then they are inferred from the passed latitudes and longitudes. If `normalise` is true, the sum of fractions in a grid cell is normalised to 1.
3332
"""
34-
function Domain(lons, lats, land_area_fraction{T, 3}, mapping; lon_bnds=nothing, lat_bnds=nothing, mask=nothing, normalise=false) where {T}
33+
function Domain(lons, lats, vegetation_area_fraction::Array{T, 3}, mapping; lon_bnds=nothing, lat_bnds=nothing, mask=nothing, normalise=false) where {T}
3534

3635
# First validate the land area fraction. This includes checking the mapping is workable
3736
# with the passed land fractions i.e. the user hasn't denoted a tile index greater than
3837
# the number of slices in the array
39-
@assert (size(land_area_fraction, 2), size(land_area_fraction, 3)) == (length(lons), length(lats)), "The size of the land_area_fraction and the supplied longitudes and latitudes are not compatible. Size of land_area_fraction: $(size(land_area_fraction)), with lons: $(length(lons)) and lats: $(length(lats))"
38+
@assert size(vegetation_area_fraction, 1) == length(lons) && size(vegetation_area_fraction, 2) == length(lats) "The size of the vegetation_area_fraction and the supplied longitudes and latitudes are not compatible. Size of vegetation_area_fraction: $(size(vegetation_area_fraction)), with lons: $(length(lons)) and lats: $(length(lats))"
4039

4140
# Convert the mapping values to vectors for consistent handling and array accessing,
4241
# then validate
4342
mapping = Dict(surface => inds isa Vector ? inds : [inds] for (surface, inds) in mapping)
44-
validate_surface_mapping(land_area_fraction, mapping)
43+
validate_surface_mapping(vegetation_area_fraction, mapping)
4544

4645
# Were the bounds supplied?
4746
if (isnothing(lon_bnds))
4847
lon_bnds = infer_grid_bounds(lons)
4948
end
49+
check_bounds_valid(lons, lon_bnds)
5050

5151
if (isnothing(lat_bnds))
5252
lat_bnds = infer_grid_bounds(lats)
5353
end
54+
check_bounds_valid(lats, lat_bnds)
55+
56+
# When creating the mask, we only want to work with the indices representing active surface
57+
# types. So cut out a view of those indices from the complete land area fractions
58+
active_inds = [ind for indices in values(mapping) for ind in indices]
59+
active_area_fraction = @view(vegetation_area_fraction[:, :, active_inds])
60+
61+
# Now use this view to create the mask
62+
if isnothing(mask)
63+
# Note that non-land points may be treated as missing
64+
frac_sums = dropdims(sum(active_area_fraction, dims=3), dims=3)
65+
mask = @. !ismissing(frac_sums) && frac_sums > 0.0
66+
else
67+
@assert size(mask) == (size(vegetation_area_fraction, 1), size(vegetation_area_fraction, 2)), "Supplied mask does not match the shape of the land area fractions."
68+
end
5469

55-
# Now we have handled everything that is specific to the gridded domains- reshape the
56-
# data so that it can be treated in the same way as site runs.
57-
# This means that the land_area_fraction should represent spatial points in a single
58-
# dimension, and the mask should be a vector (if supplied)
59-
land_area_fractions = reshape(land_area_fractions, (size(land_area_fractions), :))
60-
mask = isnothing(mask) ? mask : vec(mask)
70+
# Optionally normalise the fractions
71+
if normalise
72+
# Note that this modifies in place, in the variable that is a view into the
73+
# original array
74+
active_area_fraction .= active_area_fraction ./ sum(active_fractions, dims=2)
75+
end
6176

77+
# Use the mask and land fractions to yield the index/fraction vectors
78+
tiles, indices, fractions = process_vegetation_area_fractions(vegetation_area_fraction, mapping, mask)
79+
6280
# Vectorize the dimensions
6381
lons = repeat(lons, inner=length(lats), outer=1)
6482
lats = repeat(lats, inner=1, outer=length(lons))
6583
lon_bnds = repeat(lon_bnds, inner=(1, length(lats)), outer=(1, 1))
6684
lat_bnds = repeat(lat_bnds, inner=(1, 1), outer=(1, length(lons)))
6785

6886
# Call the generic function used for both domain types
69-
Domain(lons, lats, land_area_fractions, mapping; lon_bnds=lon_bnds, lat_bnds=lat_bnds, mask=mask, normalise=normalise)
87+
Domain{eltype(vegetation_area_fraction), length(tiles), 2}(mask, lons, lats, lon_bnds, lat_bnds, tiles, indices, fractions)
7088

7189
end
7290

7391
"""
74-
Construct a Domain using vectors of x and y coordinates (typically latitude and longitude), a land_area_fraction array and a surface class mapping. The longitudes and latitudes are paired, representing specific locations. This signature is used for site domains, and for processing of gridded domains after some preprocessing.
92+
Construct a Domain using vectors of x and y coordinates (typically latitude and longitude), a vegetation_area_fraction array and a surface class mapping. The longitudes and latitudes are paired, representing specific locations. This signature is used for site domains, and for processing of gridded domains after some preprocessing.
7593
7694
If the lon_bnds and lat_bnds are not supplied, then it is assumed that the coordinates represent longitudes and latitudes, and the size of the grid cells is assumed to be 0.01 degrees.
7795
"""
7896

79-
function Domain(lons, lats, land_area_fraction{T 2}, mapping; lon_bnds=nothing, lat_bnds=nothing, mask=nothing, normalise=false) where {T}
97+
function Domain(lons, lats, vegetation_area_fraction::Matrix{T}, mapping; lon_bnds=nothing, lat_bnds=nothing, mask=nothing, normalise=false) where {T}
98+
99+
# Check that the lons and lats are valid
100+
@assert length(lons) == length(lats) == size(vegetation_area_fraction, 2) "The longitudes, latitudes and number of columns in the vegetation_area_fraction array must be the same."
80101

81102
# Create the lons and lats bounds if not supplied
82103
if isnothing(lon_bnds)
@@ -86,65 +107,70 @@ function Domain(lons, lats, land_area_fraction{T 2}, mapping; lon_bnds=nothing,
86107
if isnothing(lat_bnds)
87108
lat_bnds = infer_site_bounds(lats)
88109
end
89-
# Verify that the bounds are valid if supplied
90-
#
110+
111+
# Validate that the bounds are valid
112+
check_bounds_valid(lons, lon_bnds)
113+
check_bounds_valid(lats, lat_bnds)
114+
91115
# Now we start processing the land fractions- we want to work with 2 copies of the
92-
# underlying land_area_fractions. One which is the full array of data, that we can
116+
# underlying vegetation_area_fractions. One which is the full array of data, that we can
93117
# index using the values in the mapping, and one which is a view of only the slices
94118
# included in the mapping.
95-
# All the indices included in the mapping
96119
all_inds = [ind for indices in values(mapping) for ind in indices]
97-
active_fractions = @view(land_area_fractions[all_inds, :])
120+
active_area_fraction = @view(vegetation_area_fractions[:, all_inds])
98121

99122
# Set the mask
100123
if (isnothing(mask))
101124
# Not supplied- created the mask. Take all of the "pages" of the land area array
102125
# that are mapped to a surface class, and check which locations have a non-zero
103126
# total surface fraction (and not missing which may represent ocean)
104-
frac_sum = dropdims(sum(active_fractions, dims=1), dims=1)
127+
frac_sum = dropdims(sum(active_area_fraction, dims=2), dims=2)
105128
mask = @. !ismissing(frac_sum) && frac_sum > 0.0
106129
else
107130
# Ensure that the mask size is compatible
108-
@assert size(mask) == size(land_area_fraction, 2)
131+
@assert length(mask) == size(vegetation_area_fraction, 1) "Supplied mask does not match the size of the vegetation_area_fraction."
109132
end
110133

111134
# Optionally normalise the fractions
112135
if normalise
113136
# Note that this modifies in place, in the variable that is a view into the
114137
# original array
115-
active_fractions .= active_fractions ./ sum(active_fractions, dims=1)
138+
active_fractions .= active_area_fraction ./ sum(active_area_fraction, dims=2)
116139
end
117140

118141
# Now create the vectors for the respective surface classes
119-
tiles, indices, fractions = process_land_area_fractions_and_mapping(land_area_fraction, mapping, mask)
142+
tiles, indices, fractions = process_vegetation_area_fractions_and_mapping(vegetation_area_fraction, mapping, vec(mask))
143+
144+
# Call the generic function used for both domain types
145+
Domain{eltype(vegetation_area_fraction), length(tiles), 1}(mask, lons, lats, lon_bnds, lat_bnds, tiles, indices, fractions)
120146

121147
end
122148

123149
"""
124-
Ensure that the specified mapping is valid with the given land area fractions. The only condition is that the mapping doesn't contain any indices outside the range available in the land area fractions i.e. 1 <= indx <= size(land_area_fraction, 1)
150+
Ensure that the specified mapping is valid with the given land area fractions. The only condition is that the mapping doesn't contain any indices outside the range available in the land area fractions i.e. 1 <= indx <= size(vegetation_area_fraction, 1)
125151
"""
126-
function validate_surface_mapping(land_area_fraction, mapping)
152+
function validate_surface_mapping(vegetation_area_fraction, mapping)
127153
min_indx = 1
128-
max_indx = size(land_area_fraction, 1)
154+
max_indx = size(vegetation_area_fraction, 1)
129155
check_failed = false
130156
for (surface, inds) in mapping
131157
for ind in inds
132158
if !(min_indx <= ind <= max_indx)
133-
@warn "Index $ind for surface class $surface falls outside the allowed range of 1 to size(land_area_fraction, 1), which is $max_indx"
159+
@warn "Index $ind for surface class $surface falls outside the allowed range of 1 to size(vegetation_area_fraction, 1), which is $max_indx"
134160
check_failed = true
135161
end
136162
end
137163
end
138164

139-
@assert !check_failed, "Supplied mapping is invalid for the given land area fractions"
165+
@assert !check_failed "Supplied mapping is invalid for the given land area fractions"
140166
end
141167

142168
"""
143169
Infer the cell bounds for gridded domains, based on the list of cell centre locations passed.
144170
"""
145171
function infer_grid_bounds(points::Vector{T}) where {T}
146172
# To infer the bounds, assume that the coordinates are ascending
147-
@assert issorted(points), "Supplied points are not monotonically ascending"
173+
@assert issorted(points) "Supplied points are not monotonically ascending"
148174

149175
bounds = Matrix{T}(undef, 2, length(points))
150176
for i = 1:length(points)
@@ -158,8 +184,8 @@ function infer_grid_bounds(points::Vector{T}) where {T}
158184
lower = points[i] - points[i-1]
159185
upper = points[i+1] - points[i]
160186
end
161-
bounds[1, i] = points[1] - 0.5 * lower
162-
bounds[2, i] = points[1] + 0.5 * upper
187+
bounds[1, i] = points[i] - 0.5 * lower
188+
bounds[2, i] = points[i] + 0.5 * upper
163189
end
164190

165191
bounds
@@ -168,7 +194,7 @@ end
168194
"""
169195
Infer the cell bounds for site domains, based on the list of cell centre locations passed.
170196
"""
171-
function infer_grid_bounds(points::Vector{T}) where {T}
197+
function infer_site_bounds(points::Vector{T}) where {T}
172198

173199
bounds = Matrix{T}(undef, 2, length(points))
174200
bounds[1, :] = points .- 0.01
@@ -181,29 +207,32 @@ end
181207
Validate that the bounds are appropriate for the specified longitudes
182208
"""
183209
function check_bounds_valid(points, point_bnds)
184-
@assert all(@view(point_bnds[1, :]) .< points .< @view(point_bnds[2, :])), "Some bounds are do not bracket their associated point."
210+
@assert all(@view(point_bnds[1, :]) .< points .< @view(point_bnds[2, :])) "Some bounds are do not bracket their associated point."
185211
end
186212

187213
"""
188214
Process the given land area fractions and the mapping to create the `tile`, `indices` and `fractions` components of the `Domain` derived type.
189215
"""
190-
function process_land_area_fractions_and_mapping(land_area_fraction::Matrix{T}, mapping, mask::Vector) where {T}
216+
function process_vegetation_area_fractions(vegetation_area_fraction, mapping, mask)
191217

218+
# First ensure that the land area fraction and mask are vectorized
219+
vegetation_area_fraction = reshape(vegetation_area_fraction, (:, size(vegetation_area_fraction, 1)))
220+
mask = vec(mask)
192221

193222
tiles = (); indices = (); fractions = ()
194223

195224
for (surface_class, mapped_indices) in mapping
196-
class_fractions = dropdims(sum(@view(land_area_fraction[mapped_indices]
197-
components = Tuple(begin
198-
# We need to track the index in terms of land points
199-
land_counter=1
200-
for (ind, m) in enumerate(mask)
201-
if m
202-
203-
225+
# Summate over the indices assigned to the surface class, then iterate through the non-zero and non-masked fractions
226+
class_total = dropdims(sum(@view(vegetation_area_fraction[mapped_indices, :]), dims=2), dims=2)
227+
indices_fracs = [(i, frac) for (i, (m, frac)) in enumerate(zip(mask, class_total)) if (m && frac > 0.0)]
204228

205-
for (surface_class, mapped_indices) in mapping
206229
tiles = (tiles..., surface_class)
230+
indices = (indices..., getindex.(indices_fracs, 1))
231+
fractions = (fractions..., getindex.(indices_fracs, 2))
232+
end
233+
234+
tiles, indices, fractions
235+
end
207236

208237
"""
209238
define_on_domain(vars, domain::Domain{T}, dims=()
@@ -224,15 +253,3 @@ function define_on_domain(vars, domain::Domain{T}, dims=()) where {T}
224253

225254
which_domain, ComponentVector(t)
226255
end
227-
228-
function check_land_domain(domain::Domain, data::Array{T, 3}) where {T}
229-
for i = 1:size(data, 3)
230-
@assert all(domain.mask .== .!(ismissing.(@view(data[:, :, i])))), "Supplied data is expected to be on the same grid as the land mask, but it is not."
231-
end
232-
end
233-
234-
function check_land_domain(domain::Domain, data::Array{T, 4}) where {T}
235-
for i = 1:size(data, 3), j = 1:size(data, 4)
236-
@assert all(domain.mask .== .!(ismissing.(@view(data[:, :, i, j])))), "Supplied data is expected to be on the same grid as the land mask, but it is not."
237-
end
238-
end
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# Not sure what traits a water surface may have yet
22

3-
const ICE_ALLOWED_TRAITS = Dict()
3+
const ICE_REQUIRED_TRAITS = Dict()
44

5-
# The master water surface type
6-
abstract type IceSurface <: SurfaceType end
5+
# The master ice surface type
6+
abstract type IceSurface <: SurfaceClass end
77

88
"""
99
@IceType name, traits
1010
1111
Define a new ice surface, which acts a functional type and subtype of WaterSurface.
1212
"""
13-
macro IceType(name, ice_traits...)
14-
traits = read_surface_traits(ICE_ALLOWED_TRAITS, ice_traits)
13+
macro IceSurface(name, ice_traits...)
14+
traits = read_surface_traits(ICE_REQUIRED_TRAITS, ice_traits)
1515

1616
esc(quote
1717
struct $(name) <: IceSurface end
1818

1919
$name()
2020
end)
2121
end
22-
export @IceType, IceSurface
22+
export @IceSurface, IceSurface

src/surface_types/surface_types.jl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
abstract type SurfaceType end
1+
abstract type SurfaceClass end
22

3-
function read_surface_traits(allowed_traits, passed_traits)
3+
"""
4+
Read the surface traits passed, and check they form the complete set of traits required for the class.
5+
"""
6+
function read_surface_traits(required_traits, passed_traits)
47
traits = Dict()
58
for trait_spec in passed_traits
69
if trait_spec.head == :(=)
7-
if trait_spec.args[1] in keys(allowed_traits)
8-
trait_options = allowed_traits[trait_spec.args[1]]
10+
if trait_spec.args[1] in keys(required_traits)
11+
trait_options = required_traits[trait_spec.args[1]]
912
if trait_spec.args[2] in keys(trait_options)
1013
traits[trait_spec.args[1]] = trait_options[trait_spec.args[2]]
1114
else
1215
@error "$(trait_spec.args[2]) is an unrecognised value for the $(trait_spec.args[1]) trait. Options are $(keys(trait_options))."
1316
end
1417
else
15-
@error "$(trait_spec.args[1]) is an unrecognised PFT trait- must be one of $(keys(allowed_traits))"
18+
@error "$(trait_spec.args[1]) is an unrecognised PFT trait- must be one of $(keys(required_traits))"
1619
end
1720
end
1821
end
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# Not sure what traits an urban surface may have
22

3-
const URBAN_ALLOWED_TRAITS = Dict()
3+
const URBAN_REQUIRED_TRAITS = Dict()
44

55
# The master urban surface type
6-
abstract type UrbanSurface <: SurfaceType end
6+
abstract type UrbanSurface <: SurfaceClass end
77

88
"""
9-
@UrbanType name, traits
9+
@UrbanSurface name, traits
1010
1111
Define a new Urban surface, which acts a functional type and subtype of WaterSurface.
1212
"""
13-
macro UrbanType(name, urban_traits...)
14-
traits = read_surface_traits(URBAN_ALLOWED_TRAITS, urban_traits)
13+
macro UrbanSurface(name, urban_traits...)
14+
traits = read_surface_traits(URBAN_REQUIRED_TRAITS, urban_traits)
1515

1616
esc(quote
1717
struct $(name) <: UrbanSurface end
1818

1919
$name()
2020
end)
2121
end
22-
export @UrbanType, UrbanSurface
22+
export @UrbanSurface, UrbanSurface

src/surface_types/vegetated_surface/define_vegetated_surface.jl

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,26 @@ const PHENOLOGY_TRAITS = Dict(:deciduous => Deciduous(),
99
)
1010

1111
# The set of allowed traits
12-
const PFT_ALLOWED_TRAITS = Dict(:phenology => PHENOLOGY_TRAITS
12+
const VEGETATED_REQUIRED_TRAITS = Dict(:phenology => PHENOLOGY_TRAITS
1313
)
1414

1515
# The master VegetationType
16-
abstract type VegetatedSurface <: SurfaceType end
16+
abstract type VegetatedSurface <: SurfaceClass end
1717

1818
"""
1919
@PFT name, traits
2020
21-
Define a new PFT, which acts as a functional type and subtype of VegetationType, with
22-
the specified traits.
21+
An alias for @VegetatedSurface.
2322
"""
2423
macro PFT(name, PFT_traits...)
25-
traits = read_surface_traits(PFT_ALLOWED_TRAITS, PFT_traits)
24+
:(@VegetatedSurface $name $PFT_traits)
25+
end
26+
27+
"""
28+
Define a new VegetatedSurface, which acts as a functional type and subtype of VegetatedSurface, with the specified traits.
29+
"""
30+
macro VegetatedSurface(name, vegetated_traits...)
31+
traits = read_surface_traits(VEGETATED_REQUIRED_TRAITS, vegetated_traits)
2632

2733
esc(quote
2834
struct $(name) <: VegetatedSurface end
@@ -32,4 +38,4 @@ macro PFT(name, PFT_traits...)
3238
$name()
3339
end)
3440
end
35-
export @PFT, VegetatedSurface
41+
export @PFT, @VegetatedSurface, VegetatedSurface

0 commit comments

Comments
 (0)