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 template<typename PhysicsT>
11 {
12 const mfem::Vector &pu = this->Prolongate(u);
13 const int restricted_size = operator_cache.restr_v->Height();
14 if (operator_cache.uVol.Size() != restricted_size)
15 {
16 operator_cache.uVol.SetSize(restricted_size);
17 operator_cache.uVol.UseDevice();
18 }
19 operator_cache.restr_v->Mult(pu, operator_cache.uVol);
20
21 const mfem::real_t advection_scale = operator_cache.stabilityAdvectionScale;
22 const mfem::real_t diffusion_scale = operator_cache.stabilityDiffusionScale;
23 const mfem::real_t surface_scale = operator_cache.stabilitySurfaceScale;
24 const bool viscous = viscousFlowModel;
25
26 const auto dc = device_cache;
27 const auto gas_model = *gas;
28 const mfem::real_t *state = operator_cache.uVol.Read();
29 const mfem::real_t *jacobian = operator_cache.elJac.Read();
30 const mfem::real_t *metric = operator_cache.elMetric.Read();
31 const int points = dc.num_elements * dc.ndof_scalar_el;
32 const int dofs = dc.ndof_scalar_el;
33 const int equations = dc.num_equations;
34 const int dimensions = dc.dim;
35
36 mfem::Vector &advective = operator_cache.stabilityAdvectiveRate;
37 mfem::Vector &diffusive = operator_cache.stabilityDiffusiveRate;
38 if (advective.Size() != points)
39 {
40 advective.SetSize(points);
41 advective.UseDevice();
42 diffusive.SetSize(points);
43 diffusive.UseDevice();
44 }
45 mfem::real_t *advective_rate = advective.Write();
46 mfem::real_t *diffusive_rate = diffusive.Write();
47 mfem::forall(points, [=] MFEM_HOST_DEVICE (int point)
48 {
49 const int element = point / dofs;
50 const int node = point % dofs;
51 const mfem::real_t *element_state = state + element * dofs * equations;
52 mfem::real_t point_state[MAXEQ];
53 Kernels::el_gather_state(element_state, dofs, equations, node, point_state);
54 PointStateView S{point_state};
55 const mfem::real_t inverse_jacobian = 1.0 / jacobian[point];
56 mfem::real_t directional_sum = 0.0;
57 mfem::real_t metric_square_sum = 0.0;
58 for (int reference_direction = 0; reference_direction < dimensions;
59 ++reference_direction)
60 {
61 mfem::real_t velocity_dot_metric = 0.0;
62 mfem::real_t metric_norm_squared = 0.0;
63 for (int physical_direction = 0; physical_direction < dimensions;
64 ++physical_direction)
65 {
66 const mfem::real_t value = metric[
67 (point * dimensions + reference_direction) * dimensions
68 + physical_direction];
69 velocity_dot_metric += gas_model.velocity(S, physical_direction) * value;
70 metric_norm_squared += value * value;
71 }
72 directional_sum += MappedDirectionalAcousticRate(
73 velocity_dot_metric, metric_norm_squared,
74 gas_model.sound_speed(S), inverse_jacobian);
75 metric_square_sum += metric_norm_squared
76 * inverse_jacobian * inverse_jacobian;
77 }
78 advective_rate[point] = advection_scale * directional_sum;
79 if (viscous)
80 {
81 const mfem::real_t density = gas_model.density(S);
82 const mfem::real_t gamma = gas_model.gamma(S);
83 const mfem::real_t shear_viscosity = gas_model.viscosity(S);
84 const mfem::real_t longitudinal_viscosity =
85 mfem::real_t(4.0 / 3.0) * shear_viscosity
86 + gas_model.bulk_viscosity(S);
87 const mfem::real_t momentum_diffusivity =
88 Kernels::rmax(shear_viscosity, longitudinal_viscosity) / density;
89 const mfem::real_t thermal_diffusivity =
90 gas_model.thermal_conductivity(S) * gamma
91 / (density * gas_model.cp(S));
92 const mfem::real_t effective_diffusivity =
93 Kernels::rmax(momentum_diffusivity, thermal_diffusivity);
94 diffusive_rate[point] = diffusion_scale * effective_diffusivity
95 * metric_square_sum;
96 }
97 else
98 {
99 diffusive_rate[point] = 0.0;
100 }
101 });
102
103 StabilityEstimate estimate;
104 const mfem::real_t *advective_host = advective.HostRead();
105 const mfem::real_t *diffusive_host = diffusive.HostRead();
106 for (int point = 0; point < points; ++point)
107 {
108 estimate.advective_rate = std::max(estimate.advective_rate,
109 advective_host[point]);
110 estimate.diffusive_rate = std::max(estimate.diffusive_rate,
111 diffusive_host[point]);
112 }
113
114 // Surface corrections carry the endpoint quadrature/Jacobian scaling in
115 // fw_minus/fw_plus. Compute their normal-aligned acoustic rates directly
116 // instead of consuming flux-specific wave-speed return values, whose units
117 // historically differ between numerical flux implementations.
118 const int interior_size = operator_cache.restr_f->Height();
119 const int face_points = dc.num_face_points;
120 const int interior_points = interior_size / (2 * equations);
121 if (interior_points > 0)
122 {
123 if (operator_cache.uInt.Size() != interior_size)
124 {
125 operator_cache.uInt.SetSize(interior_size);
126 operator_cache.uInt.UseDevice();
127 }
128 operator_cache.restr_f->Mult(pu, operator_cache.uInt);
129 mfem::Vector &surface = operator_cache.stabilitySurfaceRate;
130 if (surface.Size() < interior_points)
131 {
132 surface.SetSize(interior_points);
133 surface.UseDevice();
134 }
135 const mfem::real_t *face_state = operator_cache.uInt.Read();
136 const mfem::real_t *normal = dc.nor_d;
137 const mfem::real_t *weight_minus = dc.fw_minus_d;
138 const mfem::real_t *weight_plus = dc.fw_plus_d;
139 mfem::real_t *surface_rate = surface.Write();
140 mfem::forall(interior_points, [=] MFEM_HOST_DEVICE (int point)
141 {
142 const int face = point / face_points;
143 const int face_point = point % face_points;
144 const int face_size = 2 * face_points * equations;
145 const mfem::real_t *states = face_state + face * face_size;
146 mfem::real_t minus_state[MAXEQ];
147 mfem::real_t plus_state[MAXEQ];
148 for (int equation = 0; equation < equations; ++equation)
149 {
150 minus_state[equation] = states[(0 * equations + equation)
151 * face_points + face_point];
152 plus_state[equation] = states[(1 * equations + equation)
153 * face_points + face_point];
154 }
155 PointStateView minus{minus_state};
156 PointStateView plus{plus_state};
157 mfem::real_t normal_squared = 0.0;
158 mfem::real_t minus_normal_velocity = 0.0;
159 mfem::real_t plus_normal_velocity = 0.0;
160 for (int direction = 0; direction < dimensions; ++direction)
161 {
162 const mfem::real_t normal_component =
163 normal[point * dimensions + direction];
164 normal_squared += normal_component * normal_component;
165 minus_normal_velocity += gas_model.velocity(minus, direction)
166 * normal_component;
167 plus_normal_velocity += gas_model.velocity(plus, direction)
168 * normal_component;
169 }
170 const mfem::real_t normal_magnitude = Kernels::rsqrt(normal_squared);
171 const mfem::real_t normal_wave_speed = Kernels::rmax(
172 Kernels::rabs(minus_normal_velocity)
173 + gas_model.sound_speed(minus) * normal_magnitude,
174 Kernels::rabs(plus_normal_velocity)
175 + gas_model.sound_speed(plus) * normal_magnitude);
176 surface_rate[point] = normal_wave_speed
177 * Kernels::rmax(Kernels::rabs(weight_minus[point]),
178 Kernels::rabs(weight_plus[point]))
179 * surface_scale;
180 });
181 const mfem::real_t *surface_host = surface.HostRead();
182 for (int point = 0; point < interior_points; ++point)
183 estimate.surface_rate = std::max(estimate.surface_rate,
184 surface_host[point]);
185 }
186
187 const int boundary_size = operator_cache.restr_b->Height();
188 const int boundary_points = boundary_size / equations;
189 if (boundary_points > 0)
190 {
191 if (operator_cache.uBnd.Size() != boundary_size)
192 {
193 operator_cache.uBnd.SetSize(boundary_size);
194 operator_cache.uBnd.UseDevice();
195 }
196 operator_cache.restr_b->Mult(pu, operator_cache.uBnd);
197 mfem::Vector &surface = operator_cache.stabilitySurfaceRate;
198 const int surface_offset = interior_points;
199 if (surface.Size() < surface_offset + boundary_points)
200 {
201 surface.SetSize(surface_offset + boundary_points);
202 surface.UseDevice();
203 }
204 const mfem::real_t *face_state = operator_cache.uBnd.Read();
205 const mfem::real_t *normal = dc.bnd_nor_d;
206 const mfem::real_t *weight = dc.bnd_wt_d;
207 const int *boundary_marker = dc.bnd_marker_index_d;
208 const BCDescriptor *boundary_conditions = dc.bc_descr_d;
209 const mfem::real_t *boundary_data = dc.bc_vector_d;
210 mfem::real_t *surface_rate = surface.Write() + surface_offset;
211 mfem::forall(boundary_points, [=] MFEM_HOST_DEVICE (int point)
212 {
213 const int face = point / face_points;
214 const int face_point = point % face_points;
215 const int marker = boundary_marker[face];
216 if (marker < 0)
217 {
218 surface_rate[point] = 0.0;
219 return;
220 }
221 const mfem::real_t *states = face_state
222 + face * face_points * equations;
223 mfem::real_t interior_state[MAXEQ];
224 for (int equation = 0; equation < equations; ++equation)
225 interior_state[equation] = states[equation * face_points + face_point];
226 PointStateView interior{interior_state};
227 mfem::real_t normal_squared = 0.0;
228 mfem::real_t normal_velocity = 0.0;
229 for (int direction = 0; direction < dimensions; ++direction)
230 {
231 const mfem::real_t normal_component =
232 normal[point * dimensions + direction];
233 normal_squared += normal_component * normal_component;
234 normal_velocity += gas_model.velocity(interior, direction)
235 * normal_component;
236 }
237 const mfem::real_t normal_wave_speed =
238 Kernels::rabs(normal_velocity)
239 + gas_model.sound_speed(interior) * Kernels::rsqrt(normal_squared);
240 mfem::real_t boundary_wave_speed = normal_wave_speed;
241 const BCDescriptor &condition = boundary_conditions[marker];
242 if (condition.type == int(BCType::SupersonicInflow))
243 {
244 PointStateView exterior{boundary_data + condition.data_index};
245 mfem::real_t exterior_normal_velocity = 0.0;
246 for (int direction = 0; direction < dimensions; ++direction)
247 exterior_normal_velocity += gas_model.velocity(exterior, direction)
248 * normal[point * dimensions + direction];
249 boundary_wave_speed = Kernels::rmax(
250 boundary_wave_speed,
251 Kernels::rabs(exterior_normal_velocity)
252 + gas_model.sound_speed(exterior) * Kernels::rsqrt(normal_squared));
253 }
254 surface_rate[point] = boundary_wave_speed * Kernels::rabs(weight[point])
255 * surface_scale;
256 });
257 const mfem::real_t *surface_host = surface.HostRead() + surface_offset;
258 for (int point = 0; point < boundary_points; ++point)
259 estimate.surface_rate = std::max(estimate.surface_rate,
260 surface_host[point]);
261 }
262 return estimate;
263 }
264
265
266 template<typename PhysicsT>
267 void RHSOperator<PhysicsT>::Finalize(mfem::real_t time)
268 {
269 Theseus::ScopedTimer finalize_timer("RHSOperator::Finalize");
270
272 GetOperatorCache(vfes.get(), &operator_cache);
273 AssembleBoundaryFaceGeometryTerms(vfes.get(), bdr_marker, &operator_cache);
274#ifdef SUBCELL_FV_BLENDING
275 {
276 Theseus::ScopedTimer timer("ComputeSubcellMetrics");
277 ComputeSubcellMetrics(vfes.get(), &operator_cache);
278 }
279#endif
280
281 operator_cache.bc_descriptors = bc_descriptors;
282 operator_cache.bc_scalar_data = bc_scalar_data;
283 operator_cache.bc_vector_data = bc_vector_data;
284 ValidateAxisBoundaryGeometry(operator_cache);
285
286#ifdef SUBCELL_FV_BLENDING
287 MFEM_VERIFY(indicator, "SUBCELL_FV_BLENDING enabled but indicator is null.");
288 BuildPerssonDeviceCache(operator_cache, indicator->ModalBasis());
289#endif
290 GetDeviceCache(operator_cache, device_cache);
291 }
292
293 // pu should be prolongated
294 template<typename PhysicsT>
295 void RHSOperator<PhysicsT>::FetchRestrictions(const mfem::Vector &pu, mfem::Vector &uVol,
296 mfem::Vector &uInt, mfem::Vector &uBnd) const
297 {
298 Theseus::ScopedTimer timer("FetchRestrictions");
299 const int psize = operator_cache.restr_v->Height();
300 if(uVol.Size() != psize){
301 uVol.SetSize(psize);
302 uVol.UseDevice();
303 }
304 {
305 Theseus::ScopedTimer vrt("VolumeRestriction");
306 operator_cache.restr_v->Mult(pu, uVol);
307 }
308 const int int_restr_size = operator_cache.restr_f->Height();
309 if(uInt.Size() != int_restr_size){
310 uInt.SetSize(int_restr_size);
311 uInt.UseDevice();
312 }
313 {
314 Theseus::ScopedTimer ifr("InteriorFaceRestriction");
315 operator_cache.restr_f->Mult(pu, uInt);
316 }
317 const int bnd_restr_size = operator_cache.restr_b->Height();
318 if(uBnd.Size() != bnd_restr_size){
319 uBnd.SetSize(bnd_restr_size);
320 uBnd.UseDevice();
321 }
322 {
323 Theseus::ScopedTimer bndr("BoundaryFaceRestriction");
324 operator_cache.restr_b->Mult(pu, uBnd);
325 }
326 operator_cache.u_vol_restr_ready = true;
327 operator_cache.u_bnd_restr_ready = true;
328 operator_cache.u_int_restr_ready = true;
329 }
330
331#ifdef SUBCELL_FV_BLENDING
332 template<typename PhysicsT>
333 void RHSOperator<PhysicsT>::ComputeIndicatorField(const mfem::Vector &pu) const
334 {
335 Theseus::ScopedTimer timer("ComputeIndicator");
336
337 // This block is executed by the host
338 const int nval_restr = operator_cache.restr_v->Height();
339 // Copy the device cache so that it is not member data
340 auto dc = device_cache;
341
342 // Device cache parameters
343 const int dim = dc.dim;
344 const int ne = dc.num_elements;
345 const int ndof = dc.ndof_scalar_el;
346 const int neq = dc.num_equations;
347 const int Np_x = dc.Np_x;
348 const int Np_y = dc.Np_y;
349 const int Np_z = dc.Np_z;
350
351 MFEM_ASSERT(nval_restr == ne*ndof*neq, "Unexpected size for volume restriction in indicator calc.");
352 const int nval_ind = nval_restr / neq;
353
354 if(operator_cache.uVol.Size() != nval_restr){
355 operator_cache.uVol.SetSize(nval_restr);
356 operator_cache.uVol.UseDevice();
357 }
358 mfem::Vector &Ue(operator_cache.uVol);
359 if(!operator_cache.u_vol_restr_ready){
360 operator_cache.restr_v->Mult(pu, Ue);
361 operator_cache.u_vol_restr_ready = true;
362 }
363 const mfem::real_t *Ue_d = Ue.Read();
364
365 mfem::Vector &indicator_field(operator_cache.indicatorField);
366 if(indicator_field.Size() != nval_ind){
367 indicator_field.SetSize(nval_ind);
368 indicator_field.UseDevice();
369 }
370 mfem::real_t *ifield_d = indicator_field.Write();
371
372 const int estride = ndof*neq;
373
374 // Inside the FORALL below, executed on device
375 mfem::forall(nval_ind, [=] MFEM_HOST_DEVICE (int vind)
376 {
377 const int e = vind / ndof;
378 const int evind = vind - e * ndof;
379 const mfem::real_t *u_el = Ue_d + e * estride;
380 mfem::real_t elstate[Theseus::MAXEQ];
381 Theseus::Kernels::el_gather_state(u_el, ndof, neq, evind, elstate);
382 Theseus::PointStateView S{elstate};
383 ifield_d[vind] = dc.gas.pressure(S) * dc.gas.density(S);
384 });
385
386 }
387
388 template<typename PhysicsT>
389 void RHSOperator<PhysicsT>::ComputeBlendingCoefficient() const
390 {
391 ScopedTimer timer("ComputeBlendingCoeff");
392 const mfem::real_t *eta_d = operator_cache.eta.Read();
393 mfem::real_t *alpha_d = operator_cache.alpha->Write();
394 // operator_cache.alpha_d;
395 int ne = operator_cache.num_elements;
396 mfem::real_t mthresh = modalThreshold;
397 mfem::real_t sharp_fac = sharpness_fac;
398 mfem::real_t alpmin = alpha_min;
399 mfem::real_t alpmax = alpha_max;
400 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
401 {
402 mfem::real_t alpha_dof = \
403 1.0 / (1.0 + std::exp(-sharp_fac * (eta_d[e] - mthresh) / mthresh));
404 if (alpha_dof < alpmin)
405 {
406 alpha_dof = 0.0;
407 }
408 else if (alpha_dof > (1.0 - alpmin))
409 {
410 alpha_dof = 1.0;
411 }
412 alpha_d[e] = std::min(alpha_dof, alpmax);
413 });
414 }
415
416 template<typename PhysicsT>
417 void RHSOperator<PhysicsT>::CheckIndicatorSmoothness() const
418 {
419 Theseus::ScopedTimer timer("CheckIndicatorSmoothness");
420
421 const int ne = operator_cache.num_elements;
422 const int ndofs = operator_cache.ndof_scalar_el;
423 constexpr int block_size = 256;
424
425 const mfem::real_t *indicator_d = operator_cache.indicatorField.Read();
426 const mfem::real_t *modal_d = operator_cache.modal.Read();
427 const mfem::real_t *keep_M1_d = operator_cache.keep_M1.Read();
428 const mfem::real_t *keep_M2_d = operator_cache.keep_M2.Read();
429 mfem::real_t *eta_d = operator_cache.eta.Write();
430
431 // One thread block cooperates on each element. The previous kernel used
432 // one thread per element, leaving the dense ndofs-by-ndofs modal transform
433 // entirely serial and severely under-filling accelerators at modest ne.
434 mfem::forall_2D(ne, block_size, 1, [=] MFEM_HOST_DEVICE (int e)
435 {
436 const mfem::real_t *u = indicator_d + e * ndofs;
437
438 MFEM_SHARED mfem::real_t mm_s[block_size];
439 MFEM_SHARED mfem::real_t m1m1_s[block_size];
440 MFEM_SHARED mfem::real_t m2m2_s[block_size];
441
442 MFEM_FOREACH_THREAD(t, x, block_size)
443 {
444 mm_s[t] = 0.0;
445 m1m1_s[t] = 0.0;
446 m2m2_s[t] = 0.0;
447 }
448 MFEM_SYNC_THREAD;
449
450 MFEM_FOREACH_THREAD(m, x, ndofs)
451 {
452 mfem::real_t mode = 0.0;
453 for (int q = 0; q < ndofs; ++q)
454 {
455 // Modal data is cached transposed so adjacent threads read adjacent
456 // coefficients while each dot product retains its original q order.
457 mode += modal_d[q * ndofs + m] * u[q];
458 }
459
460 const int t = MFEM_THREAD_ID(x);
461 const mfem::real_t mode2 = mode * mode;
462 mm_s[t] += mode2;
463 m1m1_s[t] += keep_M1_d[m] * mode2;
464 m2m2_s[t] += keep_M2_d[m] * mode2;
465 }
466 MFEM_SYNC_THREAD;
467
468 for (int stride = block_size / 2; stride > 0; stride /= 2)
469 {
470 MFEM_FOREACH_THREAD(t, x, stride)
471 {
472 mm_s[t] += mm_s[t + stride];
473 m1m1_s[t] += m1m1_s[t + stride];
474 m2m2_s[t] += m2m2_s[t + stride];
475 }
476 MFEM_SYNC_THREAD;
477 }
478
479 if (MFEM_THREAD_ID(x) == 0)
480 {
481 const mfem::real_t mm = mm_s[0];
482 const mfem::real_t m1m1 = m1m1_s[0];
483 const mfem::real_t m2m2 = m2m2_s[0];
484 const mfem::real_t eps = 1.0e-30;
485 mfem::real_t val = 0.0;
486
487 if (mm > eps)
488 {
489 val = 1.0 - m1m1 / mm;
490 if (m1m1 > eps)
491 {
492 val = Theseus::Kernels::rmax(val, 1.0 - m2m2 / m1m1);
493 }
494 else
495 {
496 val = 1.0;
497 }
498 }
499 eta_d[e] = Theseus::Kernels::rmin(
500 Theseus::Kernels::rmax(val, 0.0), 1.0);
501 }
502 });
503
504 }
505#endif
506
507 template<typename PhysicsT>
509 {
510 Theseus::ScopedTimer timer("ComputeIntegralMeasures");
511
512 // This block is executed by the host
513 const int nval_restr = operator_cache.restr_v->Height();
514
515 // Copy the device cache so that it is not member data
516 auto dc = device_cache;
517
518 // Device cache parameters
519 const int ne = dc.num_elements;
520 const int ndof = dc.ndof_scalar_el;
521 const int neq = dc.num_equations;
522 const mfem::real_t *qWts_d = dc.elQWgts_d;
523 const mfem::real_t *radius_d = dc.elRadius_d;
524 auto gas = dc.gas;
525
526 if(operator_cache.uVol.Size() != nval_restr){
527 operator_cache.uVol.SetSize(nval_restr);
528 operator_cache.uVol.UseDevice();
529 }
530 mfem::Vector &Ue(operator_cache.uVol);
531 if(!operator_cache.u_vol_restr_ready){
532 operator_cache.restr_v->Mult(u, Ue);
533 operator_cache.u_vol_restr_ready = true;
534 }
535
536 const mfem::real_t *Ue_d = Ue.Read();
537 const int estride = ndof*neq;
538
539 mfem::Vector elMass_integral(ne);
540 mfem::Vector elKE_integral(ne);
541 mfem::Vector elEnergy_integral(ne);
542 mfem::Vector elMaxPressure(ne);
543 mfem::Vector elMaxTemperature(ne);
544 mfem::Vector elMaxDensity(ne);
545 mfem::Vector elMinPressure(ne);
546 mfem::Vector elMinTemperature(ne);
547 mfem::Vector elMinDensity(ne);
548
549 elMass_integral.UseDevice();
550 elKE_integral.UseDevice();
551 elEnergy_integral.UseDevice();
552 elMaxPressure.UseDevice();
553 elMaxTemperature.UseDevice();
554 elMaxDensity.UseDevice();
555 elMinPressure.UseDevice();
556 elMinTemperature.UseDevice();
557 elMinDensity.UseDevice();
558
559 mfem::real_t *elMass_int_d = elMass_integral.Write();
560 mfem::real_t *elKE_int_d = elKE_integral.Write();
561 mfem::real_t *elEnergy_int_d = elEnergy_integral.Write();
562
563 mfem::real_t *elPress_max_d = elMaxPressure.Write();
564 mfem::real_t *elTemp_max_d = elMaxTemperature.Write();
565 mfem::real_t *elDens_max_d = elMaxDensity.Write();
566 mfem::real_t *elPress_min_d = elMinPressure.Write();
567 mfem::real_t *elTemp_min_d = elMinTemperature.Write();
568 mfem::real_t *elDens_min_d = elMinDensity.Write();
569
570 // Inside the FORALL below, executed on device
571 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
572 {
573 const mfem::real_t *u_el = Ue_d + e * estride;
574 const mfem::real_t *qWgt = qWts_d + e * ndof;
575 const mfem::real_t *radius = dc.axisymmetric ?
576 radius_d + e * ndof : nullptr;
577
578 mfem::real_t mass_int = 0.0;
579 mfem::real_t ke_int = 0.0;
580 mfem::real_t en_int = 0.0;
581 mfem::real_t min_dens = 1e32;
582 mfem::real_t max_dens = 0.0;
583 mfem::real_t min_temp = 1e32;
584 mfem::real_t max_temp = 0.0;
585 mfem::real_t min_press = 1e32;
586 mfem::real_t max_press = 0.0;
587
588 for(int ep = 0;ep < ndof;ep++){
589 mfem::real_t elstate[Theseus::MAXEQ];
590 Theseus::Kernels::el_gather_state(u_el, ndof, neq, ep, elstate);
591 Theseus::PointStateView S{elstate};
592
593 mfem::real_t rho = gas.density(S);
594 mfem::real_t ke = gas.kinetic_energy_density(S);
595 mfem::real_t rhoE = gas.energy(S); // energy density
596 mfem::real_t press = gas.pressure(S);
597 mfem::real_t temper = gas.temperature(S);
598
599 const mfem::real_t measure = qWgt[ep] *
601 dc.axisymmetric, dc.axisymmetric ? radius[ep] : 0.0);
602 mass_int += rho * measure;
603 ke_int += ke * measure;
604 en_int += rhoE * measure;
605
606 min_temp = Theseus::Kernels::rmin(min_temp, temper);
607 max_temp = Theseus::Kernels::rmax(max_temp, temper);
608 min_dens = Theseus::Kernels::rmin(min_dens, rho);
609 max_dens = Theseus::Kernels::rmax(max_dens, rho);
610 min_press = Theseus::Kernels::rmin(min_press, press);
611 max_press = Theseus::Kernels::rmax(max_press, press);
612 }
613
614 elMass_int_d[e] = mass_int;
615 elKE_int_d[e] = ke_int;
616 elEnergy_int_d[e] = en_int;
617 elPress_max_d[e] = max_press;
618 elPress_min_d[e] = min_press;
619 elDens_max_d[e] = max_dens;
620 elDens_min_d[e] = min_dens;
621 elTemp_min_d[e] = min_temp;
622 elTemp_max_d[e] = max_temp;
623
624 });
625
626 // diag.mass = mfem::Sum(elMass_integral);
627 // diag.ke = mfem::Sum(elKE_integral);
628 // diag.en = mfem::Sum(elEnergy_integral);
629 diag.mass = 0.0;
630 diag.ke = 0.0;
631 diag.en = 0.0;
632 diag.min_press = 1e32;
633 diag.max_press = 0.0;
634 diag.min_dens = 1e32;
635 diag.max_dens = 0.0;
636 diag.min_temp = 1e32;
637 diag.max_temp = 0.0;
638
639 const mfem::real_t *mass_h = elMass_integral.HostRead();
640 const mfem::real_t *ke_h = elKE_integral.HostRead();
641 const mfem::real_t *en_h = elEnergy_integral.HostRead();
642 const mfem::real_t *minpress_h = elMinPressure.HostRead();
643 const mfem::real_t *maxpress_h = elMaxPressure.HostRead();
644 const mfem::real_t *mindens_h = elMinDensity.HostRead();
645 const mfem::real_t *maxdens_h = elMaxDensity.HostRead();
646 const mfem::real_t *mintemp_h = elMinTemperature.HostRead();
647 const mfem::real_t *maxtemp_h = elMaxTemperature.HostRead();
648
649 for (int e = 0; e < ne; ++e) {
650 diag.mass += mass_h[e];
651 diag.ke += ke_h[e];
652 diag.en += en_h[e];
653 diag.min_press = Theseus::Kernels::rmin(diag.min_press, minpress_h[e]);
654 diag.max_press = Theseus::Kernels::rmax(diag.max_press, maxpress_h[e]);
655 diag.min_temp = Theseus::Kernels::rmin(diag.min_temp, mintemp_h[e]);
656 diag.max_temp = Theseus::Kernels::rmax(diag.max_temp, maxtemp_h[e]);
657 diag.min_dens = Theseus::Kernels::rmin(diag.min_dens, mindens_h[e]);
658 diag.max_dens = Theseus::Kernels::rmax(diag.max_dens, maxdens_h[e]);
659 }
660
661 mfem::real_t sendbuf[3] = {diag.mass, diag.ke, diag.en};
662 mfem::real_t recvbuf[3] = {0.0, 0.0, 0.0};
663
664 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_SUM, pmesh->GetComm());
665
666 diag.mass = recvbuf[0];
667 diag.ke = recvbuf[1];
668 diag.en = recvbuf[2];
669
670 sendbuf[0] = diag.min_press;
671 sendbuf[1] = diag.min_temp;
672 sendbuf[2] = diag.min_dens;
673
674 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_MIN, pmesh->GetComm());
675
676 diag.min_press = recvbuf[0];
677 diag.min_temp = recvbuf[1];
678 diag.min_dens = recvbuf[2];
679
680 sendbuf[0] = diag.max_press;
681 sendbuf[1] = diag.max_temp;
682 sendbuf[2] = diag.max_dens;
683
684 MPI_Allreduce(sendbuf, recvbuf, 3, mfem::MPITypeMap<mfem::real_t>::mpi_type, MPI_MAX, pmesh->GetComm());
685
686 diag.max_press = recvbuf[0];
687 diag.max_temp = recvbuf[1];
688 diag.max_dens = recvbuf[2];
689
690 if(diag0.mass == 0.0){
691 diag0 = diag;
692 }
693
694 }
695
696 template<typename PhysicsT>
697 void RHSOperator<PhysicsT>::Mult(const mfem::Vector &u, mfem::Vector &dudt) const
698 {
699 Theseus::ScopedTimer timer("RHSMult");
700 operator_cache.u_vol_restr_ready = false;
701 operator_cache.u_bnd_restr_ready = false;
702 operator_cache.u_int_restr_ready = false;
703 {
704 Theseus::ScopedTimer rhsPrep("RHSRestriction");
705 const mfem::Vector &pu = this->Prolongate(u);
706 FetchRestrictions(pu, operator_cache.uVol, operator_cache.uInt, operator_cache.uBnd);
707 if (this->P)
708 {
709 if(operator_cache.pdudt.Size() != this->P->Height()){
710 operator_cache.pdudt.SetSize(this->P->Height());
711 }
712 }
713 }
714 mfem::Vector &pdudt = this->P ? operator_cache.pdudt : dudt;
715
716 // This block is executed by the host
717 int nval_restr = operator_cache.restr_v->Height();
718 if(operator_cache.uVol.Size() != nval_restr){
719 operator_cache.uVol.SetSize(nval_restr);
720 operator_cache.uVol.UseDevice();
721 }
722 mfem::Vector &Ue(operator_cache.uVol);
723 if(operator_cache.rhsVol.Size() != nval_restr){
724 operator_cache.rhsVol.SetSize(nval_restr);
725 operator_cache.rhsVol.UseDevice();
726 }
727 mfem::Vector &dUe(operator_cache.rhsVol);
728
729#ifdef SUBCELL_FV_BLENDING
730 {
731 Theseus::ScopedTimer timer("SubcellBlendingStep");
732 const mfem::Vector &pu = this->Prolongate(u);
733 ComputeIndicatorField(pu);
734 CheckIndicatorSmoothness();
735 ComputeBlendingCoefficient();
736 }
737#endif
738
739 // Zero on-device
740 int psize = pdudt.Size();
741 mfem::real_t *pdudt_d = pdudt.Write();
742 {
743 Theseus::ScopedTimer zerotim("ZeroRHS");
744 mfem::forall(psize, [=] MFEM_HOST_DEVICE (int i) { pdudt_d[i] = 0.0; });
745 }
746
747 {
748 Theseus::ScopedTimer timer("FlowMult");
749 FlowMult(u, pdudt);
750 }
751
752 if (this->Serial())
753 {
754 if(this->cP) this->cP->MultTranspose(pdudt, dudt);
755 }
756 else
757 {
758 if(this->P) this->P->MultTranspose(pdudt, dudt);
759 }
760
761 const int N = this->ess_tdof_list.Size();
762 const auto idx = this->ess_tdof_list.Read();
763 auto DU_RW = dudt.ReadWrite();
764 mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { DU_RW[idx[i]] = 0.0; });
765
766 // reset restriction readiness
767 operator_cache.u_vol_restr_ready = false;
768 operator_cache.u_bnd_restr_ready = false;
769 operator_cache.u_int_restr_ready = false;
770 }
771}
virtual void Finalize(mfem::real_t time=0)
Definition RHSOperator.hpp:84
Definition RHSOperator.hpp:118
void Mult(const mfem::Vector &u, mfem::Vector &dudt) const override
Definition RHSOperator_impl.hpp:697
void FetchRestrictions(const mfem::Vector &pu, mfem::Vector &uVol, mfem::Vector &uInt, mfem::Vector &uBnd) const
Definition RHSOperator_impl.hpp:295
StabilityEstimate EstimateStability(const mfem::Vector &u) const override
Definition RHSOperator_impl.hpp:10
void Finalize(mfem::real_t time=0) override
Definition RHSOperator_impl.hpp:267
void ComputeIntegralMeasures(const mfem::Vector &u, Theseus::IntegralMeasures &diag) const override
Definition RHSOperator_impl.hpp:508
Definition timer.hpp:22
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 rsqrt(mfem::real_t x)
Definition theseus_kernels.hpp:19
MFEM_HOST_DEVICE mfem::real_t rmax(mfem::real_t a, mfem::real_t b)
Definition theseus_kernels.hpp:17
MFEM_HOST_DEVICE mfem::real_t rabs(mfem::real_t x)
Definition theseus_kernels.hpp:22
Definition AxisymmetricGeometry.hpp:15
void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
Definition dgsem_cache_utilities.hpp:747
void BuildPerssonDeviceCache(CacheT &c, Prandtl::ModalBasis &modalBasis)
Definition dgsem_cache_utilities.hpp:848
void AssembleBoundaryFaceGeometryTerms(mfem::FiniteElementSpace *fes, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:337
MFEM_HOST_DEVICE mfem::real_t MappedDirectionalAcousticRate(const mfem::real_t velocity_dot_metric, const mfem::real_t metric_norm_squared, const mfem::real_t sound_speed, const mfem::real_t inverse_jacobian)
Definition StabilityEstimate.hpp:12
constexpr const int MAXEQ
Definition theseus_kernels.hpp:13
void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:19
void ValidateAxisBoundaryGeometry(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:841
void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:623
static MFEM_HOST_DEVICE mfem::real_t MeasureMultiplier(bool axisymmetric, mfem::real_t radius)
Definition AxisymmetricGeometry.hpp:37
Definition bc_cache_utilities.hpp:35
int type
Definition bc_cache_utilities.hpp:36
int data_index
Definition bc_cache_utilities.hpp:38
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
MFEM_HOST_DEVICE mfem::real_t velocity(const StateLayout &L, int d) const
Definition GasState.hpp:169
Definition StabilityEstimate.hpp:24
mfem::real_t advective_rate
Definition StabilityEstimate.hpp:25