Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to using Singlton types for field offsets #24

Merged
merged 1 commit into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ upper_bounds = ntuple(x -> 1.0, dimension)
num_cells = ntuple(x -> 16, dimension)
periodic = ntuple(x -> true, dimension)
g = UniformCartesianGrid(lower_bounds, upper_bounds, num_cells, periodic)
rho = Field(g, ParticleInCell.node, 1)
phi = Field(g, ParticleInCell.node, 1)
Eedge = Field(g, ParticleInCell.edge, 2)
Enode = Field(g, ParticleInCell.node, 2)
rho = Field(g, NodeOffset(), 1)
phi = Field(g, NodeOffset(), 1)
Eedge = Field(g, EdgeOffset(), 2)
Enode = Field(g, NodeOffset(), 2)

# Create a species
n_particles = 100
Expand Down
8 changes: 4 additions & 4 deletions logo/logo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ grid = UniformCartesianGrid((0.0,), (sim_length,), (num_cells,), (periodic,));

field_dimension = 1
lower_guard_cells = 10
rho = Field(grid, ParticleInCell.node, field_dimension, lower_guard_cells)
phi = Field(grid, ParticleInCell.node, field_dimension, lower_guard_cells)
Eedge = Field(grid, ParticleInCell.edge, field_dimension, lower_guard_cells)
Enode = Field(grid, ParticleInCell.node, field_dimension, lower_guard_cells);
rho = Field(grid, NodeOffset(), field_dimension, lower_guard_cells)
phi = Field(grid, NodeOffset(), field_dimension, lower_guard_cells)
Eedge = Field(grid, NodeOffset(), field_dimension, lower_guard_cells)
Enode = Field(grid, NodeOffset(), field_dimension, lower_guard_cells);

num_particles_per_stream = div(num_cells * particles_per_cell, 2)
particles_per_macro = nom_density * sim_length / num_particles_per_stream
Expand Down
4 changes: 2 additions & 2 deletions src/ParticleInCell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ using LinearSolve
import Base: eachindex

include("grids/grid.jl")
export AbstractGrid, UniformCartesianGrid, node, edge, face
export AbstractGrid, UniformCartesianGrid

include("species/species.jl")
export AbstractSpecies,
Expand All @@ -35,7 +35,7 @@ export AbstractSpecies,
electrons

include("field.jl")
export Field, num_elements
export Field, NodeOffset, EdgeOffset, FaceOffset, CenterOffset, num_elements

abstract type AbstractSimulationStep end
function step!(::T) where {T<:AbstractSimulationStep}
Expand Down
2 changes: 1 addition & 1 deletion src/communicate/communicate_field_test.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@testitem "CommunicateGuardCells" tags = [:unit] begin
@testset "1D" begin
grid = UniformCartesianGrid((0.0,), (1.0,), (10,), (false,))
field = Field(grid, ParticleInCell.node, 1)
field = Field(grid, NodeOffset(), 1)

step = CommunicateGuardCells(field)

Expand Down
59 changes: 30 additions & 29 deletions src/field.jl
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
abstract type AbstractField{T,N,D,G} end
abstract type AbstractField{T,N,O,D,G} end

# TODO: Consider making these singleton types to help with dispatch
@enum GridOffset begin
node
edge
face
end
abstract type GridOffset end
struct NodeOffset <: GridOffset end
struct EdgeOffset <: GridOffset end
struct FaceOffset <: GridOffset end
struct CenterOffset <: GridOffset end

"""
Field(grid, offset, num_elements,[ lower_guard_cells = 0,
[upper_guard_cells = lower_guard_cells + 1]])

Stores the values of a field.
"""
struct Field{T,N,D,D1,G} <: AbstractField{T,N,D,G}
struct Field{T,N,O,D,D1,G} <: AbstractField{T,N,O,D,G}
values::Array{T,D1}
grid::G
offset::GridOffset
offset::O

lower_guard_cells::Int
upper_guard_cells::Int
index_offset::NTuple{D,Int}

function Field(
grid::G,
offset::GridOffset,
offset::O,
num_elements::Int,
lower_guard_cells = 0,
upper_guard_cells = lower_guard_cells + 1,
T = Float64,
) where {D,G<:AbstractGrid{D}}
) where {D,O<:GridOffset,G<:AbstractGrid{D}}

