Cholla 3.0.1-dev
Cholla - Massively parallel hydro on GPUs
Loading...
Searching...
No Matches
prescription.h
1/* This file defines feeback prescriptions that can actually be used within Cholla
2 *
3 * Some (not all) of these prescriptions are defined in terms of factored out stencils. Those stencils
4 * are defined separately in a different header file (feedback/stencil).
5 */
6
7#pragma once
8
9#include "../feedback/feedback.h"
10#include "../feedback/ratecalc.h"
11#include "../feedback/stencil.h"
12#include "../global/global.h"
13#include "../utils/basic_structs.h"
14
15namespace fb_prescription
16{
17
18inline __device__ void log_fb(int cycle_num, int num_SN, bool is_resolved, Real pos_x_indU, Real pos_y_indU,
19 Real pos_z_indU, Real vel_x, Real vel_y, Real vel_z, part_int_t particle_id,
20 Real n_0_cgs = -1.23456789)
21{
22 // use json formatting so this is easier to parse from logs
23 kernel_printf(
24 "..fb: { \"cycle\":%d, \"id\": %lld, \"num_SN\": %d, \"resolved\": %d, \"n0\": %g, \"pos_indU\": [%g, %g, %g], "
25 "\"vel\": [%g, %g, %g]}\n",
26 cycle_num, (long long int)(particle_id), num_SN, int(is_resolved), n_0_cgs, pos_x_indU, pos_y_indU, pos_z_indU,
27 vel_x, vel_y, vel_z);
28}
29
37inline __device__ __host__ Real radius_shell_formation_kpc(Real ndens_cgs, int num_SN)
38{
39 // originally, we adopted the normalization from eq.(31) from Kim & Ostriker (2015)
40 // 30.2 pc (the fit to R_sh for a multiphase ambient medium) and the analytic
41 // scaling for a unifom medium eq.(8)
42 //
43 // subsequently, we made a plot for making comparisons with Figure 6 of
44 // Tigress paper, (Kim & Ostriker 2017) -- we did this for CIE cooling with
45 // ambient temperatures fixed at 1e4 K. This made it clear that we, like TIGRESS,
46 // should use the analytic choice for a singlephase ambient medium (it means we
47 // will more readily transition to unresolved and will decrease chance of over-cooling)
48 return 0.0226 * pow(ndens_cgs, -0.46) * pow(fabs(Real(num_SN)), 0.29);
49}
50
51template <typename Stencil>
53 /* the following 2 attributes exists for testing purposes */
54 static constexpr bool has_resolved_prescription = true;
55 static constexpr bool has_unresolved_prescription = false;
56
57 static __device__ hydro_utilities::VectorXYZ<Real> nearest_noGhostOverlap_pos(
58 hydro_utilities::VectorXYZ<Real> pos_indU, int ng_x, int ng_y, int ng_z, int n_ghost)
59 {
60 return Stencil::nearest_noGhostOverlap_pos(pos_indU, ng_x, ng_y, ng_z, n_ghost);
61 }
62
63 // ToDo: refactor to make use of Stencil::for_each_overlap_zone
64 template <typename Function>
65 static __device__ void for_each_possible_overlap(Real pos_x_indU, Real pos_y_indU, Real pos_z_indU, int nx_g,
66 int ny_g, Function&& f)
67 {
68 Stencil::for_each(hydro_utilities::VectorXYZ<Real>{pos_x_indU, pos_y_indU, pos_z_indU}, nx_g, ny_g,
69 std::forward<Function>(f));
70 }
71
72 static __device__ void apply_feedback(Real pos_x_indU, Real pos_y_indU, Real pos_z_indU, Real vel_x, Real vel_y,
73 Real vel_z, Real age, Real& mass_ref, part_int_t particle_id, Real dx, Real dy,
74 Real dz, int nx_g, int ny_g, int nz_g, int n_ghost, int num_SN, int cycle_num,
75 Real* s_info, Real* conserved_dev)
76 {
77 int tid = threadIdx.x;
78
79 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::countSN] += num_SN;
80 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::countResolved] += num_SN;
81 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalEnergy] += feedback::ENERGY_PER_SN;
82
83 Real dV = dx * dy * dz;
84 Real feedback_energy = num_SN * feedback::ENERGY_PER_SN / dV;
85 Real feedback_density = num_SN * feedback::MASS_PER_SN / dV;
86
87 mass_ref = max(0.0, mass_ref - num_SN * feedback::MASS_PER_SN); // update the cluster mass
88
89 log_fb(cycle_num, num_SN, true, pos_x_indU, pos_y_indU, pos_z_indU, vel_x, vel_y, vel_z, particle_id);
90
91 ResolvedSNPrescription::apply(hydro_utilities::VectorXYZ<Real>{pos_x_indU, pos_y_indU, pos_z_indU}, vel_x, vel_y,
92 vel_z, nx_g, ny_g, nx_g * ny_g * nz_g, conserved_dev, feedback_density,
93 feedback_energy);
94 }
95
96 /* apply the resolved feedback prescription */
97 static __device__ void apply(hydro_utilities::VectorXYZ<Real> pos_indU, Real vel_x, Real vel_y, Real vel_z, int nx_g,
98 int ny_g, int n_cells, Real* conserved_device, Real feedback_density,
99 Real feedback_energy)
100 {
101 Real* density = conserved_device;
102 Real* momentum_x = &conserved_device[n_cells * grid_enum::momentum_x];
103 Real* momentum_y = &conserved_device[n_cells * grid_enum::momentum_y];
104 Real* momentum_z = &conserved_device[n_cells * grid_enum::momentum_z];
105 Real* energy = &conserved_device[n_cells * grid_enum::Energy];
106#ifdef DE
107 Real* gasEnergy = &conserved_device[n_cells * grid_enum::GasEnergy];
108#endif
109
110 Stencil::for_each(pos_indU, nx_g, ny_g, [=](double stencil_vol_frac, int idx3D) {
111 // stencil_vol_frac is the fraction of the total stencil volume enclosed by the given cell
112 // indx3D can be used to index the conserved fields (it assumes ghost-zones are present)
113
114 // Step 1: substract off the kinetic-energy-density from total energy density.
115 // - While we aren't going to inject any of the supernova energy directly as kinetic energy,
116 // the kinetic energy density will change to some degree because the gas density and gas
117 // momentum will be changed
118
119 Real intial_ke_density = 0.5 *
120 (momentum_x[idx3D] * momentum_x[idx3D] + momentum_y[idx3D] * momentum_y[idx3D] +
121 momentum_z[idx3D] * momentum_z[idx3D]) /
122 density[idx3D];
123
124 energy[idx3D] -= intial_ke_density;
125
126 // Step 2: convert the momentum-density into the star's reference frame, update the density,
127 // and then update the momentum-density back into the initial reference-frame
128 // - since we aren't explicitly injecting the supernova-energy as kinetic energy, this is
129 // equivalent to adding momentum in the original frame as is done below
130 double injected_density = stencil_vol_frac * feedback_density;
131
132 momentum_x[idx3D] += vel_x * injected_density;
133 momentum_y[idx3D] += vel_y * injected_density;
134 momentum_z[idx3D] += vel_z * injected_density;
135
136 // Step 2b: actually update the density
137 density[idx3D] += injected_density;
138
139 // Step 3: inject thermal energy
140#ifdef DE
141 gasEnergy[idx3D] += stencil_vol_frac * feedback_energy;
142#endif
143 energy[idx3D] += stencil_vol_frac * feedback_energy;
144
145 // Step 4: reintroduce the kinetic energy density back to the total energy field
146 energy[idx3D] += 0.5 *
147 (momentum_x[idx3D] * momentum_x[idx3D] + momentum_y[idx3D] * momentum_y[idx3D] +
148 momentum_z[idx3D] * momentum_z[idx3D]) /
149 density[idx3D];
150 });
151 }
152};
153
154// Define some type aliases to be able to more easily refer to more conveniently
155// refer to different flavors of Resolved Feedback Prescriptions
158// the following case is mostly experimental
160
161/* Overwrite the stencil region with average density specified by `overwrite_average`
162 *
163 * This overwrites each component of the momentum density with the average value and holding the
164 * thermal-energy density constant.
165 *
166 * \note
167 * We were a little torn about how to handle the momentum. There were essentially 3 options when
168 * overwriting the density:
169 * 1. Holding the velocity constant at each location.
170 * - Pro: If you had a bunch of cells with common velocity (e.g. because all of the gas rotates
171 * in a disk) but had a varying density, this would ensure that the gas remains comoving.
172 * - Con: Imagine that you had a bunch of cells that varied in density and velocity and you
173 * had 1 cell with a particularly high velocity, but below-average density. This approach
174 * would give the gas in that cell a lot of additional inertia, which could cause problems
175 * 2. Holding the momentum constant at each location
176 * - Pro: You would ALWAYS avoid converting a very fast-moving underdense cell into a
177 * fast-moving average-density cell (This avoid the CON of holding velocity constant)
178 * - Con: In the case where all cells are co-rotating around the disk, but have differing
179 * densities, the initially under-dense (over-dense) cells would move slower (faster) after
180 * the overwrite operation. (This loses the PRO of holding velocity constant)
181 * 3. Overwriting the momentum of each cell with the average momentum
182 * - Pro: this has the advantages and none of the disadvantages of the other options
183 * - Con: this involves more averaging (Ideally, we wouldn't average any field)
184 * Since we are already overwriting the density with the average value anyways, we decided that it
185 * made the most sense to adopt option #3
186 */
187template <typename Stencil>
188__device__ void Overwrite_Average(hydro_utilities::VectorXYZ<Real> stencil_pos_indU, int nx_g, int ny_g, int nz_g,
189 Real* conserved_device, Real overwrite_density)
190{
191 // step 1: load in the relevant fields
192 // - Note: even if we were holding the momentum density constant in each cell, we would need still
193 // need to modify total energy to reflect changes in kinetic energy density
194 const int n_cells = nx_g * ny_g * nz_g;
195 Real* density = conserved_device;
196 Real* momentum_x = &conserved_device[n_cells * grid_enum::momentum_x];
197 Real* momentum_y = &conserved_device[n_cells * grid_enum::momentum_y];
198 Real* momentum_z = &conserved_device[n_cells * grid_enum::momentum_z];
199 Real* energy = &conserved_device[n_cells * grid_enum::Energy];
200
201 // step 2: determine the average momentum in each cell
202 // - Note: we use overwrite_density as the average density since the caller already needed to
203 // compute that value anyways
205 {
206 Real tot_momentum[3] = {0.0, 0.0, 0.0};
207 int num = 0;
208 Stencil::for_each_overlap_zone(stencil_pos_indU, nx_g, ny_g, [&](int idx3D) {
209 tot_momentum[0] += momentum_x[idx3D];
210 tot_momentum[1] += momentum_y[idx3D];
211 tot_momentum[2] += momentum_z[idx3D];
212 num++;
213 });
214 avg_momentum =
215 hydro_utilities::VectorXYZ<Real>{tot_momentum[0] / num, tot_momentum[1] / num, tot_momentum[2] / num};
216 }
217
218 // step 3: Actually overwrite the fields
219 const Real new_ke_density =
220 0.5 *
221 (avg_momentum[0] * avg_momentum[0] + avg_momentum[1] * avg_momentum[1] + avg_momentum[2] * avg_momentum[2]) /
222 overwrite_density;
223 Stencil::for_each_overlap_zone(stencil_pos_indU, nx_g, ny_g, [=](int idx3D) {
224 // precompute 1/initial_density (take care to avoid divide by 0)
225 const Real inv_initial_dens = 1.0 / (density[idx3D] + TINY_NUMBER * (density[idx3D] == 0.0));
226 const Real intial_ke_density = 0.5 * inv_initial_dens *
227 (momentum_x[idx3D] * momentum_x[idx3D] + momentum_y[idx3D] * momentum_y[idx3D] +
228 momentum_z[idx3D] * momentum_z[idx3D]);
229 density[idx3D] = overwrite_density;
230 momentum_x[idx3D] = avg_momentum[0];
231 momentum_y[idx3D] = avg_momentum[1];
232 momentum_z[idx3D] = avg_momentum[2];
233 energy[idx3D] += new_ke_density - intial_ke_density;
234 });
235}
236
237/* \brief Function used for depositing energy or momentum from an unresolved
238 * supernova or from a stellar wind
239 *
240 * \note
241 * Previously there were 2 separate functions defined to perform this operation.
242 * They were functionally the same. They only differences were the names of
243 * variables.
244 *
245 * \par
246 * There are currently issues with the internals of this function:
247 * - this requires the codebase to be compiled with the dual energy formalism
248 * - momentum and total energy are not updated self-consistently
249 */
250template <typename Stencil>
251inline __device__ void Apply_Energy_Momentum_Deposition(Real pos_x_indU, Real pos_y_indU, Real pos_z_indU, Real vel_x,
252 Real vel_y, Real vel_z, int nx_g, int ny_g, int n_ghost,
253 int n_cells, Real* conserved_device, Real feedback_density,
254 Real feedback_momentum, Real feedback_energy)
255{
256 Real* density = conserved_device;
257 Real* momentum_x = &conserved_device[n_cells * grid_enum::momentum_x];
258 Real* momentum_y = &conserved_device[n_cells * grid_enum::momentum_y];
259 Real* momentum_z = &conserved_device[n_cells * grid_enum::momentum_z];
260 Real* energy = &conserved_device[n_cells * grid_enum::Energy];
261#ifdef DE
262 Real* gas_energy = &conserved_device[n_cells * grid_enum::GasEnergy];
263#endif
264
265 Stencil::for_each_vecflavor(
266 {pos_x_indU, pos_y_indU, pos_z_indU}, nx_g, ny_g,
267 [=](Real scalar_weight, hydro_utilities::VectorXYZ<Real> momentum_weights, int idx3D) {
268 // precompute 1/initial_density (take care to avoid divide by 0)
269 const Real inv_initial_density = 1.0 / (density[idx3D] + TINY_NUMBER * (density[idx3D] == 0.0));
270
271 // Step 1: substract off the kinetic-energy-density from total energy density.
272 // - Regardles of whether we inject thermal energy, the kinetic energy density will change to
273 // some degree because the gas density and gas momentum will be changed
274
275 const Real intial_ke_density = 0.5 * inv_initial_density *
276 (momentum_x[idx3D] * momentum_x[idx3D] + momentum_y[idx3D] * momentum_y[idx3D] +
277 momentum_z[idx3D] * momentum_z[idx3D]);
278 energy[idx3D] -= intial_ke_density;
279
280 // Step 2: convert the gas's momentum density to its value in the particle's reference frame
281 // - This must be done after subtracting off KE
282 // - This could probably be written more concisely (momentum_x[idx3D] -= density[idx3D] * vel_x),
283 // but before we do that, we should leave the 3 lines of algebra used to derive that in the
284 // comments since the abbreviated form "looks wrong" at a quick glance
285 {
286 // compute the local velocity
287 Real gas_vx = inv_initial_density * momentum_x[idx3D];
288 Real gas_vy = inv_initial_density * momentum_y[idx3D];
289 Real gas_vz = inv_initial_density * momentum_z[idx3D];
290
291 // adjust the velocity so its in the new frame
292 gas_vx -= vel_x;
293 gas_vy -= vel_y;
294 gas_vz -= vel_z;
295
296 // update the momentum
297 momentum_x[idx3D] = density[idx3D] * gas_vx;
298 momentum_y[idx3D] = density[idx3D] * gas_vy;
299 momentum_z[idx3D] = density[idx3D] * gas_vz;
300 }
301
302 // step 3a: inject density, and momentum
303 density[idx3D] += scalar_weight * feedback_density;
304 momentum_x[idx3D] += momentum_weights[0] * feedback_momentum;
305 momentum_y[idx3D] += momentum_weights[1] * feedback_momentum;
306 momentum_z[idx3D] += momentum_weights[2] * feedback_momentum;
307
308 // Step 3b: inject any thermal energy
309 // - Note: its weird to be inject a fixed amount of thermal energy and momentum. This means we are
310 // injecting a variable amount of total energy...
311
312 energy[idx3D] += scalar_weight * feedback_energy;
313#ifdef DE
314 gas_energy[idx3D] += scalar_weight * feedback_energy;
315#endif
316
317 // precompute 1/final_density (take care to avoid divide by 0)
318 const Real inv_final_density = 1.0 / (density[idx3D] + TINY_NUMBER * (density[idx3D] == 0.0));
319
320 // Step 4: convert the momentum back to the starting reference frame.
321 // - again, this could certainly be done more concisely
322 {
323 // compute the local velocity
324 Real gas_vx = inv_final_density * momentum_x[idx3D];
325 Real gas_vy = inv_final_density * momentum_y[idx3D];
326 Real gas_vz = inv_final_density * momentum_z[idx3D];
327
328 // adjust the velocity that it's in the original frame (it's no longer in the particle's frame)
329 gas_vx += vel_x;
330 gas_vy += vel_y;
331 gas_vz += vel_z;
332
333 // update the momentum
334 momentum_x[idx3D] = density[idx3D] * gas_vx;
335 momentum_y[idx3D] = density[idx3D] * gas_vy;
336 momentum_z[idx3D] = density[idx3D] * gas_vz;
337 }
338
339 // Step 5: add the new kinetic energy density to the total_energy density field
340 // - currently the total_energy density field just holds the non-kinetic energy density
341 // - this needs to happen after changing reference frames (since KE is reference frame dependent)
342 energy[idx3D] += 0.5 * inv_final_density *
343 (momentum_x[idx3D] * momentum_x[idx3D] + momentum_y[idx3D] * momentum_y[idx3D] +
344 momentum_z[idx3D] * momentum_z[idx3D]);
345 });
346}
347
348/* Legacy SNe prescription that combines resolved and unresolved */
349template <typename ResolvedPrescriptionT, typename UnresolvedStencil>
351 /* the following 2 attributes exists for testing purposes */
352 static constexpr bool has_resolved_prescription = true;
353 static constexpr bool has_unresolved_prescription = true;
354
355 static __device__ hydro_utilities::VectorXYZ<Real> nearest_noGhostOverlap_pos(
356 hydro_utilities::VectorXYZ<Real> pos_indU, int ng_x, int ng_y, int ng_z, int n_ghost)
357 {
358 // for right now, we are assuming that the stencil of the unresolved feedback is the same size or
359 // bigger than the stencil used for the resolved feedback
360 return UnresolvedStencil::nearest_noGhostOverlap_pos(pos_indU, ng_x, ng_y, ng_z, n_ghost);
361 }
362
363 // ToDo: refactor to make use of UnresolvedStencil::for_each_overlap_zone
364 template <typename Function>
365 static __device__ void for_each_possible_overlap(Real pos_x_indU, Real pos_y_indU, Real pos_z_indU, int nx_g,
366 int ny_g, Function&& f)
367 {
368 UnresolvedStencil::for_each(hydro_utilities::VectorXYZ<Real>{pos_x_indU, pos_y_indU, pos_z_indU}, nx_g, ny_g,
369 std::forward<Function>(f));
370 }
371
372 static __device__ void apply_feedback(Real pos_x_indU, Real pos_y_indU, Real pos_z_indU, Real vel_x, Real vel_y,
373 Real vel_z, Real age, Real& mass_ref, part_int_t particle_id, Real dx, Real dy,
374 Real dz, int nx_g, int ny_g, int nz_g, int n_ghost, int num_SN, int cycle_num,
375 Real* s_info, Real* conserved_dev)
376 {
377 int tid = threadIdx.x;
378
379 Real dV = dx * dy * dz;
380 int n_cells = nx_g * ny_g * nz_g;
381
382 hydro_utilities::VectorXYZ<Real> pos_indU{pos_x_indU, pos_y_indU, pos_z_indU};
383
384 Real* density = conserved_dev;
385
386 // compute the average mass density
387 Real avg_mass_dens;
388 {
389 Real dtot = 0.0;
390 int num = 0;
391 UnresolvedStencil::for_each_overlap_zone(pos_indU, nx_g, ny_g, [&dtot, &num, density](int idx3) {
392 dtot += density[idx3];
393 num++;
394 });
395 avg_mass_dens = dtot / num;
396 }
397 Real n_0_cgs = avg_mass_dens * DENSITY_UNIT / (MU * MP); // average number density in cgs
398
399 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::countSN] += num_SN;
400
401 Real shell_radius = radius_shell_formation_kpc(n_0_cgs, num_SN);
402
403 const bool is_resolved = (3 * max(dx, max(dy, dz)) <= shell_radius);
404
405 mass_ref = max(0.0, mass_ref - num_SN * feedback::MASS_PER_SN); // update the cluster mass
406 Real feedback_density = num_SN * feedback::MASS_PER_SN / dV;
407
408 log_fb(cycle_num, num_SN, is_resolved, pos_x_indU, pos_y_indU, pos_z_indU, vel_x, vel_y, vel_z, particle_id,
409 n_0_cgs);
410
411 if (is_resolved) {
412 // inject energy and density
413 Real feedback_energy = num_SN * feedback::ENERGY_PER_SN / dV;
414
415 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::countResolved] += num_SN;
416 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalEnergy] += feedback_energy * dV;
417
418 ResolvedPrescriptionT::apply(pos_indU, vel_x, vel_y, vel_z, nx_g, ny_g, n_cells, conserved_dev, feedback_density,
419 feedback_energy);
420 } else {
421 // only unresolved SN feedback involves averaging the densities.
422 // -> we decided that if we are averaging the densities, it probably also
423 // makes sense to average the momentum
424 Overwrite_Average<UnresolvedStencil>(pos_indU, nx_g, ny_g, nz_g, conserved_dev, avg_mass_dens);
425
426 // inject momentum and density
427
428 // the calculation of momentum was inherited from Orlando's older implementation
429 // -> `feedback::FINAL_MOMENTUM * pow(n_0_cgs, -0.17)` comes directly from eqn 34 of Kim &
430 // Ostriker (2015). This is equation is also cited in Kim & Ostriker (2017) -- the TIGRESS
431 // paper.
432 // -> the factor of pow(Real(num_SN), 0.93) has a less clear origin
433 // - my speculation is that it comes from the E_{51}^{0.93} term in equation 17 of Kim &
434 // Ostriker (2015).
435 // - Note: in that other equation, the coefficient is slightly different AND n_0_cgs's
436 // exponent is also slightly different
437 // - It's not clear to me whether we should include this term.
438 // -> Earlier versions of the code divided by sqrt(3).
439 // - I didn't totally understand this, but I'm confident that this is due to the fact that
440 // we weren't explicitly normalizing the momentum based on the full normalized stencil
441 // (effectively momentum normalization was computed a prior). Now we do normalize by the
442 // the total magnitude, so it's definitely unnecessary!
443 // - if we ever want to reintroduce this extra factor of sqrt(3), based on the current
444 // organization of the code, we should now do it in the stencil.
445 Real feedback_momentum = feedback::FINAL_MOMENTUM * pow(n_0_cgs, -0.17) * pow(Real(num_SN), 0.93);
446 Real feedback_momentum_density = feedback_momentum / dV;
447 Real feedback_energy = 0.0; // for now, don't inject any energy
448
449 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::countUnresolved] += num_SN;
450 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalMomentum] += feedback_momentum;
451 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalUnresEnergy] += feedback_energy * dV;
452 Apply_Energy_Momentum_Deposition<UnresolvedStencil>(pos_x_indU, pos_y_indU, pos_z_indU, vel_x, vel_y, vel_z, nx_g,
453 ny_g, n_ghost, n_cells, conserved_dev, feedback_density,
454 feedback_momentum_density, feedback_energy);
455 }
456 }
457};
458
459// the next line defines a type-aliases to make it easier to refer to different hybrid prescriptions
460// - this uses Orlando's unresolved feedback depostion
463
464// the next line defines a shorthand for describing a prescription that is mostly used for testing purposes
465// - in this case, we adopt a slightly different strategy for momentum-feedback. We still use 27 cells,
466// but I think some of the choices make more sense (and are easier to understand). With that said, testing
467// doesn't seem to reveal much of a difference from the prior case. So we primarily stick with the prior case
468// (since we used it to run simulations already)
471
472// the following code is left over from a much earlier version:
473
474/*
475inline __device__ void Wind_Feedback(Real pos_x, Real pos_y, Real pos_z, Real age, Real& mass_ref, part_int_t
476particle_id, Real xMin, Real yMin, Real zMin, Real xMax, Real yMax, Real zMax, Real dx, Real dy, Real dz, int nx_g, int
477ny_g, int nz_g, int n_ghost, int n_step, Real t, Real dt, const feedback::SWRateCalc sw_calc, Real* s_info, Real*
478conserved_dev, Real gamma, int indx_x, int indx_y, int indx_z)
479{
480 int tid = threadIdx.x;
481
482 Real dV = dx * dy * dz;
483 int n_cells = nx_g * ny_g * nz_g;
484
485 if ((age < 0) or not sw_calc.is_active(age)) return;
486 Real feedback_momentum = sw_calc.Get_Wind_Flux(age);
487 // no sense in proceeding if there is no feedback.
488 if (feedback_momentum == 0) return;
489 Real feedback_energy = sw_calc.Get_Wind_Power(age);
490 Real feedback_density = sw_calc.Get_Wind_Mass(feedback_momentum, feedback_energy);
491
492 // feedback_momentum now becomes momentum component along one direction.
493 feedback_momentum *= mass_ref * dt / dV / sqrt(3.0);
494 feedback_density *= mass_ref * dt / dV;
495 feedback_energy *= mass_ref * dt / dV;
496
497 mass_ref -= feedback_density * dV;
498
499 // we log net momentum, not momentum density, and magnitude (not the
500 // component along a direction)
501 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalWindMomentum] += feedback_momentum * dV * sqrt(3.0);
502 s_info[FBInfoLUT::LEN * tid + FBInfoLUT::totalWindEnergy] += feedback_energy * dV;
503
504
505 const double pos_x_indU = (pos_x - xMin) / dx + n_ghost;
506 const double pos_y_indU = (pos_y - yMin) / dy + n_ghost;
507 const double pos_z_indU = (pos_z - zMin) / dz + n_ghost;
508
509 Apply_Energy_Momentum_Deposition(pos_x_indU, pos_y_indU, pos_z_indU, nx_g, ny_g, n_ghost,
510 n_cells, conserved_dev, feedback_density,
511 feedback_momentum, feedback_energy);
512}
513*/
514
515} // namespace fb_prescription
Definition prescription.h:350
Definition prescription.h:52
A data only struct that acts as a simple 3 element vector.
Definition basic_structs.h:32