Skip to content

Commit 13633ca

Browse files
committed
Add scalar_fieldmatrix
Add a function to convert a FieldMatrix where each matrix entry has an eltype of some struct into a FieldMatrix where each entry has an eltype of a scalar. Add additional tests for scalar_matrixfields Use @test_all in tests Make suggested changes to tests and field_name_dict.jl Revert unrolled_findfirst Clean up field matrix tests and add support for DiagonalMatrixRows CamelCase struct name Clean up tests and get_scalar_keys wip backup Minimal working with allocs WIP1 WIP more allocs fix Assorted cleanup Fix dx/dx case reduce code duplication; fix example Add gpu test further cleanup, extend diagonalrow fix names test and comments Add docs docs bugfix remvoe bad refs fix docs formatting
1 parent f880322 commit 13633ca

10 files changed

Lines changed: 898 additions & 83 deletions

File tree

.buildkite/pipeline.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,18 @@ steps:
861861
agents:
862862
slurm_gpus: 1
863863

864+
- label: "Unit: scalar_fieldmatrix (CPU)"
865+
key: cpu_scalar_fieldmatrix
866+
command: "julia --color=yes --check-bounds=yes --project=.buildkite test/MatrixFields/scalar_fieldmatrix.jl"
867+
868+
- label: "Unit: mscalar_fieldmatrix (GPU)"
869+
key: gpu_scalar_fieldmatrix
870+
command: "julia --color=yes --project=.buildkite test/MatrixFields/scalar_fieldmatrix.jl"
871+
env:
872+
CLIMACOMMS_DEVICE: "CUDA"
873+
agents:
874+
slurm_gpus: 1
875+
864876
- group: "Unit: MatrixFields - broadcasting (CPU)"
865877
steps:
866878

docs/src/matrix_fields.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ preconditioner_cache
8989
check_preconditioner
9090
lazy_or_concrete_preconditioner
9191
apply_preconditioner
92+
get_scalar_keys
93+
get_field_first_index_offset
94+
broadcasted_get_field_type
95+
inner_type_ignore_adjoint
9296
```
9397

9498
## Utilities
@@ -98,4 +102,80 @@ column_field2array
98102
column_field2array_view
99103
field2arrays
100104
field2arrays_view
105+
scalar_fieldmatrix
101106
```
107+
108+
## Indexing a FieldMatrix
109+
110+
A FieldMatrix entry can be:
111+
112+
- An `UniformScaling`, which contains a `Number`
113+
- A `DiagonalMatrixRow`, which can contain aything
114+
- A `ColumnwiseBandMatrixField`, where each row is a [`BandMatrixRow`](@ref) where the band element type is representable with the space's base number type.
115+
116+
If an entry contains a composite type, the fields of that type can be extracted.
117+
This is also true for nested composite types.
118+
119+
For example:
120+
121+
```@example 1
122+
using ClimaCore.CommonSpaces # hide
123+
import ClimaCore: MatrixFields, Quadratures # hide
124+
import ClimaCore.MatrixFields: @name # hide
125+
space = Box3DSpace(; # hide
126+
z_elem = 3, # hide
127+
x_min = 0, # hide
128+
x_max = 1, # hide
129+
y_min = 0, # hide
130+
y_max = 1, # hide
131+
z_min = 0, # hide
132+
z_max = 10, # hide
133+
periodic_x = false, # hide
134+
periodic_y = false, # hide
135+
n_quad_points = 1, # hide
136+
quad = Quadratures.GL{1}(), # hide
137+
x_elem = 1, # hide
138+
y_elem = 2, # hide
139+
staggering = CellCenter() # hide
140+
) # hide
141+
nt_entry_field = fill(MatrixFields.DiagonalMatrixRow((; foo = 1.0, bar = 2.0)), space)
142+
nt_fieldmatrix = MatrixFields.FieldMatrix((@name(a), @name(b)) => nt_entry_field)
143+
nt_fieldmatrix[(@name(a), @name(b))]
144+
```
145+
146+
The internal values of the named tuples can be extracted with
147+
148+
```@example 1
149+
nt_fieldmatrix[(@name(a.foo), @name(b))]
150+
```
151+
152+
and
153+
154+
```@example 1
155+
nt_fieldmatrix[(@name(a.bar), @name(b))]
156+
```
157+
158+
If the key `(@name(name1), @name(name2))` corresponds to an entry, then
159+
`(@name(foo.bar.buz), @name(biz.bop.fud))` would be the internal key for the key
160+
`(@name(name1.foo.bar.buz), @name(name2.biz.bop.fud))`.
161+
162+
Currently, internal values cannot be extracted in all situations. Extracting interal values
163+
works when:
164+
165+
- The second name in the internal key is empty, and the first name in the internal key accesses internal values for the type of element contained in each row of the entry. This does not work when the element type of each row is a 2d tensor.
166+
167+
- The first name in the internal key is empty, and the type of element contained in each row of the entry is an `AxisVector` or the adjoint of an `AxisVector`. In this case, the second name must access inernal values for the type of `AxisVector` contained in each row.
168+
169+
- The element type of each row in the entry is a 2d tensor, and the internal key is of the form `(@name(components.data.:(1)), @name(components.data.:(2)))`, but possibly with different numbers to index into the 2d tensor
170+
171+
- The element type of each row in the entry is some number of nested `Tuple`s and `NamedTuple`s, and the first name in the internal key accesses an `AxisVector` or the adjoint of an `AxisVector` from the outer `Tuple`/`NamedTuple`, and the second name in the inernal key accesses a component of the `AxisVector`
172+
173+
If the `FieldMatrix` represents a Jacobian, then extracting internal values works when an entry represents:
174+
175+
- The partial derrivative of an `AxisVector`, `Tuple`, or `NamedTuple` with respect to a scalar.
176+
177+
- The partial derrivative of a scalar with respect to an `AxisVector`.
178+
179+
- The partial derrivative of a `Tuple`, or `NamedTuple` with respect to an `AxisVector`. In this case, the first name of the internal key must index into the tuple and result in a scalar.
180+
181+
- The partial derrivative of an `AxisVector` with respect to an `AxisVector`. In this case, the partial derrivative of a component of the first `AxisVector` with respect to a component of the second `AxisVector` can be extracted, but not an entire `AxisVector` with respect to a component, or a component with respect to an entire `AxisVector`

