Vorticity Convection Nonlinear Function¤
2D: Streamfunction-Vorticity Convection¤
exponax.nonlin_fun.VorticityConvection2d
¤
Bases: BaseNonlinearFun
Source code in exponax/nonlin_fun/_vorticity_convection.py
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 | |
__init__
¤
__init__(
num_spatial_dims: int,
num_points: int,
*,
convection_scale: float = 1.0,
derivative_operator: Complex[Array, "D ... (N//2)+1"],
dealiasing_fraction: float
)
Performs a pseudo-spectral evaluation of the nonlinear vorticity convection, e.g., found in the 2d Navier-Stokes equations in streamfunction-vorticity formulation. In state space, it reads
𝒩(u) = - b ([1, -1]ᵀ ⊙ ∇(Δ⁻¹u)) ⋅ ∇u
with b the convection scale, ⊙ the Hadamard product, ∇ the
derivative operator, Δ⁻¹ the inverse Laplacian, and u the vorticity.
The minus arises because Exponax follows the convention that all
nonlinear and linear differential operators are on the right-hand side
of the equation. Typically, the vorticity convection term is on the
left-hand side. Hence, the minus is required to move the term to the
right-hand side.
Since the inverse Laplacian is required, it internally performs a Poisson solve which is straightforward in Fourier space.
Arguments:
num_spatial_dims: The number of spatial dimensionsD.num_points: The number of pointsNused to discretize the domain. This includes the left boundary point and excludes the right boundary point. In higher dimensions; the number of points in each dimension is the same.convection_scale: The scalebof the convection term. Defaults to1.0.derivative_operator: A complex array of shape(d, ..., N//2+1)that represents the derivative operator in Fourier space.dealiasing_fraction: The fraction of the highest resolved modes that are not aliased. Defaults to2/3which corresponds to Orszag's 2/3 rule.
Source code in exponax/nonlin_fun/_vorticity_convection.py
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 | |
__call__
¤
__call__(
u_hat: Complex[Array, "1 ... (N//2)+1"],
) -> Complex[Array, "1 ... (N//2)+1"]
Source code in exponax/nonlin_fun/_vorticity_convection.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
exponax.nonlin_fun.VorticityConvection2dKolmogorov
¤
Bases: VorticityConvection2d
Source code in exponax/nonlin_fun/_vorticity_convection.py
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 | |
__init__
¤
__init__(
num_spatial_dims: int,
num_points: int,
*,
convection_scale: float = 1.0,
injection_mode: int = 4,
injection_scale: float = 1.0,
derivative_operator: Complex[Array, "D ... (N//2)+1"],
dealiasing_fraction: float
)
Performs a pseudo-spectral evaluation of the nonlinear vorticity convection together with a Kolmogorov-like injection term, e.g., found in the 2d Navier-Stokes equations in streamfunction-vorticity formulation used for simulating a Kolmogorov flow.
In state space, it reads
𝒩(u) = - b ([1, -1]ᵀ ⊙ ∇(Δ⁻¹u)) ⋅ ∇u - f
For details on the vorticity convective term, see
exponax.nonlin_fun.VorticityConvection2d. The forcing term has the
form
f = -k (2π/L) γ cos(k (2π/L) x₁)
i.e., energy of intensity γ is injected at wavenumber k. Note that
the forcing is on the vorticity. As such, we get the prefactor k
(2π/L) and the sin(...) turns into a -cos(...) (minus sign because
the vorticity is derived via the curl).
Arguments:
num_spatial_dims: The number of spatial dimensionsd.num_points: The number of pointsNused to discretize the domain. This includes the left boundary point and excludes the right boundary point. In higher dimensions; the number of points in each dimension is the same.convection_scale: The scalebof the convection term. Defaults to1.0.injection_mode: The wavenumberkat which energy is injected.injection_scale: The intensityγof the injection term.derivative_operator: A complex array of shape(d, ..., N//2+1)that represents the derivative operator in Fourier space.dealiasing_fraction: The fraction of the highest resolved modes that are not aliased. Defaults to2/3which corresponds to Orszag's 2/3 rule.
Source code in exponax/nonlin_fun/_vorticity_convection.py
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 | |
__call__
¤
__call__(
u_hat: Complex[Array, "1 ... (N//2)+1"],
) -> Complex[Array, "1 ... (N//2)+1"]
Source code in exponax/nonlin_fun/_vorticity_convection.py
178 179 180 181 182 | |
3D: Projected Convection (Rotational Form)¤
exponax.nonlin_fun.ProjectedConvection3d
¤
Bases: BaseNonlinearFun
Source code in exponax/nonlin_fun/_projected_convection.py
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 | |
__init__
¤
__init__(
num_spatial_dims: int,
num_points: int,
*,
derivative_operator: Complex[Array, " 3 N N (N//2+1) "],
dealiasing_fraction: float = 2 / 3
)
Performs a pseudo-spectral evaluation of the nonlinear convection term for the 3d incompressible Navier-Stokes equations in velocity formulation using the rotational form. In state space, it reads
𝒩(u) = 𝒫(u × ω)
with u the velocity, ω = ∇ × u the vorticity, and 𝒫 the Leray
projection onto divergence-free fields.
The incompressible Navier-Stokes equations read
uₜ = -(u ⋅ ∇)u - ∇p + ν Δu
The Lamb vector identity allows rewriting the convection term as
(u ⋅ ∇)u = ∇(|u|²/2) + ω × u
Substituting and rearranging gives
uₜ = u × ω - ∇(|u|²/2 + p) + ν Δu
where u × ω = -(ω × u). The Leray projection 𝒫 projects onto the
space of divergence-free fields by removing any gradient component
(i.e., 𝒫(∇φ) = 0 for any scalar φ). Since both the pressure
gradient ∇p and the kinetic energy gradient ∇(|u|²/2) are
gradients of scalar fields, the projection annihilates them:
𝒫(uₜ) = 𝒫(u × ω) + ν Δu
Hence, the nonlinear term reduces to 𝒩(u) = 𝒫(u × ω), and the
pressure never needs to be computed explicitly.
The curl is computed in Fourier space, the cross product u × ω in
physical space (pseudo-spectral), and the result is projected back
in Fourier space.
Based on https://arxiv.org/pdf/1602.03638
Arguments:
num_spatial_dims: The number of spatial dimensionsD. Must be3.num_points: The number of pointsNused to discretize the domain. This includes the left boundary point and excludes the right boundary point. In higher dimensions; the number of points in each dimension is the same.derivative_operator: A complex array of shape(3, ..., N//2+1)that represents the derivative operator in Fourier space.dealiasing_fraction: The fraction of the highest resolved modes that are not aliased. Defaults to2/3which corresponds to Orszag's 2/3 rule.
Source code in exponax/nonlin_fun/_projected_convection.py
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 | |
__call__
¤
__call__(
u_hat: Complex[Array, " 3 N N (N//2+1) "],
) -> Complex[Array, " 3 N N (N//2+1) "]
Source code in exponax/nonlin_fun/_projected_convection.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
exponax.nonlin_fun.ProjectedConvection3dKolmogorov
¤
Bases: ProjectedConvection3d
Source code in exponax/nonlin_fun/_projected_convection.py
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | |
__init__
¤
__init__(
num_spatial_dims: int,
num_points: int,
*,
injection_mode: int = 4,
injection_scale: float = 1.0,
derivative_operator: Complex[Array, " 3 ... (N//2)+1"],
dealiasing_fraction: float
)
Performs a pseudo-spectral evaluation of the nonlinear convection term together with a Kolmogorov-like injection term for the 3d incompressible Navier-Stokes equations in velocity formulation. In state space, it reads
𝒩(u) = 𝒫(u × ω) + f
For details on the projected convection term, see
exponax.nonlin_fun.ProjectedConvection3d. The forcing term has the
form
f₀ = γ sin(k (2π/L) x₁)
f₁ = 0
f₂ = 0
i.e., energy of intensity γ is injected at wavenumber k in the
first velocity channel varying over the second spatial axis. This
forcing is naturally divergence-free because the forced component does
not vary along its own direction.
Arguments:
num_spatial_dims: The number of spatial dimensionsD. Must be3.num_points: The number of pointsNused to discretize the domain. This includes the left boundary point and excludes the right boundary point. In higher dimensions; the number of points in each dimension is the same.injection_mode: The wavenumberkat which energy is injected.injection_scale: The intensityγof the injection term.derivative_operator: A complex array of shape(3, ..., N//2+1)that represents the derivative operator in Fourier space.dealiasing_fraction: The fraction of the highest resolved modes that are not aliased. Defaults to2/3which corresponds to Orszag's 2/3 rule.
Source code in exponax/nonlin_fun/_projected_convection.py
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 209 210 211 212 213 214 215 216 217 218 219 220 | |
__call__
¤
__call__(
u_hat: Complex[Array, "3 ... (N//2)+1"],
) -> Complex[Array, "3 ... (N//2)+1"]
Source code in exponax/nonlin_fun/_projected_convection.py
222 223 224 225 226 | |
Leray Projection¤
exponax.nonlin_fun.Leray
¤
Bases: BaseNonlinearFun
Source code in exponax/nonlin_fun/_leray.py
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 | |
__init__
¤
__init__(
num_spatial_dims: int,
num_points: int,
*,
derivative_operator: Complex[Array, " D ... (N//2+1) "],
order: int = 2
)
Leray projection operator that projects a vector field onto the space of divergence-free fields. In state space, it reads
𝒫(u) = u - ∇(Δ⁻¹ ∇ ⋅ u)
This is equivalent to removing the irrotational (curl-free) component via the Helmholtz decomposition. The projection is trivial to compute in Fourier space because the Laplacian is diagonal.
Derivation:
Consider an operator splitting approach to the incompressible
Navier-Stokes equations in which advection and diffusion have already
been integrated, yielding an intermediate velocity u* that is
generally not divergence-free (∇ ⋅ u* ≠ 0). The pressure substep
reads
(u** - u*) / Δt = -(1/ρ) ∇p
We require u** to be incompressible (∇ ⋅ u** = 0). Applying the
divergence to both sides gives
(1/Δt)(∇ ⋅ u** - ∇ ⋅ u*) = -(1/ρ) Δp
Setting ∇ ⋅ u** = 0 yields the pressure-Poisson equation
Δp = (ρ/Δt) ∇ ⋅ u*
Solving for p and substituting back gives
u** = u* - (Δt/ρ) ∇(Δ⁻¹ (ρ/Δt) ∇ ⋅ u*)
For constant density (as in the incompressible case), ρ and Δt
cancel, and we obtain the Leray projection
u** = u* - ∇(Δ⁻¹(∇ ⋅ u*))
Hence, making a velocity field incompressible is a three-step process:
- Compute the divergence:
d = ∇ ⋅ u* - Solve a Poisson equation for a pseudo-pressure:
p = Δ⁻¹(-d) - Correct the velocity via the pressure gradient:
u** = u* + ∇p
In general, step 2 is the most computationally demanding (e.g., requiring a conjugate gradient solve). However, in Fourier space the Laplacian is diagonal, making the Poisson solve a trivial pointwise division.
Note that one can either use a negative sign in the Poisson solve (p =
Δ⁻¹(-d)) or a positive sign (p = Δ⁻¹(d)) as long as the opposite sign
is used in the velocity correction step. The choice of sign is arbitrary
and does not affect the final result.
Technically not a nonlinear operator, but it performs channel mixing
between the velocity channels and hence subclasses BaseNonlinearFun.
Arguments:
num_spatial_dims: The number of spatial dimensionsD.num_points: The number of pointsNused to discretize the domain. This includes the left boundary point and excludes the right boundary point. In higher dimensions; the number of points in each dimension is the same.derivative_operator: A complex array of shape(D, ..., N//2+1)that represents the derivative operator in Fourier space.order: The order of the Laplacian used for the Poisson solve. Default is2.
Source code in exponax/nonlin_fun/_leray.py
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 | |
__call__
¤
__call__(
u_hat: Complex[Array, " D ... (N//2+1) "],
) -> Complex[Array, " D ... (N//2+1) "]
Compute the Leray projection of the velocity field u_hat.
Args: u_hat: Velocity field in Fourier space.
Returns: The Leray projection of u_hat in Fourier space.
Source code in exponax/nonlin_fun/_leray.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |