Theseus
Compressible flow solver
Loading...
Searching...
No Matches
RHSOperator_impl.hpp
Go to the documentation of this file.
1// Copyright (c) 2025-2026 Board of Trustees of the University of Illinois
2//
3// This file is part of Theseus.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7namespace Theseus
8{
9
10 template<typename PhysicsT>
11 void RHSOperator<PhysicsT>::Finalize(mfem::real_t time)
12 {
13 Theseus::ScopedTimer finalize_timer("RHSOperator::Finalize");
14
16 GetOperatorCache(vfes.get(), &operator_cache);
17 AssembleBoundaryFaceGeometryTerms(vfes.get(), bdr_marker, &operator_cache);
18#ifdef SUBCELL_FV_BLENDING
19 {
20 Theseus::ScopedTimer timer("ComputeSubcellMetrics");
21 ComputeSubcellMetrics(vfes.get(), &operator_cache);
22 }
23#endif
24
25 operator_cache.bc_descriptors = bc_descriptors;
26 operator_cache.bc_scalar_data = bc_scalar_data;
27 operator_cache.bc_vector_data = bc_vector_data;
28 ValidateAxisBoundaryGeometry(operator_cache);
29
30#ifdef SUBCELL_FV_BLENDING
31 MFEM_VERIFY(indicator, "SUBCELL_FV_BLENDING enabled but indicator is null.");
32 BuildPerssonDeviceCache(operator_cache, indicator->ModalBasis());
33#endif
34 GetDeviceCache(operator_cache, device_cache);
35 }
36
37 // pu should be prolongated
38 template<typename PhysicsT>
39 void RHSOperator<PhysicsT>::FetchRestrictions(const mfem::Vector &pu, mfem::Vector &uVol,
40 mfem::Vector &uInt, mfem::Vector &uBnd) const
41 {
42 Theseus::ScopedTimer timer("FetchRestrictions");
43 const int psize = operator_cache.restr_v->Height();
44 if(uVol.Size() != psize){
45 uVol.SetSize(psize);
46 uVol.UseDevice();
47 }
48 operator_cache.restr_v->Mult(pu, uVol);
49 const int int_restr_size = operator_cache.restr_f->Height();
50 if(uInt.Size() != int_restr_size){
51 uInt.SetSize(int_restr_size);
52 uInt.UseDevice();
53 }
54 operator_cache.restr_f->Mult(pu, uInt);
55 const int bnd_restr_size = operator_cache.restr_b->Height();
56 if(uBnd.Size() != bnd_restr_size){
57 uBnd.SetSize(bnd_restr_size);
58 uBnd.UseDevice();
59 }
60 operator_cache.restr_b->Mult(pu, uBnd);
61 operator_cache.u_vol_restr_ready = true;
62 operator_cache.u_bnd_restr_ready = true;
63 operator_cache.u_int_restr_ready = true;
64 }
65
66#ifdef SUBCELL_FV_BLENDING
67 template<typename PhysicsT>
68 void RHSOperator<PhysicsT>::ComputeIndicatorField(const mfem::Vector &pu) const
69 {
70 Theseus::ScopedTimer timer("ComputeIndicator");
71
72 // This block is executed by the host
73 const int nval_restr = operator_cache.restr_v->Height();
74 // Copy the device cache so that it is not member data
75 auto dc = device_cache;
76
77 // Device cache parameters
78 const int dim = dc.dim;
79 const int ne = dc.num_elements;
80 const int ndof = dc.ndof_scalar_el;
81 const int neq = dc.num_equations;
82 const int Np_x = dc.Np_x;
83 const int Np_y = dc.Np_y;
84 const int Np_z = dc.Np_z;
85
86 MFEM_ASSERT(nval_restr == ne*ndof*neq, "Unexpected size for volume restriction in indicator calc.");
87 const int nval_ind = nval_restr / neq;
88
89 if(operator_cache.uVol.Size() != nval_restr){
90 operator_cache.uVol.SetSize(nval_restr);
91 operator_cache.uVol.UseDevice();
92 }
93 mfem::Vector &Ue(operator_cache.uVol);
94 if(!operator_cache.u_vol_restr_ready){
95 operator_cache.restr_v->Mult(pu, Ue);
96 operator_cache.u_vol_restr_ready = true;
97 }
98 const mfem::real_t *Ue_d = Ue.Read();
99
100 mfem::Vector &indicator_field(operator_cache.indicatorField);
101 if(indicator_field.Size() != nval_ind){
102 indicator_field.SetSize(nval_ind);
103 indicator_field.UseDevice();
104 }
105 mfem::real_t *ifield_d = indicator_field.Write();
106
107 const int estride = ndof*neq;
108
109 // Inside the FORALL below, executed on device
110 mfem::forall(nval_ind, [=] MFEM_HOST_DEVICE (int vind)
111 {
112 const int e = vind / ndof;
113 const int evind = vind - e * ndof;
114 const mfem::real_t *u_el = Ue_d + e * estride;
115 mfem::real_t elstate[Theseus::MAXEQ];
116 Theseus::Kernels::el_gather_state(u_el, ndof, neq, evind, elstate);
117 Theseus::PointStateView S{elstate};
118 ifield_d[vind] = dc.gas.pressure(S) * dc.gas.density(S);
119 });
120
121 }
122
123 template<typename PhysicsT>
124 void RHSOperator<PhysicsT>::ComputeBlendingCoefficient() const
125 {
126 ScopedTimer timer("ComputeBlendingCoeff");
127 const mfem::real_t *eta_d = operator_cache.eta.Read();
128 mfem::real_t *alpha_d = operator_cache.alpha->Write();
129 // operator_cache.alpha_d;
130 int ne = operator_cache.num_elements;
131 mfem::real_t mthresh = modalThreshold;
132 mfem::real_t sharp_fac = sharpness_fac;
133 mfem::real_t alpmin = alpha_min;
134 mfem::real_t alpmax = alpha_max;
135 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
136 {
137 mfem::real_t alpha_dof = \
138 1.0 / (1.0 + std::exp(-sharp_fac * (eta_d[e] - mthresh) / mthresh));
139 if (alpha_dof < alpmin)
140 {
141 alpha_dof = 0.0;
142 }
143 else if (alpha_dof > (1.0 - alpmin))
144 {
145 alpha_dof = 1.0;
146 }
147 alpha_d[e] = std::min(alpha_dof, alpmax);
148 });
149 }
150
151 template<typename PhysicsT>
152 void RHSOperator<PhysicsT>::CheckIndicatorSmoothness() const
153 {
154 Theseus::ScopedTimer timer("CheckIndicatorSmoothness");
155
156 const int ne = operator_cache.num_elements;
157 const int ndofs = operator_cache.ndof_scalar_el;
158
159 const mfem::real_t *indicator_d = operator_cache.indicatorField.Read();
160 const mfem::real_t *modal_d = operator_cache.modal.Read();
161 const mfem::real_t *keep_M1_d = operator_cache.keep_M1.Read();
162 const mfem::real_t *keep_M2_d = operator_cache.keep_M2.Read();
163 mfem::real_t *eta_d = operator_cache.eta.ReadWrite();
164
165 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
166 {
167 const mfem::real_t *u = indicator_d + e * ndofs;
168
169 mfem::real_t mm = 0.0;
170 mfem::real_t m1m1 = 0.0;
171 mfem::real_t m2m2 = 0.0;
172
173 for (int m = 0; m < ndofs; ++m)
174 {
175 mfem::real_t mode = 0.0;
176
177 for (int q = 0; q < ndofs; ++q)
178 {
179 mode += modal_d[m * ndofs + q] * u[q];
180 }
181
182 const mfem::real_t m1 = keep_M1_d[m] * mode;
183 const mfem::real_t m2 = keep_M2_d[m] * mode;
184
185 mm += mode * mode;
186 m1m1 += m1 * m1;
187 m2m2 += m2 * m2;
188 }
189
190 const mfem::real_t eps = 1.0e-30;
191 mfem::real_t val = 0.0;
192
193 if(mm > eps) {
194 val = 1.0 - m1m1 / mm;
195 if (m1m1 > eps){
196 val = Theseus::Kernels::rmax(val, 1.0 - m2m2 / m1m1);
197 } else {
198 val = 1.0;
199 }
200 }
201 eta_d[e] = Theseus::Kernels::rmin(Theseus::Kernels::rmax(val, 0.0), 1.0);
202 });
203
204 }
205#endif
206
207 template<typename PhysicsT>
209 {
210 Theseus::ScopedTimer timer("ComputeIntegralMeasures");
211
212 // This block is executed by the host
213 const int nval_restr = operator_cache.restr_v->Height();
214
215 // Copy the device cache so that it is not member data
216 auto dc = device_cache;
217
218 // Device cache parameters
219 const int ne = dc.num_elements;
220 const int ndof = dc.ndof_scalar_el;
221 const int neq = dc.num_equations;
222 const mfem::real_t *qWts_d = dc.elQWgts_d;
223 const mfem::real_t *radius_d = dc.elRadius_d;
224 auto gas = dc.gas;
225
226 if(operator_cache.uVol.Size() != nval_restr){
227 operator_cache.uVol.SetSize(nval_restr);
228 operator_cache.uVol.UseDevice();
229 }
230 mfem::Vector &Ue(operator_cache.uVol);
231 if(!operator_cache.u_vol_restr_ready){
232 operator_cache.restr_v->Mult(u, Ue);
233 operator_cache.u_vol_restr_ready = true;
234 }
235
236 const mfem::real_t *Ue_d = Ue.Read();
237 const int estride = ndof*neq;
238
239 mfem::Vector elMass_integral(ne);
240 mfem::Vector elKE_integral(ne);
241 mfem::Vector elEnergy_integral(ne);
242 mfem::Vector elMaxPressure(ne);
243 mfem::Vector elMaxTemperature(ne);
244 mfem::Vector elMaxDensity(ne);
245 mfem::Vector elMinPressure(ne);
246 mfem::Vector elMinTemperature(ne);
247 mfem::Vector elMinDensity(ne);
248
249 elMass_integral.UseDevice();
250 elKE_integral.UseDevice();
251 elEnergy_integral.UseDevice();
252 elMaxPressure.UseDevice();
253 elMaxTemperature.UseDevice();
254 elMaxDensity.UseDevice();
255 elMinPressure.UseDevice();
256 elMinTemperature.UseDevice();
257 elMinDensity.UseDevice();
258
259 mfem::real_t *elMass_int_d = elMass_integral.Write();
260 mfem::real_t *elKE_int_d = elKE_integral.Write();
261 mfem::real_t *elEnergy_int_d = elEnergy_integral.Write();
262
263 mfem::real_t *elPress_max_d = elMaxPressure.Write();
264 mfem::real_t *elTemp_max_d = elMaxTemperature.Write();
265 mfem::real_t *elDens_max_d = elMaxDensity.Write();
266 mfem::real_t *elPress_min_d = elMinPressure.Write();
267 mfem::real_t *elTemp_min_d = elMinTemperature.Write();
268 mfem::real_t *elDens_min_d = elMinDensity.Write();
269
270 // Inside the FORALL below, executed on device
271 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
272 {
273 const mfem::real_t *u_el = Ue_d + e * estride;
274 const mfem::real_t *qWgt = qWts_d + e * ndof;
275 const mfem::real_t *radius = dc.axisymmetric ?
276 radius_d + e * ndof : nullptr;
277
278 mfem::real_t mass_int = 0.0;
279 mfem::real_t ke_int = 0.0;
280 mfem::real_t en_int = 0.0;
281 mfem::real_t min_dens = 1e32;
282 mfem::real_t max_dens = 0.0;
283 mfem::real_t min_temp = 1e32;
284 mfem::real_t max_temp = 0.0;
285 mfem::real_t min_press = 1e32;
286 mfem::real_t max_press = 0.0;
287
288 for(int ep = 0;ep < ndof;ep++){
289 mfem::real_t elstate[Theseus::MAXEQ];
290 Theseus::Kernels::el_gather_state(u_el, ndof, neq, ep, elstate);
291 Theseus::PointStateView S{elstate};
292
293 mfem::real_t rho = gas.density(S);
294 mfem::real_t ke = gas.kinetic_energy_density(S);
295 mfem::real_t rhoE = gas.energy(S); // energy density
296 mfem::real_t press = gas.pressure(S);
297 mfem::real_t temper = gas.temperature(S);
298
299 const mfem::real_t measure = qWgt[ep] *
301 dc.axisymmetric, dc.axisymmetric ? radius[ep] : 0.0);
302 mass_int += rho * measure;
303 ke_int += ke * measure;
304 en_int += rhoE * measure;
305
306 min_temp = Theseus::Kernels::rmin(min_temp, temper);
307 max_temp = Theseus::Kernels::rmax(max_temp, temper);
308 min_dens = Theseus::Kernels::rmin(min_dens, rho);
309 max_dens = Theseus::Kernels::rmax(max_dens, rho);
310 min_press = Theseus::Kernels::rmin(min_press, press);
311 max_press = Theseus::Kernels::rmax(max_press, press);
312 }
313
314 elMass_int_d[e] = mass_int;
315 elKE_int_d[e] = ke_int;
316 elEnergy_int_d[e] = en_int;
317 elPress_max_d[e] = max_press;
318 elPress_min_d[e] = min_press;
319 elDens_max_d[e] = max_dens;
320 elDens_min_d[e] = min_dens;
321 elTemp_min_d[e] = min_temp;
322 elTemp_max_d[e] = max_temp;
323
324 });
325
326 // diag.mass = mfem::Sum(elMass_integral);
327 // diag.ke = mfem::Sum(elKE_integral);
328 // diag.en = mfem::Sum(elEnergy_integral);
329 diag.mass = 0.0;
330 diag.ke = 0.0;
331 diag.en = 0.0;
332 diag.min_press = 1e32;
333 diag.max_press = 0.0;
334 diag.min_dens = 1e32;
335 diag.max_dens = 0.0;
336 diag.min_temp = 1e32;
337 diag.max_temp = 0.0;
338
339 const mfem::real_t *mass_h = elMass_integral.HostRead();
340 const mfem::real_t *ke_h = elKE_integral.HostRead();
341 const mfem::real_t *en_h = elEnergy_integral.HostRead();
342 const mfem::real_t *minpress_h = elMinPressure.HostRead();
343 const mfem::real_t *maxpress_h = elMaxPressure.HostRead();
344 const mfem::real_t *mindens_h = elMinDensity.HostRead();
345 const mfem::real_t *maxdens_h = elMaxDensity.HostRead();
346 const mfem::real_t *mintemp_h = elMinTemperature.HostRead();
347 const mfem::real_t *maxtemp_h = elMaxTemperature.HostRead();
348
349 for (int e = 0; e < ne; ++e) {
350 diag.mass += mass_h[e];
351 diag.ke += ke_h[e];
352 diag.en += en_h[e];
353 diag.min_press = Theseus::Kernels::rmin(diag.min_press, minpress_h[e]);
354 diag.max_press = Theseus::Kernels::rmax(diag.max_press, maxpress_h[e]);
355 diag.min_temp = Theseus::Kernels::rmin(diag.min_temp, mintemp_h[e]);
356 diag.max_temp = Theseus::Kernels::rmax(diag.max_temp, maxtemp_h[e]);
357 diag.min_dens = Theseus::Kernels::rmin(diag.min_dens, mindens_h[e]);
358 diag.max_dens = Theseus::Kernels::rmax(diag.max_dens, maxdens_h[e]);
359 }
360
361 mfem::real_t sendbuf[3] = {diag.mass, diag.ke, diag.en};
362 mfem::real_t recvbuf[3] = {0.0, 0.0, 0.0};
363
364 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_SUM, pmesh->GetComm());
365
366 diag.mass = recvbuf[0];
367 diag.ke = recvbuf[1];
368 diag.en = recvbuf[2];
369
370 sendbuf[0] = diag.min_press;
371 sendbuf[1] = diag.min_temp;
372 sendbuf[2] = diag.min_dens;
373
374 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_MIN, pmesh->GetComm());
375
376 diag.min_press = recvbuf[0];
377 diag.min_temp = recvbuf[1];
378 diag.min_dens = recvbuf[2];
379
380 sendbuf[0] = diag.max_press;
381 sendbuf[1] = diag.max_temp;
382 sendbuf[2] = diag.max_dens;
383
384 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_MAX, pmesh->GetComm());
385
386 diag.max_press = recvbuf[0];
387 diag.max_temp = recvbuf[1];
388 diag.max_dens = recvbuf[2];
389
390 if(diag0.mass == 0.0){
391 diag0 = diag;
392 }
393
394 }
395
396 template<typename PhysicsT>
397 void RHSOperator<PhysicsT>::Mult(const mfem::Vector &u, mfem::Vector &dudt) const
398 {
399 Theseus::ScopedTimer timer("RHSMult");
400 operator_cache.u_vol_restr_ready = false;
401 operator_cache.u_bnd_restr_ready = false;
402 operator_cache.u_int_restr_ready = false;
403 {
404 Theseus::ScopedTimer rhsPrep("RHSRestriction");
405 const mfem::Vector &pu = this->Prolongate(u);
406 FetchRestrictions(pu, operator_cache.uVol, operator_cache.uInt, operator_cache.uBnd);
407 if (this->P)
408 {
409 operator_cache.pdudt.SetSize(this->P->Height());
410 }
411 }
412 mfem::Vector &pdudt = this->P ? operator_cache.pdudt : dudt;
413
414 // This block is executed by the host
415 int nval_restr = operator_cache.restr_v->Height();
416 if(operator_cache.uVol.Size() != nval_restr){
417 operator_cache.uVol.SetSize(nval_restr);
418 operator_cache.uVol.UseDevice();
419 }
420 mfem::Vector &Ue(operator_cache.uVol);
421 if(operator_cache.rhsVol.Size() != nval_restr){
422 operator_cache.rhsVol.SetSize(nval_restr);
423 operator_cache.rhsVol.UseDevice();
424 }
425 mfem::Vector &dUe(operator_cache.rhsVol);
426
427#ifdef SUBCELL_FV_BLENDING
428 {
429 Theseus::ScopedTimer timer("SubcellBlendingStep");
430 const mfem::Vector &pu = this->Prolongate(u);
431 ComputeIndicatorField(pu);
432 CheckIndicatorSmoothness();
433 ComputeBlendingCoefficient();
434 }
435#endif
436
437 // Zero on-device
438 int psize = pdudt.Size();
439 mfem::real_t *pdudt_d = pdudt.Write();
440 {
441 Theseus::ScopedTimer zerotim("ZeroRHS");
442 mfem::forall(psize, [=] MFEM_HOST_DEVICE (int i) { pdudt_d[i] = 0.0; });
443 }
444
445 // max_char_speed is consumed by external components
446 // between steps
447 {
448 Theseus::ScopedTimer timer("FlowMult");
449 max_char_speed = FlowMult(u, pdudt);
450 }
451
452 if (this->Serial())
453 {
454 if(this->cP) this->cP->MultTranspose(pdudt, dudt);
455 }
456 else
457 {
458 if(this->P) this->P->MultTranspose(pdudt, dudt);
459 }
460
461 const int N = this->ess_tdof_list.Size();
462 const auto idx = this->ess_tdof_list.Read();
463 auto DU_RW = dudt.ReadWrite();
464 mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { DU_RW[idx[i]] = 0.0; });
465
466 // reset restriction readiness
467 operator_cache.u_vol_restr_ready = false;
468 operator_cache.u_bnd_restr_ready = false;
469 operator_cache.u_int_restr_ready = false;
470 }
471}
virtual void Finalize(mfem::real_t time=0)
Definition RHSOperator.hpp:84
Definition RHSOperator.hpp:121
void Mult(const mfem::Vector &u, mfem::Vector &dudt) const override
Definition RHSOperator_impl.hpp:397
void FetchRestrictions(const mfem::Vector &pu, mfem::Vector &uVol, mfem::Vector &uInt, mfem::Vector &uBnd) const
Definition RHSOperator_impl.hpp:39
void Finalize(mfem::real_t time=0) override
Definition RHSOperator_impl.hpp:11
void ComputeIntegralMeasures(const mfem::Vector &u, Theseus::IntegralMeasures &diag) const override
Definition RHSOperator_impl.hpp:208
Definition timer.hpp:17
MFEM_HOST_DEVICE mfem::real_t rmin(mfem::real_t a, mfem::real_t b)
Definition theseus_kernels.hpp:18
MFEM_HOST_DEVICE void el_gather_state(const mfem::real_t *u, const int dof, const int num_eq, const int id, mfem::real_t *dst)
Definition theseus_kernels.hpp:135
MFEM_HOST_DEVICE mfem::real_t rmax(mfem::real_t a, mfem::real_t b)
Definition theseus_kernels.hpp:17
Definition AxisymmetricGeometry.hpp:15
void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
Definition dgsem_cache_utilities.hpp:844
void BuildPerssonDeviceCache(CacheT &c, Prandtl::ModalBasis &modalBasis)
Definition dgsem_cache_utilities.hpp:950
void AssembleBoundaryFaceGeometryTerms(mfem::FiniteElementSpace *fes, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:337
constexpr const int MAXEQ
Definition theseus_kernels.hpp:13
void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:18
void ValidateAxisBoundaryGeometry(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:943
void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:627
static MFEM_HOST_DEVICE mfem::real_t MeasureMultiplier(bool axisymmetric, mfem::real_t radius)
Definition AxisymmetricGeometry.hpp:37
Definition dgsem_cache.hpp:16
mfem::real_t max_temp
Definition dgsem_cache.hpp:22
mfem::real_t min_dens
Definition dgsem_cache.hpp:25
mfem::real_t en
Definition dgsem_cache.hpp:19
mfem::real_t mass
Definition dgsem_cache.hpp:17
mfem::real_t ke
Definition dgsem_cache.hpp:18
mfem::real_t max_press
Definition dgsem_cache.hpp:20
mfem::real_t max_dens
Definition dgsem_cache.hpp:24
mfem::real_t min_press
Definition dgsem_cache.hpp:21
mfem::real_t min_temp
Definition dgsem_cache.hpp:23
Definition GasState.hpp:127
MFEM_HOST_DEVICE mfem::real_t energy(const StateLayout &L) const
Definition GasState.hpp:190