22
22
23
23
using namespace nse_rp ;
24
24
25
+ template <typename T>
26
+ struct nse_solver_data
27
+ {
28
+ T state;
29
+ amrex::Array1D<amrex::Real, 1 , NumSpec> u_c;
30
+ };
31
+
32
+
25
33
template <typename T>
26
34
AMREX_GPU_HOST_DEVICE AMREX_INLINE
27
35
T get_nonexponent_nse_state (const T& state) {
@@ -42,7 +50,7 @@ T get_nonexponent_nse_state(const T& state) {
42
50
// if we are doing drive_initial_convection, we want to use
43
51
// the temperature that comes in through T_fixed
44
52
45
- Real T_in = state.T_fixed > 0 .0_rt ? state.T_fixed : state.T ;
53
+ amrex:: Real T_in = state.T_fixed > 0 .0_rt ? state.T_fixed : state.T ;
46
54
47
55
#ifndef NEW_NETWORK_IMPLEMENTATION
48
56
auto tfactors = evaluate_tfactors (T_in);
@@ -75,56 +83,78 @@ T get_nonexponent_nse_state(const T& state) {
75
83
return nse_state;
76
84
}
77
85
78
-
79
86
template <typename T>
80
87
AMREX_GPU_HOST_DEVICE AMREX_INLINE
81
- void apply_nse_exponent (T& nse_state) {
82
- // This function calculates a portion of the nse_state that dependents
83
- // on the chemical potential.
84
- // The input nse_state should be the result from get_nonexponent_nse_state
85
-
86
- // if we are doing drive_initial_convection, we want to use
87
- // the temperature that comes in through T_fixed
88
-
89
- Real T_in = nse_state.T_fixed > 0 .0_rt ? nse_state.T_fixed : nse_state.T ;
90
- Real exponent;
88
+ void compute_coulomb_contribution (amrex::Array1D<amrex::Real, 1 , NumSpec>& u_c,
89
+ const T& state) {
90
+ // This function computes the coulomb contribution 1D array
91
91
92
92
// if we use chabrier1998 screening
93
93
// Get the required terms to calculate coulomb correction term, u_c
94
94
95
- # if SCREEN_METHOD == SCREEN_METHOD_chabrier1998
95
+ amrex::Real T_in = state. T_fixed > 0 .0_rt ? state. T_fixed : state. T ;
96
96
97
+ //
97
98
// Find n_e for original state;
98
- const amrex::Real n_e = nse_state.rho * nse_state.y_e / C::m_u;
99
- const amrex::Real Gamma_e = C::q_e * C::q_e * std::cbrt (4 .0_rt * M_PI * n_e / 3 .0_rt)
100
- / (C::k_B * T_in);
99
+ // Note that y_e depends on the mass fraction,
100
+ // but we use the coulomb correction to compute the mass fraction
101
+ // So here y_e is simply the actual y_e we want to achieve.
102
+ // so we just treat u_c as a constant.
103
+ //
104
+
105
+ const amrex::Real n_e = state.rho * state.y_e / C::m_u;
106
+ const amrex::Real Gamma_e = C::q_e * C::q_e *
107
+ std::cbrt (4 .0_rt * M_PI * n_e / 3 .0_rt) / (C::k_B * T_in);
101
108
amrex::Real gamma ;
102
- #endif
103
-
104
- amrex::Real u_c = 0 .0_rt;
105
109
106
110
for (int n = 0 ; n < NumSpec; ++n) {
107
111
#ifdef NEW_NETWORK_IMPLEMENTATION
108
112
if (n == NSE_INDEX::H1_index) {
109
- nse_state.xn [n] = 0.0 ;
110
113
continue ;
111
114
}
112
115
#endif
113
116
// term for calculating u_c
114
117
115
- // if use chabrier1998 screening, calculate the coulomb correction term
116
- #if SCREEN_METHOD == SCREEN_METHOD_chabrier1998
117
118
gamma = std::pow (zion[n], 5 .0_rt/3 .0_rt) * Gamma_e;
118
119
119
120
// chemical potential for coulomb correction
120
121
// see appendix of Calder 2007, doi:10.1086/510709 for more detail
121
122
122
123
// reuse existing implementation from screening routine
123
- Real f, df;
124
+ amrex:: Real f, df;
124
125
constexpr int do_T_derivatives = 0 ;
125
126
chabrier1998_helmholtz_F<do_T_derivatives>(gamma , 0 .0_rt, f, df);
126
127
127
- u_c = C::k_B * T_in / C::Legacy::MeV2erg * f;
128
+ //
129
+ // Here u_c is a dimensionless quantity.
130
+ // Otherwise:
131
+ // u_c = C::k_B * T_in / C::Legacy::MeV2erg * f;
132
+ //
133
+
134
+ u_c (n+1 ) = f;
135
+ }
136
+ }
137
+
138
+
139
+ template <typename T>
140
+ AMREX_GPU_HOST_DEVICE AMREX_INLINE
141
+ void apply_nse_exponent (T& nse_state,
142
+ const amrex::Array1D<amrex::Real, 1 , NumSpec>& u_c) {
143
+ // This function applies the nse exponent which depends on
144
+ // the chemical potential.
145
+
146
+ // if we are doing drive_initial_convection, we want to use
147
+ // the temperature that comes in through T_fixed
148
+
149
+ amrex::Real T_in = nse_state.T_fixed > 0 .0_rt ? nse_state.T_fixed : nse_state.T ;
150
+ amrex::Real exponent;
151
+
152
+ for (int n = 0 ; n < NumSpec; ++n) {
153
+ #ifdef NEW_NETWORK_IMPLEMENTATION
154
+ if (n == NSE_INDEX::H1_index) {
155
+ nse_state.xn [n] = 0.0 ;
156
+ continue ;
157
+ }
128
158
#endif
129
159
130
160
// find nse mass frac
@@ -134,8 +164,8 @@ void apply_nse_exponent(T& nse_state) {
134
164
135
165
exponent = amrex::min (500 .0_rt,
136
166
(zion[n] * nse_state.mu_p + (aion[n] - zion[n]) *
137
- nse_state.mu_n - u_c + network::bion (n+1 )) /
138
- C::k_B / T_in * C::Legacy::MeV2erg);
167
+ nse_state.mu_n + network::bion (n+1 )) /
168
+ C::k_B / T_in * C::Legacy::MeV2erg - u_c (n+ 1 ) );
139
169
140
170
nse_state.xn [n] *= std::exp (exponent);
141
171
}
@@ -156,21 +186,21 @@ void apply_nse_exponent(T& nse_state) {
156
186
157
187
// constraint equation
158
188
159
- template <typename T>
189
+ template <typename T>
160
190
AMREX_GPU_HOST_DEVICE AMREX_INLINE
161
191
void fcn (Array1D<Real, 1 , 2 >& x, Array1D<Real, 1 , 2 >& fvec,
162
- const T& state , int & iflag) {
192
+ const nse_solver_data<T>& state_data , int & iflag) {
163
193
// here state is the nse_state from get_nonexponent_nse_state
164
194
165
195
amrex::ignore_unused (iflag);
166
196
167
- T nse_state = state;
197
+ auto nse_state = state_data. state ;
168
198
nse_state.mu_p = x (1 );
169
199
nse_state.mu_n = x (2 );
170
200
171
201
// Apply exponent component for calculating nse mass fractions
172
202
173
- apply_nse_exponent (nse_state);
203
+ apply_nse_exponent (nse_state, state_data. u_c );
174
204
175
205
fvec (1 ) = -1 .0_rt;
176
206
@@ -187,25 +217,25 @@ void fcn(Array1D<Real, 1, 2>& x, Array1D<Real, 1, 2>& fvec,
187
217
188
218
// constraint equation 2, electron fraction should be the same
189
219
190
- fvec (2 ) = nse_state.y_e - state.y_e ;
220
+ fvec (2 ) = nse_state.y_e - state_data. state .y_e ;
191
221
192
222
}
193
223
194
224
// constraint jacobian
195
225
196
- template <typename T>
226
+ template <typename T>
197
227
AMREX_GPU_HOST_DEVICE AMREX_INLINE
198
228
void jcn (Array1D<Real, 1 , 2 >& x, Array2D<Real, 1 , 2 , 1 , 2 >& fjac,
199
- const T& state , int & iflag) {
229
+ const nse_solver_data<T>& state_data , int & iflag) {
200
230
// here state is the nse_state from get_nonexponent_nse_state
201
231
202
232
amrex::ignore_unused (iflag);
203
233
204
- T nse_state = state;
234
+ auto nse_state = state_data. state ;
205
235
nse_state.mu_p = x (1 );
206
236
nse_state.mu_n = x (2 );
207
237
208
- apply_nse_exponent (nse_state);
238
+ apply_nse_exponent (nse_state, state_data. u_c );
209
239
210
240
// evaluate jacobian of the constraint
211
241
@@ -217,7 +247,7 @@ void jcn(Array1D<Real, 1, 2>& x, Array2D<Real, 1, 2, 1, 2>& fjac,
217
247
// if we are doing drive_initial_convection, we want to use
218
248
// the temperature that comes in through T_fixed
219
249
220
- Real T_in = state .T_fixed > 0 .0_rt ? state .T_fixed : state .T ;
250
+ amrex:: Real T_in = nse_state .T_fixed > 0 .0_rt ? nse_state .T_fixed : nse_state .T ;
221
251
222
252
for (int n = 0 ; n < NumSpec; ++n) {
223
253
#ifdef NEW_NETWORK_IMPLEMENTATION
@@ -233,9 +263,10 @@ void jcn(Array1D<Real, 1, 2>& x, Array2D<Real, 1, 2, 1, 2>& fjac,
233
263
234
264
}
235
265
236
- template <typename T>
266
+ template <typename T>
237
267
AMREX_GPU_HOST_DEVICE AMREX_INLINE
238
- void nse_hybrid_solver (T& state, amrex::Real eps=1 .0e-10_rt) {
268
+ void nse_hybrid_solver (nse_solver_data<T>& state_data,
269
+ amrex::Real eps=1 .0e-10_rt) {
239
270
// state is the nse_state from get_nonexponent_nse_state
240
271
241
272
hybrj_t <2 > hj;
@@ -259,8 +290,8 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
259
290
amrex::Array1D<amrex::Real, 1 , 2 > outer_x;
260
291
amrex::Array1D<amrex::Real, 1 , 2 > inner_x;
261
292
262
- outer_x (1 ) = state.mu_p ;
263
- outer_x (2 ) = state.mu_n ;
293
+ outer_x (1 ) = state_data. state .mu_p ;
294
+ outer_x (2 ) = state_data. state .mu_n ;
264
295
265
296
// for (int j = 1; j <= 2; ++j) {
266
297
// hj.diag(j) = 1.0_rt;
@@ -280,14 +311,14 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
280
311
hj.x (2 ) = inner_x (2 );
281
312
282
313
// hybrj<2, T>(hj, state, fcn_hybrid<T>, jcn_hybrid<T>);
283
- hybrj (hj, state );
314
+ hybrj (hj, state_data );
284
315
285
- fcn (hj.x , f, state , flag);
316
+ fcn (hj.x , f, state_data , flag);
286
317
287
318
if (std::abs (f (1 )) < eps && std::abs (f (2 )) < eps) {
288
319
289
- state.mu_p = hj.x (1 );
290
- state.mu_n = hj.x (2 );
320
+ state_data. state .mu_p = hj.x (1 );
321
+ state_data. state .mu_n = hj.x (2 );
291
322
return ;
292
323
}
293
324
@@ -323,10 +354,11 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
323
354
// }
324
355
#ifndef AMREX_USE_GPU
325
356
std::cout << " NSE solver failed with these conditions: " << std::endl;
326
- std::cout << " Temperature: " << state.T << std::endl;
327
- std::cout << " Density: " << state.rho << std::endl;
328
- std::cout << " Ye: " << state.y_e << std::endl;
329
- std::cout << " Initial mu_p and mu_n: " << state.mu_p << " , " << state.mu_n << std::endl;
357
+ std::cout << " Temperature: " << state_data.state .T << std::endl;
358
+ std::cout << " Density: " << state_data.state .rho << std::endl;
359
+ std::cout << " Ye: " << state_data.state .y_e << std::endl;
360
+ std::cout << " Initial mu_p and mu_n: " << state_data.state .mu_p
361
+ << " , " << state_data.state .mu_n << std::endl;
330
362
#endif
331
363
332
364
amrex::Error (" failed to solve" );
@@ -335,10 +367,12 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
335
367
// A newton-raphson solver for finding nse state used for calibrating
336
368
// chemical potential of proton and neutron
337
369
338
- template <typename T>
370
+ template <typename T>
339
371
AMREX_GPU_HOST_DEVICE AMREX_INLINE
340
- void nse_nr_solver (T& state, amrex::Real eps=1 .0e-10_rt) {
341
- // state is the nse_state from get_nonexponent_nse_state
372
+ void nse_nr_solver (nse_solver_data<T>& state_data,
373
+ amrex::Real eps=1 .0e-10_rt) {
374
+ // state_data is the state_data after from
375
+ // get_nonexponent_nse_state and compute_coulomb_contribution
342
376
343
377
// whether nse solver converged or not
344
378
@@ -349,11 +383,11 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
349
383
amrex::Array1D<amrex::Real, 1 , 2 > x;
350
384
int flag = 0 ;
351
385
352
- x (1 ) = state.mu_p ;
353
- x (2 ) = state.mu_n ;
386
+ x (1 ) = state_data. state .mu_p ;
387
+ x (2 ) = state_data. state .mu_n ;
354
388
355
- jcn (x, jac, state , flag);
356
- fcn (x, f, state , flag);
389
+ jcn (x, jac, state_data , flag);
390
+ fcn (x, f, state_data , flag);
357
391
358
392
// store determinant for finding inverse jac
359
393
amrex::Real det;
@@ -373,8 +407,8 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
373
407
if (std::abs (d_mu_p) < eps * std::abs (x (1 )) &&
374
408
std::abs (d_mu_n) < eps * std::abs (x (2 ))) {
375
409
converged = true ;
376
- state.mu_p = x (1 );
377
- state.mu_n = x (2 );
410
+ state_data. state .mu_p = x (1 );
411
+ state_data. state .mu_n = x (2 );
378
412
break ;
379
413
}
380
414
@@ -429,8 +463,8 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
429
463
430
464
// update constraint
431
465
432
- jcn (x, jac, state , flag);
433
- fcn (x, f, state , flag);
466
+ jcn (x, jac, state_data , flag);
467
+ fcn (x, f, state_data , flag);
434
468
}
435
469
436
470
if (!converged) {
@@ -439,7 +473,7 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
439
473
}
440
474
441
475
// Get the NSE state;
442
- template <typename T>
476
+ template <typename T>
443
477
AMREX_GPU_HOST_DEVICE AMREX_INLINE
444
478
T get_actual_nse_state (T& state, amrex::Real eps=1 .0e-10_rt,
445
479
bool input_ye_is_valid=false ) {
@@ -477,15 +511,23 @@ T get_actual_nse_state(T& state, amrex::Real eps=1.0e-10_rt,
477
511
#endif
478
512
}
479
513
514
+ nse_solver_data<T> state_data = {state, {0 .0_rt}};
515
+
480
516
// Get nse_state without the exponent term
481
517
482
- auto nse_state = get_nonexponent_nse_state (state);
518
+ state_data.state = get_nonexponent_nse_state (state);
519
+
520
+ // if use chabrier1998 screening, calculate the coulomb correction term
521
+
522
+ #if SCREEN_METHOD == SCREEN_METHOD_chabrier1998
523
+ compute_coulomb_contribution (state_data.u_c , state);
524
+ #endif
483
525
484
526
// invoke newton-raphson or hybrj to solve chemical potential of proton and neutron
485
527
// which are the exponent part of the nse mass fraction calculation
486
528
487
529
if (use_hybrid_solver) {
488
- nse_hybrid_solver (nse_state , eps);
530
+ nse_hybrid_solver (state_data , eps);
489
531
}
490
532
else {
491
533
bool singular_network = true ;
@@ -504,18 +546,18 @@ T get_actual_nse_state(T& state, amrex::Real eps=1.0e-10_rt,
504
546
amrex::Error (" This network always results in singular jacobian matrix, thus can't find nse mass fraction using nr!" );
505
547
}
506
548
507
- nse_nr_solver (state , eps);
549
+ nse_nr_solver (state_data , eps);
508
550
}
509
551
510
552
// Apply exponent for calculating nse mass fractions
511
553
512
- apply_nse_exponent (nse_state );
554
+ apply_nse_exponent (state_data. state , state_data. u_c );
513
555
514
556
// update mu_n and mu_p to input state
515
557
516
- state.mu_p = nse_state .mu_p ;
517
- state.mu_n = nse_state .mu_n ;
558
+ state.mu_p = state_data. state .mu_p ;
559
+ state.mu_n = state_data. state .mu_n ;
518
560
519
- return nse_state ;
561
+ return state_data. state ;
520
562
}
521
563
#endif
0 commit comments