Projecting Points
2D to 1D Projection
Suppose we wish to solve the grid spacing along a discrete boundary $\Gamma$ given by the points $\gamma_i \in \R^2$ for $i=1,2,\dots, n$ where $n$ is the total number of points along the boundary. In-between each point is a linear interpolation $\Gamma_i$ for $i=1,2,\dots,n-1$ which defines the piecewise-continuous boundary $\Gamma$. That is
\[\Gamma(x) = \begin{cases} \Gamma_i(\eta_i(x)) &\text{if } x \in [\gamma_i, \gamma_{i+1}], \\ 0 &\text{otherwise}, \end{cases}\]
where
\[\Gamma_i(\eta) = \eta \gamma_{i+1} + (1 - \eta) \gamma_i, \quad \eta \in [0,1].\]
Here $\eta_i(x)$ would be a parameterization between $\gamma_i$ and $\gamma_{i+1}$. That is
\[\eta_i(x) = \frac{|x - \gamma_i|}{|\gamma_{i+1} - \gamma_i|}.\]
Okay writing this out mathematically is a pain, let's just explain the algorithm.
Algorithm
Given $\gamma_i \in \R^2$, find a 1D representation $s \in \R$. We want to do this in such a way that we can find an invertible mapping $f: \R^2 \to \R$. Now with $f(\Gamma_i) \in \R$ and $m \in \R$, we can solve the spacing ODE to get the optimal distribution $\xi_i$. Finally we can project the points back onto the $\Gamma_i$ to get the optimal distribution on the boundary using $f^{-1}$.
Basic Method in Words
Step 1 - 2D to 1D
Given points $\{ \gamma_i \}_{i=1}^n$ we can $\{ f(\gamma_i) \}_{i=1}^n$ by
- Computing the local spacing in array
diff
, - Accumulate the spacing
sp = cumsum(diff)
, - Add zero back in
xi = [0, sp]
, - Normalize
xs = xi / xi[end]
.
Now $\{ \text{xs} \}_{i=1}^n$ is within $[0,1]$ with normalized spacing according to the original boundary spacing. Algorithmically:
function Boundary2Dto1D(boundary)
# boundarySection is 2×N (rows: x,y; columns: points in order)
x = boundary[1, :]
y = boundary[2, :]
# segment lengths (N-1)
Δx = diff(x)
Δy = diff(y)
Δs = sqrt.(Δx.^2 .+ Δy.^2)
xs = [0.0; cumsum(Δs)] # length N, xs[1]=0, xs[end]=arclength
# normalize
xs = xs ./ xs[end] # now xs is in [0, 1]
return xs
end
Step 2 - Spacing
Now we get the optimal spacing
- Compute non-optimal solution
sol = GridGeneration.SolveODE(M, Mx, N, xs[1], xs[end])
- Compute optimal spacing
N_opt = ceil(Int, 1 / GridGeneration.ComputeOptimalSpacing(sol[1, :], M, xs))
- Compute optimal solution
sol_opt = GridGeneration.SolveODE(M, Mx, N_opt, xs[1], xs[end])
function GetOptimalSolution(m, mx, N, xs; method = "system of odes")
m_func = GridGeneration.build_interps_linear(xs, m)
mx_func = GridGeneration.build_interps_linear(xs, mx)
if method == "system of odes"
sol = GridGeneration.SolveODE(m_func, mx_func, N, xs[1], xs[end])
N_opt = GridGeneration.ComputeOptimalNumberofPoints(sol[1, :], m, xs)
@info("Optimal number of points: ", N_opt)
sol_opt = GridGeneration.SolveODE(m_func, mx_func, N_opt, xs[1], xs[end])
end
return sol_opt, sol
end
Step 3 - 1D to 2D
And finally let's project the points back onto the boundary
- Extract solution
x_sol = sol_opt[1,:]
- For each
x
inx_sol
- determine
i
such that $x \in [\text{xs}_i, \text{xs}_{i+1}]$ - compute $\eta = \frac{|x - \text{xs}_i|}{|\text{xs}_{i+1} - \text{xs}_i|}$
- Interpolate
x
onto $\Gamma_i$ according to $\eta$
- determine
The algorithm for this can be pretty straightforward if we assert that the incoming xs
is increasing. We have already taken care of that. Let's also add clamping to the edges to make sure that the start and ending points don't move.
function ProjectBoundary1Dto2D(boundary, points, xs)
projectedPoints = zeros(2, length(xs))
for (i, pnt) in enumrate(points)
intervalIndex = FindContainingIntervalIndex(pnt, xs)
dist = pnt - xs[intervalIndex]
normalDist = dist / (xs[intervalIndex + 1] - xs[intervalIndex])
projectPoint = ProjectPointOntoBoundary(normalDist, intervalIndex, boundary)
projectedPoints[i] = projectPoint
end
return projectedPoints
end
with the two helper functions
function FindContainingIntervalIndex(pnt, dist)
# assert that dist is increasing
index = - 1
# clamp
if pnt < dist[1]
index = 1
end
if pnt > dist[end]
index = length(dist) - 1
end
for (i,d) in enumerate(dist)
if d > pnt
index = i - 1
end
end
return index
end
function ProjectPointOntoBoundary(s, ind, boundary)
# interpolate boundary[ind] and boundary[ind + 1] with s
projectedPoint = s * boundary[ind] + (1 - s) * boundary[ind+1]
return projectPoint
end