src/Geometry/axistensors.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ Base.zero(::Type{AdjointAxisTensor{T, N, A, S}}) where {T, N, A, S} =
308308

309309
const AdjointAxisVector{T, A1, S} = Adjoint{T, AxisVector{T, A1, S}}
310310

311+
const AxisVectorOrAdj{T, A, S} =
312+
Union{AxisVector{T, A, S}, AdjointAxisVector{T, A, S}}
313+
311314
Base.@propagate_inbounds Base.getindex(va::AdjointAxisVector, i::Int) =
312315
getindex(components(va), i)
313316
Base.@propagate_inbounds Base.getindex(va::AdjointAxisVector, i::Int, j::Int) =

src/MatrixFields/MatrixFields.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import ..Utilities: PlusHalf, half
5858
import ..RecursiveApply:
5959
rmap, rmaptype, rpromote_type, rzero, rconvert, radd, rsub, rmul, rdiv
6060
import ..RecursiveApply: , ,
61+
import ..DataLayouts
6162
import ..DataLayouts: AbstractData
6263
import ..DataLayouts: vindex
6364
import ..Geometry

src/MatrixFields/field_name.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ extract_first(::FieldName{name_chain}) where {name_chain} = first(name_chain)
5050
drop_first(::FieldName{name_chain}) where {name_chain} =
5151
FieldName(Base.tail(name_chain)...)
5252

53+
extract_last(::FieldName{name_chain}) where {name_chain} =
54+
name_chain[length(name_chain)]
55+
5356
has_field(x, ::FieldName{()}) = true
5457
has_field(x, name::FieldName) =
5558
extract_first(name) in propertynames(x) &&
@@ -59,6 +62,18 @@ get_field(x, ::FieldName{()}) = x
5962
get_field(x, name::FieldName) =
6063
get_field(getproperty(x, extract_first(name)), drop_first(name))
6164

65+
"""
66+
broadcasted_get_field_type(::Type{X}, name::FieldName)
67+
68+
Returns the type of the field accessed by `name` in the type `X`.
69+
"""
70+
broadcasted_get_field_type(::Type{X}, ::FieldName{()}) where {X} = X
71+
broadcasted_get_field_type(::Type{X}, name::FieldName) where {X} =
72+
broadcasted_get_field_type(
73+
fieldtype(X, extract_first(name)),
74+
drop_first(name),
75+
)
76+
6277
broadcasted_has_field(::Type{X}, ::FieldName{()}) where {X} = true
6378
broadcasted_has_field(::Type{X}, name::FieldName) where {X} =
6479
extract_first(name) in fieldnames(X) &&
@@ -199,4 +214,7 @@ if hasfield(Method, :recursion_relation)
199214
for m in methods(get_subtree_at_name)
200215
m.recursion_relation = dont_limit
201216
end
217+
for m in methods(broadcasted_get_field_type)
218+
m.recursion_relation = dont_limit
219+
end
202220
end

0 commit comments

Comments
 (0)