NeoN
A framework for CFD software
Loading...
Searching...
No Matches
limitedLinear.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2023 - 2026 NeoN authors
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include "NeoN/core/error.hpp"
13
14#include <Kokkos_Core.hpp>
15
17{
18
19/* @brief blended central/upwind weights of the limitedLinear scheme.
20 *
21 * w_f = limiter * w_CD + (1 - limiter) * w_upwind, with the limiter built from the TVD ratio r of
22 * the upwind-cell gradient to the face gradient: limiter = max(min(twoByk * r, 1), 0), where
23 * twoByk = 2/k: k = 1 limits most strongly (fully bounded), smaller k relaxes the blend towards
24 * central differencing.
25 */
27 const VolumeField<scalar>& src,
28 const SurfaceField<scalar>& flux,
29 const SurfaceField<scalar>& cdWeights,
30 const SurfaceField<Vec3>& faceDeltaOwner,
31 const SurfaceField<Vec3>& faceDeltaNeighbour,
32 scalar twoByk,
33 SurfaceField<scalar>& weights,
34 bool cellLimitedGradient = false
35);
36
37/* @brief limitedLinear surface interpolation.
38 *
39 * Registered for scalar fields only. Limiting a vector field is conventionally done on
40 * magSqr(phi), which needs a derived scalar field and its gradient; that variant is not
41 * implemented here, so a Vec3 field must use another scheme.
42 *
43 * Spec: "limitedLinear <k> [cellLimited]". The optional trailing word selects the gradient the
44 * TVD ratio is built from. That gradient is a property of the transported *field*, not of this
45 * scheme -- a framework that keeps a per-field table of gradient schemes resolves it there and
46 * states the outcome here, because this class has no field-to-scheme table to consult. Omitting
47 * it keeps the unlimited Gauss-Green gradient, which reports a larger upwind-cell slope, hence a
48 * larger TVD ratio and a limiter closer to pure central differencing.
49 */
50template<typename ValueType>
52 public SurfaceInterpolationFactory<ValueType>::template Register<LimitedLinear<ValueType>>
53{
54 using Base =
56
57public:
58
59 LimitedLinear(const Executor& exec, const UnstructuredMesh& mesh, Input input)
60 : Base(exec, mesh), geometryScheme_(GeometryScheme::readOrCreate(mesh)),
61 twoByk_(readTwoByk(input)), cellLimitedGradient_(readCellLimited(input))
62 {
63 // Opt in to the per-face cell-to-face offsets while the mesh centres are still alive; the
64 // geometry scheme frees them on the first read of any cached geometry field.
65 geometryScheme_->ensureFaceDeltas();
66 };
67
68 static std::string name() { return "limitedLinear"; }
69
70 static std::string doc() { return "limitedLinear interpolation (TVD limited blend)"; }
71
72 static std::string schema() { return "none"; }
73
75 [[maybe_unused]] const VolumeField<ValueType>& src,
76 [[maybe_unused]] SurfaceField<ValueType>& dst
77 ) const override
78 {
79 NF_ERROR_EXIT("limitedLinear interpolation scheme requires a faceFlux");
80 }
81
83 const SurfaceField<scalar>& flux,
84 const VolumeField<ValueType>& src,
86 ) const override
87 {
89 src.exec(),
90 "limitedLinearWeights",
91 src.mesh(),
93 );
94 weight(flux, src, blended);
95 computeLinearInterpolation(src, blended, dst);
96 }
97
99 {
100 NF_ERROR_EXIT("limitedLinear interpolation scheme requires a faceFlux");
101 }
102
103 void weight(
104 const SurfaceField<scalar>& flux,
105 const VolumeField<ValueType>& src,
106 SurfaceField<scalar>& weights
107 ) const override
108 {
110 src,
111 flux,
112 geometryScheme_->weights(),
113 geometryScheme_->faceDeltaOwner(),
114 geometryScheme_->faceDeltaNeighbour(),
115 twoByk_,
116 weights,
117 cellLimitedGradient_
118 );
119 }
120
121 std::unique_ptr<SurfaceInterpolationFactory<ValueType>> clone() const override
122 {
123 return std::make_unique<LimitedLinear>(*this);
124 }
125
126private:
127
128 // Scheme spec "limitedLinear <k>": the factory consumed the name, so only k remains. k is
129 // written as an integer (e.g. 1) or a fractional value (e.g. 0.2). It is mandatory: a missing
130 // or non-numeric coefficient is a spec error and is rejected rather than silently replaced by a
131 // default that would change the scheme's behaviour.
132 static scalar readTwoByk(Input& input)
133 {
134 scalar k = 0;
135 if (std::holds_alternative<NeoN::TokenList>(input))
136 {
137 auto& tokens = std::get<NeoN::TokenList>(input);
138 if (tokens.peekIs<scalar>())
139 {
140 k = tokens.next<scalar>();
141 }
142 else if (tokens.peekIs<label>())
143 {
144 k = static_cast<scalar>(tokens.next<label>());
145 }
146 else
147 {
148 NF_THROW("limitedLinear requires a numeric coefficient: 'limitedLinear <k>'");
149 }
150 }
151 else
152 {
153 const auto& dict = std::get<NeoN::Dictionary>(input);
154 if (!dict.contains("limitedLinearCoeff"))
155 {
156 NF_THROW("limitedLinear requires the dictionary entry 'limitedLinearCoeff'");
157 }
158 k = dict.get<scalar>("limitedLinearCoeff");
159 }
160
161 if (!(k >= scalar(0) && k <= scalar(1)))
162 {
163 NF_THROW(
164 std::string("limitedLinear coefficient must be in [0, 1], got ") + std::to_string(k)
165 );
166 }
167 return scalar(2) / Kokkos::max(k, ROOTVSMALL);
168 }
169
170 // Optional trailing marker "limitedLinear <k> cellLimited", which selects the cell-limited
171 // (minmod, k=1) gradient for the TVD ratio; absent, the unlimited Gauss-Green gradient is
172 // used. An unrecognised trailing word is a spec error rather than a silently ignored token.
173 static bool readCellLimited(Input& input)
174 {
175 if (std::holds_alternative<NeoN::TokenList>(input))
176 {
177 auto& tokens = std::get<NeoN::TokenList>(input);
178 if (!tokens.peekIs<std::string>()) return false;
179
180 const auto word = tokens.next<std::string>();
181 if (word == "cellLimited") return true;
182 NF_THROW(
183 std::string("limitedLinear accepts only 'cellLimited' after its coefficient, got ")
184 + word
185 );
186 }
187
188 const auto& dict = std::get<NeoN::Dictionary>(input);
189 if (!dict.contains("limitedLinearGrad")) return false;
190
191 const auto word = dict.get<std::string>("limitedLinearGrad");
192 if (word == "cellLimited") return true;
193 if (word == "Gauss") return false;
194 NF_THROW(std::string("limitedLinearGrad must be 'Gauss' or 'cellLimited', got ") + word);
195 }
196
197 const std::shared_ptr<GeometryScheme> geometryScheme_;
198 scalar twoByk_;
199 bool cellLimitedGradient_;
200};
201
202} // namespace NeoN::finiteVolume::cellCentred
203
204namespace NeoN
205{
206
208
209template class fvcc::LimitedLinear<scalar>;
210
211}
Represents an unstructured mesh in NeoN.
const UnstructuredMesh & mesh() const
Returns a const reference to the unstructured mesh object.
Definition domain.hpp:124
const Executor & exec() const
Returns a const reference to the executor object.
Definition domain.hpp:117
void weight(const SurfaceField< scalar > &flux, const VolumeField< ValueType > &src, SurfaceField< scalar > &weights) const override
void interpolate(const VolumeField< ValueType > &src, SurfaceField< ValueType > &dst) const override
void interpolate(const SurfaceField< scalar > &flux, const VolumeField< ValueType > &src, SurfaceField< ValueType > &dst) const override
LimitedLinear(const Executor &exec, const UnstructuredMesh &mesh, Input input)
std::unique_ptr< SurfaceInterpolationFactory< ValueType > > clone() const override
void weight(const VolumeField< ValueType > &, SurfaceField< scalar > &) const override
Represents a surface boundary field for a cell-centered finite volume method.
Represents a surface field in a finite volume method.
Represents a volume field in a finite volume method.
A template class for registering derived classes with a base class.
#define NF_THROW(message)
Macro for throwing a NeoNException with the specified error message.
Definition error.hpp:103
#define NF_ERROR_EXIT(message)
Macro for printing an error message and aborting the program.
Definition error.hpp:90
void computeLinearInterpolation(const VolumeField< ValueType > &src, const SurfaceField< scalar > &weights, SurfaceField< ValueType > &dst)
void computeLimitedLinearWeights(const VolumeField< scalar > &src, const SurfaceField< scalar > &flux, const SurfaceField< scalar > &cdWeights, const SurfaceField< Vec3 > &faceDeltaOwner, const SurfaceField< Vec3 > &faceDeltaNeighbour, scalar twoByk, SurfaceField< scalar > &weights, bool cellLimitedGradient=false)
std::vector< BoundaryType > createCalculatedBCs(const UnstructuredMesh &mesh)
Definition boundary.hpp:37
Integer types used throughout NeoN.
Definition array.hpp:18
std::variant< SerialExecutor, CPUExecutor, GPUExecutor > Executor
Definition executor.hpp:21
std::variant< Dictionary, TokenList > Input
Definition input.hpp:15
float scalar
Definition scalar.hpp:17
constexpr scalar ROOTVSMALL
Definition scalar.hpp:20
int32_t label
Definition label.hpp:44
std::string word
Definition time.hpp:17