index_offset = ntuple(_ -> lower_guard_cells + 1, D)
values = zeros(
Expand All @@ -38,7 +37,7 @@ struct Field{T,N,D,D1,G} <: AbstractField{T,N,D,G}
num_elements,
)

new{T,num_elements,D,D + 1,G}(
new{T,num_elements,O,D,D + 1,G}(
values,
grid,
offset,
Expand All @@ -51,10 +50,10 @@ end

num_elements(f::Field{T,N}) where {T,N} = N

function Base.show(io::IO, f::Field{T,N,D,D1,G}) where {T,N,D,D1,G}
function Base.show(io::IO, f::Field{T,N,O,D,D1,G}) where {T,N,O,D,D1,G}
print(
io,
"Field(grid=$(f.grid), offset=$(f.offset),
"Field(grid=$(f.grid), offset=$(O),
num_elements=$(N), T=$T",
)
end
Expand All @@ -77,35 +76,37 @@ end
@inline Base.firstindex(f::Field) = firstindex(f.values)

@inline function cell_index_to_cell_coords(f::Field, I)
# align_offset = f.offset == edge ? 0.5 : 0.0
# return Tuple(I) .- f.index_offset .+ align_offset
return Tuple(I) .- f.index_offset
end

@inline function cell_index_to_cell_coords(f::Field{T,N}, I, dim) where {T,N}
node_cell_coords = cell_index_to_cell_coords(f, I)
@inline function cell_index_to_cell_coords(f::Field{T,N,NodeOffset}, I, dim) where {T,N}
return cell_index_to_cell_coords(f, I)
end

if f.offset == node
return node_cell_coords
elseif f.offset == edge
return node_cell_coords .+ 0.5 .* unit_vec(dim, Val(N))
elseif f.offset == face
return node_cell_coords .+ 0.5 .* orth_vec(dim, Val(N))
end
@inline function cell_index_to_cell_coords(f::Field{T,N,EdgeOffset}, I, dim) where {T,N}
return cell_index_to_cell_coords(f, I) .+ 0.5 .* unit_vec(dim, Val(N))
end

@inline function cell_index_to_cell_coords(f::Field{T,N,FaceOffset}, I, dim) where {T,N}
return cell_index_to_cell_coords(f, I) .+ 0.5 .* orth_vec(dim, Val(N))
end

@inline function cell_index_to_cell_coords(f::Field{T,N,CenterOffset}, I, dim) where {T,N}
return cell_index_to_cell_coords(f, I) .+ 0.5
end

@inline function cell_coords_to_cell_index(f::Field, idxs)
return CartesianIndex(Tuple(floor.(Int, idxs) .+ f.index_offset))
end

extra_width_func(f::Field{T,N,NodeOffset}) where {T,N} = 0
extra_width_func(f) = 1

@inline function phys_coords_to_cell_index_ittr(f::Field, xs, width::Int)
cell_coords = phys_coords_to_cell_coords(f.grid, xs)
cell_index = Tuple(cell_coords_to_cell_index(f, cell_coords))

extra_width = 0
if f.offset == edge || f.offset == face
extra_width = 1
end
extra_width = extra_width_func(f)

# Need to add one to lower bound to account for the fact that the particle
# is 'in' the cell at cell_index.
Expand Down
8 changes: 4 additions & 4 deletions src/field_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
(10, 10, 10),
(true, true, true),
)
scalar_node_field = Field(grid, ParticleInCell.node, 1, 3)
vector_node_field = Field(grid, ParticleInCell.node, 3)
vector_edge_field = Field(grid, ParticleInCell.edge, 3)
vector_face_field = Field(grid, ParticleInCell.face, 3)
scalar_node_field = Field(grid, NodeOffset(), 1, 3)
vector_node_field = Field(grid, NodeOffset(), 3)
vector_edge_field = Field(grid, EdgeOffset(), 3)
vector_face_field = Field(grid, FaceOffset(), 3)

@test num_elements(scalar_node_field) == 1
@test num_elements(vector_node_field) == 3
Expand Down
34 changes: 20 additions & 14 deletions src/field_utils.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
struct FiniteDifferenceToEdges{F,NT} <: AbstractSimulationStep
nodal_field::F
edge_field::F
struct FiniteDifferenceToEdges{F1,F2,NT} <: AbstractSimulationStep
nodal_field::F1
edge_field::F2

edge_lengths::NT

function FiniteDifferenceToEdges(nodal_field::F, edge_field::F) where {F}
@assert nodal_field.offset == node
@assert edge_field.offset == edge
function FiniteDifferenceToEdges(
nodal_field::Field{T1,N1,NodeOffset},
edge_field::Field{T2,N2,EdgeOffset},
) where {T1,T2,N1,N2}
@assert edge_field.upper_guard_cells >= 1

edge_lengths = cell_lengths(nodal_field.grid)

new{F,typeof(edge_lengths)}(nodal_field, edge_field, edge_lengths)
new{typeof(nodal_field),typeof(edge_field),typeof(edge_lengths)}(
nodal_field,
edge_field,
edge_lengths,
)
end
end

Expand All @@ -31,17 +36,18 @@ function step!(step::FiniteDifferenceToEdges{F}) where {T,D,F<:AbstractField{T,D
end
end

struct AverageEdgesToNodes{F} <: AbstractSimulationStep
edge_field::F
nodal_field::F
struct AverageEdgesToNodes{F1,F2} <: AbstractSimulationStep
edge_field::F1
nodal_field::F2

function AverageEdgesToNodes(edge_field::F, nodal_field::F) where {F}
@assert edge_field.offset == edge
@assert nodal_field.offset == node
function AverageEdgesToNodes(
edge_field::Field{T1,N1,EdgeOffset},
nodal_field::Field{T2,N2,NodeOffset},
) where {T1,T2,N1,N2}
@assert edge_field.lower_guard_cells >= 1
@assert edge_field.upper_guard_cells >= 1

new{F}(edge_field, nodal_field)
new{typeof(edge_field),typeof(nodal_field)}(edge_field, nodal_field)
end
end

Expand Down
8 changes: 4 additions & 4 deletions src/field_utils_test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@testitem "FiniteDifferenceToEdges" tags = [:unit] begin
@testset "1D" begin
grid = UniformCartesianGrid((0.0,), (1.0,), (10,), (false,))
nodal_field = Field(grid, ParticleInCell.node, 1)
edge_field = Field(grid, ParticleInCell.edge, 1)
nodal_field = Field(grid, NodeOffset(), 1)
edge_field = Field(grid, EdgeOffset(), 1)

step = FiniteDifferenceToEdges(nodal_field, edge_field)
@test step.edge_lengths == (0.1,)
Expand All @@ -21,8 +21,8 @@ end
@testitem "AverageEdgesToNodes" tags = [:unit] begin
@testset "1D" begin
grid = UniformCartesianGrid((0.0,), (1.0,), (10,), (false,))
nodal_field = Field(grid, ParticleInCell.node, 1, 1)
edge_field = Field(grid, ParticleInCell.edge, 1, 1)
nodal_field = Field(grid, NodeOffset(), 1, 1)
edge_field = Field(grid, EdgeOffset(), 1, 1)

step = AverageEdgesToNodes(edge_field, nodal_field)

Expand Down
8 changes: 3 additions & 5 deletions src/grids/grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ numbering systems that can refer to a location in the simulation domain:
2. The 'cell coordinates' of a point is the non-dimensional location of the point
in units of cell lengths. This value can range from 0 to num_cells - 1,
or outside this range if guard cells are included. The value will
typically have the type `NTuple{N, Int}`.
typically have the type `NTuple{N, Int}` or `NTuple{N, T}` with `T <: Real`.
3. The 'cell index' of a point is the `CartesianIndex` that can be used to
index into field arrays at that point. This value must strictly be
confined to `axes(field.values)`, which, for any given dimension, will
Expand Down Expand Up @@ -51,12 +51,10 @@ orth_vec(i, ::Val{3}) = ntuple(j -> j == i ? 0 : 1, 3)
orth_vec(::Any, ::Union{Val{1},Val{2}}) = error("orth_vec only makes sense in 3D")

"""
cell_coords_to_phys_coords(grid, idxs, [offset, component])
cell_coords_to_phys_coords(grid, idxs)

Converts the cell coordinates `idxs` to a physical coordinate using the geometry
specified in `grid`. Optionally, an offset and component can be specified to get
the physical coordinates of a specific `edge` or `face` of the cell. The component
argument is one-indexed.
specified in `grid`.

For more information on the different types of coordinate systems, see
`AbstractGrid`.
Expand Down
29 changes: 0 additions & 29 deletions src/grids/grid_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,10 @@
)

@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0)) == (0, 0, 0)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0), node) == (0, 0, 0)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0), edge, 1) ==
(0.05, 0, 0)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0), face, 1) ==
(0, 0.05, 0.05)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0), edge, 3) ==
(0, 0, 0.05)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (0, 0, 0), face, 3) ==
(0.05, 0.05, 0)

@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5)) == (0.5, 0.5, 0.5)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5), node) ==
(0.5, 0.5, 0.5)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5), edge, 1) ==
(0.55, 0.5, 0.5)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5), face, 1) ==
(0.5, 0.55, 0.55)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5), edge, 3) ==
(0.5, 0.5, 0.55)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (5, 5, 5), face, 3) ==
(0.55, 0.55, 0.5)

@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10)) == (1, 1, 1)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10), node) ==
(1, 1, 1)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10), edge, 1) ==
(1.05, 1, 1)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10), face, 1) ==
(1, 1.05, 1.05)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10), edge, 3) ==
(1, 1, 1.05)
@test ParticleInCell.cell_coords_to_phys_coords(grid, (10, 10, 10), face, 3) ==
(1.05, 1.05, 1)
end

@testset "phys_coords_to_cell_coords" begin
Expand Down
19 changes: 2 additions & 17 deletions src/grids/uniform_cartesian_grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,8 @@ end
return (grid.upper_bounds .- grid.lower_bounds) ./ grid.num_cells
end

# TODO: inline this function once the offsets become singletons, and this
# function becomes performant
function cell_coords_to_phys_coords(
grid::UniformCartesianGrid{D},
idxs,
offset = node,
component::Int = 1,
) where {D}
node_coords = grid.lower_bounds .+ idxs .* cell_lengths(grid)

if offset == node
return node_coords
elseif offset == edge
return node_coords .+ cell_lengths(grid) ./ 2 .* unit_vec(component, Val(D))
elseif offset == face
return node_coords .+ cell_lengths(grid) ./ 2 .* orth_vec(component, Val(D))
end
@inline function cell_coords_to_phys_coords(grid::UniformCartesianGrid, idxs)
return grid.lower_bounds .+ idxs .* cell_lengths(grid)
end

@inline function phys_coords_to_cell_coords(grid::UniformCartesianGrid, xs)
Expand Down
10 changes: 6 additions & 4 deletions src/interpolation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,15 @@ struct BSplineChargeInterpolation{S,F,IF} <: AbstractSimulationStep

interp_func::IF

function BSplineChargeInterpolation(species::S, rho::F, bspline_degree::Int) where {S,F}
@assert rho.offset == node

function BSplineChargeInterpolation(
species::S,
rho::Field{T,N,NodeOffset},
bspline_degree::Int,
) where {T,N,S}
interp_width = div(bspline_degree, 2) + 1
interp_func = select_bs_interp(bspline_degree)

new{S,F,typeof(interp_func)}(
new{S,typeof(rho),typeof(interp_func)}(
species,
rho,
bspline_degree,
Expand Down
4 changes: 2 additions & 2 deletions src/interpolation_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
end

g = UniformCartesianGrid((0.0,), (1.0,), (10,), (true,))
rho = Field(g, ParticleInCell.node, 1)
phi = Field(g, ParticleInCell.node, 1)
rho = Field(g, NodeOffset(), 1)
phi = Field(g, NodeOffset(), 1)
s = VariableWeightSpecies(fill(0.5, 1, 1), fill(0.0, 1, 1), [1.0], 1.0, 1.0)

bs_interp = BSplineChargeInterpolation(s, rho, 1)
Expand Down
Loading
Loading