-
Notifications
You must be signed in to change notification settings - Fork 27
/
World.jl
208 lines (180 loc) · 7.92 KB
/
World.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
abstract type AbstractWorld{T, N} end
"""
struct SSDInterval{T <: SSDFloat, L, R, BL, BR} <: IntervalSets.TypedEndpointsInterval{L,R,T}
Interval containing boundary conditions of left and right boundary as parametric type (`BL` and `BR`).
## Parametric types
* `T`: Precision type.
* `L`: Boundary type of the left endpoint.
* `R`: Boundary type of the right endpoint.
* `BL`: Boundary condition at the left endpoint.
* `BR`: Boundary condition at the right endpoint.
The boundary types of an `SSDInterval` can be `L, R ∈ {:closed, :open}`.
The boundary conditions of an `SSDInterval` can be
`BL, BR ∈ {:periodic, :reflecting, :infinite, :r0, :fixed}`.
## Fields
* `left::T`: Value of the left endpoint.
* `right::T`: Value of the right endpoint.
"""
struct SSDInterval{T <: SSDFloat, L, R, BL, BR} <: IntervalSets.TypedEndpointsInterval{L,R,T}
left::T
right::T
end
function get_boundary_types(int::SSDInterval{T, L, R, BL, BR}) where {T <:SSDFloat, L, R, BL, BR}
return L, R, BL, BR
end
const boundary_condition_mapping = Dict{String, Symbol}(
"r0" => :r0,
"inf" => :infinite,
"infinite" => :infinite,
"fixed" => :fixed,
"fix" => :fixed,
"reflecting" => :reflecting,
"periodic" => :periodic,
)
"""
struct World{T <: SSDFloat, N, S} <: AbstractWorld{T, N}
Definition of the finite volume on which a [`Simulation`](@ref) is performed.
## Parametric types
* `T`: Precision type.
* `N`: Dimensions of the world.
* `S`: Coordinate system (`Cartesian` or `Cylindrical`).
## Fields
* `intervals::NTuple{N, SSDInterval{T}}`: A set of [`SSDInterval`](@ref) defining the dimensions of the world.
"""
struct World{T <: SSDFloat, N, S} <: AbstractWorld{T, N}
intervals::NTuple{N, SSDInterval{T}}
end
function World{T, N, S}(args...) where {T <: SSDFloat, N, S}
return World{T, N, S}(args)
end
function World(T, dict::Dict, input_units::NamedTuple)::World
if dict["coordinates"] == "cylindrical"
CylindricalWorld(T, dict["axes"], input_units)
elseif dict["coordinates"] == "cartesian"
CartesianWorld(T, dict["axes"], input_units)
else
error("Gridtype must be \"cylindrical\" or \"cartesian\"")
end
end
function IntervalSets.endpoints(int::SSDInterval{T, L, R, BL, BR}) where {T <:SSDFloat, L, R, BL, BR} # this defines width(::SSDInterval)
(int.left, int.right)
end
function get_interval_boundary_types(dict::Dict)
BL, BR = missing, missing
if haskey(dict, "boundaries")
if typeof(dict["boundaries"]) == String
BL = boundary_condition_mapping[dict["boundaries"]]
BR = boundary_condition_mapping[dict["boundaries"]]
else
BL = boundary_condition_mapping[dict["boundaries"]["left"]]
BR = boundary_condition_mapping[dict["boundaries"]["right"]]
end
end
if !ismissing(BL)
if BL == :periodic || BR == :periodic
if !(BL == :periodic && BR == :periodic) && !((BL == :periodic && BR == :reflecting) || (BL == :reflecting && BR == :periodic))
throw(ConfigFileError("both or none endings must be \"periodic\" for an interval. Or, \"periodic\" and \"reflecting\" (for periodic behaviour plus mirror symmetry)."))
end
end
end
return BL, BR
end
function is_periodic_plus_mirror_symmetric(BL::Symbol, BR::Symbol)::Bool
return (BL == :periodic && BR == :reflecting) || (BL == :reflecting && BR == :periodic)
end
function get_r_SSDInterval(T, dict, input_units::NamedTuple)
if isa(dict, Dict)
from::T = 0
if "from" in keys(dict) @warn "ConfigFileWarning: \"from\" is not used in r-axis. It is fixed to 0." end
to::T = "to" in keys(dict) ? _parse_value(T, dict["to"], input_units.length) : throw(ConfigFileError("No \"to\" given for r-axis."))
if from < 0 throw(ConfigFileError("left boundary of r-axis cannot be negative.")) end
if to < 0 throw(ConfigFileError("right boundary of r-axis cannot be negative.")) end
L, BL = :closed, :r0
R, BR = :closed, :infinite
if "boundaries" in keys(dict)
BR = boundary_condition_mapping[dict["boundaries"]]
end
return SSDInterval{T, L, R, BL, BR}(from, to)
else
SSDInterval{T, :closed, :closed, :r0, :infinite}(0, _parse_value(T, dict, input_units.length))
end
end
function get_φ_SSDInterval(T, dict::Dict, input_units::NamedTuple)
if haskey(dict, "phi")
dp = dict["phi"]
from::T = "from" in keys(dp) ? _parse_value(T, dp["from"], input_units.angle) : T(0)
to::T = "to" in keys(dp) ? _parse_value(T, dp["to"], input_units.angle) : T(2π)
L = :closed
R = :open
BL = :periodic
BR = :periodic
cfBL, cfBR = get_interval_boundary_types(dp)
if !ismissing(cfBL) BL = cfBL; BR = cfBR; end
if is_periodic_plus_mirror_symmetric(BL, BR)
L = :closed; R = :closed;
BL = :reflecting; BR = :reflecting
end
if from == to # 2D
L = :closed; R = :closed;
BL = :reflecting; BR = :reflecting
end
return SSDInterval{T, L, R, BL, BR}(from, to)
else
return SSDInterval{T, :closed, :open, :periodic, :periodic}(T(0), T(2π))
end
end
function get_cartesian_SSDInterval(T, dict::Dict, input_units::NamedTuple)
from::T = "from" in keys(dict) ? _parse_value(T, dict["from"], input_units.length) : 0
to = "to" in keys(dict) ? _parse_value(T, dict["to"], input_units.length) : throw(ConfigFileError("No \"to\" given for z-axis."))
L = :closed
R = :closed
BL, BR = :infinite, :infinite
cfBL, cfBR = get_interval_boundary_types(dict)
if !ismissing(cfBL) BL = cfBL; BR = cfBR; end
if BL == BR == :periodic
L = :closed; R = :open
end
return SSDInterval{T, L, R, BL, BR}(from, to)
end
function CylindricalWorld(T, dict::Dict, input_units::NamedTuple)::World
r_int = get_r_SSDInterval(T, dict["r"], input_units)
φ_int = get_φ_SSDInterval(T, dict, input_units)
z_int = get_cartesian_SSDInterval(T, dict["z"], input_units)
return World{T, 3, Cylindrical}( r_int, φ_int, z_int )
end
function CartesianWorld(T, dict::Dict, input_units::NamedTuple)::World
x_int = get_cartesian_SSDInterval(T, dict["x"], input_units)
y_int = get_cartesian_SSDInterval(T, dict["y"], input_units)
z_int = get_cartesian_SSDInterval(T, dict["z"], input_units)
return World{T, 3, Cartesian}( x_int, y_int, z_int )
end
function CartesianWorld(xl::T, xr::T, yl::T, yr::T, zl::T, zr::T)::World where {T <: SSDFloat}
Δx::T = (xr - xl) / 10
Δy::T = (yr - yl) / 10
Δz::T = (zr - zl) / 10
x_int = SSDInterval{T, :closed, :closed, :infinite, :infinite}(xl - Δx, xr + Δx)
y_int = SSDInterval{T, :closed, :closed, :infinite, :infinite}(yl - Δy, yr + Δy)
z_int = SSDInterval{T, :closed, :closed, :infinite, :infinite}(zl - Δz, zr + Δz)
return World{T, 3, Cartesian}( x_int, y_int, z_int )
end
function CylindricalWorld(r_max::T, zl::T, zr::T)::World where {T <: SSDFloat}
r_int = SSDInterval{T, :closed, :closed, :r0, :infinite}(T(0), abs(r_max * T(1.1)))
φ_int = SSDInterval{T, :closed, :open, :periodic, :periodic}(T(0), T(2π))
Δz::T = zr - zl
z_int = SSDInterval{T, :closed, :closed, :infinite, :infinite}(zl - (Δz / 10), zr + (Δz / 10))
return World{T, 3, Cylindrical}( r_int, φ_int, z_int )
end
function World(::Type{Cylindrical}, limits::NTuple{6, T})::World where {T <: SSDFloat}
return CylindricalWorld(limits[2], limits[5], limits[6])
end
function World(::Type{Cartesian}, limits::NTuple{6, T})::World where {T <: SSDFloat}
return CartesianWorld(limits...)
end
function max_tick_distance_default(w::World{T, 3, Cylindrical}; n = 50) where {T}
Δw = width.(w.intervals)
return max(Δw[1], Δw[3]) * internal_length_unit / n
end
function max_tick_distance_default(w::World{T, 3, Cartesian}; n = 50) where {T}
Δw = width.(w.intervals)
return max(Δw...) * internal_length_unit / n
end