1+ function calculate_weights_cartesian (order:: Int , x0:: T , xs:: AbstractVector , idxs:: AbstractVector ) where T<: Real
2+ # Cartesian domain: use Fornberg
3+ calculate_weights (order, x0, vec (xs[idxs]))
4+ end
5+ function calculate_weights_spherical (order:: Int , x0:: T , x:: AbstractVector , idxs:: AbstractVector ) where T<: Real
6+ # Spherical domain: see #367
7+ # https://web.mit.edu/braatzgroup/analysis_of_finite_difference_discretization_schemes_for_diffusion_in_spheres_with_variable_diffusivity.pdf
8+ # Only order 2 is implemented
9+ @assert order == 2
10+ # Only 2nd order discretization is implemented
11+ # We can't activate this assertion for now because the rules try to create the spherical Laplacian
12+ # before checking whether there is a spherical Laplacian
13+ # this could be fixed by dispatching on domain type when we have different domain types
14+ # but for now everything is an Interval
15+ # @assert length(x) == 3
16+ # TODO : nonlinear diffusion in a spherical domain
17+ i = idxs[2 ]
18+ dx1 = x[i] - x[i- 1 ]
19+ dx2 = x[i+ 1 ] - x[i]
20+ i0 = i - 1 # indexing starts at 0 in the paper and starts at 1 in julia
21+ 1 / (i0 * dx1 * dx2) * [i0- 1 , - 2 i0, i0+ 1 ]
22+ end
23+
24+ function generate_finite_difference_rules (II, s, pde, discretization)
25+ approx_order = discretization. centered_order
26+ I1 = oneunit (first (s. Igrid))
27+
28+ Imin (I1, order) = first (s. Igrid) + I1 * (order ÷ 2 )
29+ Imax (I1, order) = last (s. Igrid) - I1 * (order ÷ 2 )
30+
31+ # --------------------------------------------------
32+ # * The stencil is the tappoints of the finite difference operator, relative to the current index
33+ stencil (j, order) = CartesianIndices (Tuple (map (x -> - x: x, (1 : length (s. nottime) .== j) * (order ÷ 2 ))))
34+
35+ # TODO : Generalize central difference handling to allow higher even order derivatives
36+ # The central neighbour indices should add the stencil to II, unless II is too close
37+ # to an edge in which case we need to shift away from the edge
38+
39+ central_neighbor_idxs (II, j, order) = stencil (j, order) .+ max (Imin (I1, order), min (II, Imax (I1, order)))
40+ central_weights_cartesian (d_order, II, j) = calculate_weights_cartesian (d_order, s. grid[j][II[j]], s. grid[j], vec (map (i -> i[j],
41+ central_neighbor_idxs (II, j, approx_order))))
42+ central_deriv (d_order, II, j, k) = dot (central_weights (d_order, II, j), s. discvars[k][central_neighbor_idxs (II, j, approx_order)])
43+
44+ central_deriv_cartesian (d_order, II, j, k) = dot (central_weights_cartesian (d_order, II, j), s. discvars[k][central_neighbor_idxs (II, j, approx_order)])
45+
46+ # spherical Laplacian has a hardcoded order of 2 (only 2nd order is implemented)
47+ # both for derivative order and discretization order
48+ central_weights_spherical (II, j) = calculate_weights_spherical (2 , s. grid[j][II[j]], s. grid[j], vec (map (i -> i[j], central_neighbor_idxs (II, j, 2 ))))
49+ central_deriv_spherical (II, j, k) = dot (central_weights_spherical (II, j), s. discvars[k][central_neighbor_idxs (II, j, 2 )])
50+
51+ # get a sorted list derivative order such that highest order is first. This is useful when substituting rules
52+ # starting from highest to lowest order.
53+ d_orders (x) = reverse (sort (collect (union (differential_order (pde. rhs, x), differential_order (pde. lhs, x)))))
54+
55+ # orders = [d_orders(s) for s in s.nottime]
56+ # D = [CenteredDifference{1} for i in 1:length(s.nottime), d in dorders()]
57+
58+ # central_deriv_rules = [(Differential(s)^2)(u) => central_deriv(2,II,j,k) for (j,s) in enumerate(s.nottime), (k,u) in enumerate(s.vars)]
59+ central_deriv_rules_cartesian = Array {Pair{Num,Num},1} ()
60+ for (j, x) in enumerate (s. nottime)
61+ rs = [(Differential (x)^ d)(u) => central_deriv_cartesian (d, II, j, k) for d in d_orders (x), (k, u) in enumerate (s. vars)]
62+ for rule in rs
63+ push! (central_deriv_rules_cartesian, rule)
64+ end
65+ end
66+
67+ central_deriv_rules_spherical = [Differential (r)(r^ 2 * Differential (r)(u)) / r^ 2 => central_deriv_spherical (II, j, k)
68+ for (j, r) in enumerate (s. nottime), (k, u) in enumerate (s. vars)]
69+
70+ valrules = map_symbolic_to_discrete (II, s)
71+
72+ # ! Use DerivativeOperator to get the coefficients, write a new constructor
73+ # TODO : upwind rules needs interpolation into `@rule`
74+ # upwinding_rules = [@rule(*(~~a,$(Differential(s.nottime[j]))(u),~~b) => IfElse.ifelse(*(~~a..., ~~b...,)>0,
75+ # *(~~a..., ~~b..., dot(reverse_weights(II,j),s.vars[k][central_neighbor_idxs(II,j)[1:2]])),
76+ # *(~~a..., ~~b..., dot(forward_weights(II,j),s.vars[k][central_neighbor_idxs(II,j)[2:3]]))))
77+ # for j in 1:length(s.nottime), k in 1:length(pdesys.s.vars)]
78+
79+
80+ return vcat (nonlinear_laplacian_rules (II, I1, pde, s, discretization, central_deriv_spherical),
81+ vec (central_deriv_rules_cartesian),
82+ vec (central_deriv_rules_spherical),
83+ valrules)
84+ end
85+
86+ function nonlinear_laplacian_rules (II, I1, pde, s, discretization, central_deriv_spherical)
87+ approx_order = discretization. centered_order
88+ # # Discretization of non-linear laplacian.
89+ # d/dx( a du/dx ) ~ (a(x+1/2) * (u[i+1] - u[i]) - a(x-1/2) * (u[i] - u[i-1]) / dx^2
90+ stencil (j, order) = CartesianIndices (Tuple (map (x -> - x: x, (1 : length (s. nottime) .== j) * (order ÷ 2 ))))
91+ Imin (I1, order) = first (s. Igrid) + I1 * (order ÷ 2 )
92+ Imax (I1, order) = last (s. Igrid) - I1 * (order ÷ 2 )
93+
94+ central_neighbor_idxs (II, j, order) = stencil (j, order) .+ max (Imin (I1, order), min (II, Imax (I1, order)))
95+ # TODO : upwind rules needs interpolation into `@rule`
96+
97+
98+ forward_weights (II, j) = calculate_weights (discretization. upwind_order, 0.0 , s. grid[j][[II[j], II[j] + 1 ]])
99+ reverse_weights (II, j) = - calculate_weights (discretization. upwind_order, 0.0 , s. grid[j][[II[j] - 1 , II[j]]])
100+
101+
102+ reverse_finite_difference (II, j, k) = - dot (reverse_weights (II, j), s. discvars[k][central_neighbor_idxs (II, j, approx_order)[1 : 2 ]]) / s. dxs[j]
103+ forward_finite_difference (II, j, k) = dot (forward_weights (II, j), s. discvars[k][central_neighbor_idxs (II, j, approx_order)[2 : 3 ]]) / s. dxs[j]
104+
105+ # TODO : improve interpolation of g(x) = u(x) for calculating u(x+-dx/2)
106+ interpolate_discrete_depvar (II, j, k, l) = sum ([s. discvars[k][central_neighbor_idxs (II, j, approx_order)][i] for i in (l == 1 ? [2 , 3 ] : [1 , 2 ])]) / 2.0
107+ # interpolate_discrete_indvar returns middle space values. E.g. x(i-1/2) or y(i+1/2).
108+ interpolate_discrete_indvar (II, j, l) = (s. grid[j][II[j]] + s. grid[j][II[j]+ l]) / 2.0
109+ # Dependent variable rules
110+ map_depvars_to_discrete (II, j, l) = [s. vars[k] => interpolate_discrete_depvar (II, j, k, l) for k = 1 : length (s. vars)]
111+ # Independent variable rules
112+ map_indvars_to_discrete (II, l) = [s. nottime[j] => interpolate_discrete_indvar (II, j, l) for j = 1 : length (s. nottime)]
113+
114+ build_discrete_symbolic_expression (II, expr, j, l) = Num (substitute (substitute (expr, map_depvars_to_discrete (II, j, l)), map_indvars_to_discrete (II, l)))
115+
116+ function cartesian_deriv (expr, II, j, k)
117+ discrete_expression_upwind = build_discrete_symbolic_expression (II, expr, j, 1 )
118+ discrete_expression_downwind = build_discrete_symbolic_expression (II, expr, j, - 1 )
119+ # We need to do
120+ discrete_expression_combined = [discrete_expression_downwind, discrete_expression_upwind]
121+ combined_finite_difference = [reverse_finite_difference (II, j, k), forward_finite_difference (II, j, k)]
122+ return dot (discrete_expression_combined, combined_finite_difference)
123+ end
124+
125+ function generate_cartesian_deriv_rules ()
126+ rules = [@rule ($ (Differential (iv))(* (~~ a, $ (Differential (iv))(dv), ~~ b))) =>
127+ cartesian_deriv (* (~~ a... , ~~ b... ), II, j, k) for (j, iv) in enumerate (s. nottime) for (k, dv) in enumerate (s. vars)]
128+
129+ rules = vcat (vec (rules), vec (
130+ [@rule ($ (Differential (iv))($ (Differential (iv))(dv) / ~ a)) =>
131+ cartesian_deriv (1 / ~ a, II, j, k) for (j, iv) in enumerate (s. nottime) for (k, dv) in enumerate (s. vars)]))
132+
133+ return rules
134+ end
135+
136+ function generate_spherical_deriv_rules ()
137+ rules = [@rule * (~~ a, ($ (Differential (iv))((iv^ 2 ) * $ (Differential (iv))(dv))), ~~ b) / (iv^ 2 ) =>
138+ * (~ a... , central_deriv_spherical (II, j, k), ~~ b... )
139+ for (j, iv) in enumerate (s. nottime) for (k, dv) in enumerate (s. vars)]# r^-2 needs to be handled separately
140+ rules = vcat (vec (rules), vec (
141+ [@rule * (~~ a, (iv^- 2 ) * ($ (Differential (iv))((iv^ 2 ) * $ (Differential (iv))(dv))), ~~ b) =>
142+ * (~ a... , central_deriv_spherical (II, j, k), ~~ b... ) for (j, iv) in enumerate (s. nottime) for (k, dv) in enumerate (s. vars)]))
143+ return rules
144+ end
145+
146+ cartesian_deriv_rules = vec (generate_cartesian_deriv_rules ())
147+ spherical_deriv_rules = vec (generate_spherical_deriv_rules ())
148+
149+ # If lhs or rhs is a linear combination, apply rules term by term
150+ rhs_arg = istree (pde. rhs) && (SymbolicUtils. operation (pde. rhs) == + ) ? SymbolicUtils. arguments (pde. rhs) : [pde. rhs]
151+ lhs_arg = istree (pde. lhs) && (SymbolicUtils. operation (pde. lhs) == + ) ? SymbolicUtils. arguments (pde. lhs) : [pde. lhs]
152+
153+ nonlinlap_rules = []
154+
155+ for term in vcat (lhs_arg, rhs_arg)
156+ for rule in vcat (cartesian_deriv_rules, spherical_deriv_rules)
157+ if rule (term) != = nothing
158+ push! (nonlinlap_rules, term => rule (term))
159+ end
160+ end
161+ end
162+
163+
164+ return vec (nonlinlap_rules)
165+ end
0 commit comments