Skip to content

Commit e0323a1

Browse files
authored
separate coulomb contribution calculation from nse solver (#1508)
Separate out calculation of coulomb contribution from nse solver to make things faster. Even though coulomb contribution depends on electron fraction, y_e, but the y_e we use to evaluate the coulomb contribution does not change during iteration. We just use the input y_e.
1 parent aa0a4ff commit e0323a1

File tree

2 files changed

+189
-147
lines changed

2 files changed

+189
-147
lines changed

nse_solver/nse_solver.H

+109-67
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222

2323
using namespace nse_rp;
2424

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+
2533
template <typename T>
2634
AMREX_GPU_HOST_DEVICE AMREX_INLINE
2735
T get_nonexponent_nse_state(const T& state) {
@@ -42,7 +50,7 @@ T get_nonexponent_nse_state(const T& state) {
4250
// if we are doing drive_initial_convection, we want to use
4351
// the temperature that comes in through T_fixed
4452

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;
4654

4755
#ifndef NEW_NETWORK_IMPLEMENTATION
4856
auto tfactors = evaluate_tfactors(T_in);
@@ -75,56 +83,78 @@ T get_nonexponent_nse_state(const T& state) {
7583
return nse_state;
7684
}
7785

78-
7986
template <typename T>
8087
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
9191

9292
// if we use chabrier1998 screening
9393
// Get the required terms to calculate coulomb correction term, u_c
9494

95-
#if SCREEN_METHOD == SCREEN_METHOD_chabrier1998
95+
amrex::Real T_in = state.T_fixed > 0.0_rt ? state.T_fixed : state.T;
9696

97+
//
9798
// 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);
101108
amrex::Real gamma;
102-
#endif
103-
104-
amrex::Real u_c = 0.0_rt;
105109

106110
for (int n = 0; n < NumSpec; ++n) {
107111
#ifdef NEW_NETWORK_IMPLEMENTATION
108112
if (n == NSE_INDEX::H1_index) {
109-
nse_state.xn[n] = 0.0;
110113
continue;
111114
}
112115
#endif
113116
// term for calculating u_c
114117

115-
// if use chabrier1998 screening, calculate the coulomb correction term
116-
#if SCREEN_METHOD == SCREEN_METHOD_chabrier1998
117118
gamma = std::pow(zion[n], 5.0_rt/3.0_rt) * Gamma_e;
118119

119120
// chemical potential for coulomb correction
120121
// see appendix of Calder 2007, doi:10.1086/510709 for more detail
121122

122123
// reuse existing implementation from screening routine
123-
Real f, df;
124+
amrex::Real f, df;
124125
constexpr int do_T_derivatives = 0;
125126
chabrier1998_helmholtz_F<do_T_derivatives>(gamma, 0.0_rt, f, df);
126127

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+
}
128158
#endif
129159

130160
// find nse mass frac
@@ -134,8 +164,8 @@ void apply_nse_exponent(T& nse_state) {
134164

135165
exponent = amrex::min(500.0_rt,
136166
(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));
139169

140170
nse_state.xn[n] *= std::exp(exponent);
141171
}
@@ -156,21 +186,21 @@ void apply_nse_exponent(T& nse_state) {
156186

157187
// constraint equation
158188

159-
template<typename T>
189+
template <typename T>
160190
AMREX_GPU_HOST_DEVICE AMREX_INLINE
161191
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) {
163193
// here state is the nse_state from get_nonexponent_nse_state
164194

165195
amrex::ignore_unused(iflag);
166196

167-
T nse_state = state;
197+
auto nse_state = state_data.state;
168198
nse_state.mu_p = x(1);
169199
nse_state.mu_n = x(2);
170200

171201
// Apply exponent component for calculating nse mass fractions
172202

173-
apply_nse_exponent(nse_state);
203+
apply_nse_exponent(nse_state, state_data.u_c);
174204

175205
fvec(1) = -1.0_rt;
176206

@@ -187,25 +217,25 @@ void fcn(Array1D<Real, 1, 2>& x, Array1D<Real, 1, 2>& fvec,
187217

188218
// constraint equation 2, electron fraction should be the same
189219

190-
fvec(2) = nse_state.y_e - state.y_e;
220+
fvec(2) = nse_state.y_e - state_data.state.y_e;
191221

192222
}
193223

194224
// constraint jacobian
195225

196-
template<typename T>
226+
template <typename T>
197227
AMREX_GPU_HOST_DEVICE AMREX_INLINE
198228
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) {
200230
// here state is the nse_state from get_nonexponent_nse_state
201231

202232
amrex::ignore_unused(iflag);
203233

204-
T nse_state = state;
234+
auto nse_state = state_data.state;
205235
nse_state.mu_p = x(1);
206236
nse_state.mu_n = x(2);
207237

208-
apply_nse_exponent(nse_state);
238+
apply_nse_exponent(nse_state, state_data.u_c);
209239

210240
// evaluate jacobian of the constraint
211241

@@ -217,7 +247,7 @@ void jcn(Array1D<Real, 1, 2>& x, Array2D<Real, 1, 2, 1, 2>& fjac,
217247
// if we are doing drive_initial_convection, we want to use
218248
// the temperature that comes in through T_fixed
219249

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;
221251

222252
for (int n = 0; n < NumSpec; ++n) {
223253
#ifdef NEW_NETWORK_IMPLEMENTATION
@@ -233,9 +263,10 @@ void jcn(Array1D<Real, 1, 2>& x, Array2D<Real, 1, 2, 1, 2>& fjac,
233263

234264
}
235265

236-
template<typename T>
266+
template <typename T>
237267
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) {
239270
// state is the nse_state from get_nonexponent_nse_state
240271

241272
hybrj_t<2> hj;
@@ -259,8 +290,8 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
259290
amrex::Array1D<amrex::Real, 1, 2> outer_x;
260291
amrex::Array1D<amrex::Real, 1, 2> inner_x;
261292

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;
264295

265296
// for (int j = 1; j <= 2; ++j) {
266297
// hj.diag(j) = 1.0_rt;
@@ -280,14 +311,14 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
280311
hj.x(2) = inner_x(2);
281312

282313
// hybrj<2, T>(hj, state, fcn_hybrid<T>, jcn_hybrid<T>);
283-
hybrj(hj, state);
314+
hybrj(hj, state_data);
284315

285-
fcn(hj.x, f, state, flag);
316+
fcn(hj.x, f, state_data, flag);
286317

287318
if (std::abs(f(1)) < eps && std::abs(f(2)) < eps) {
288319

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);
291322
return;
292323
}
293324

@@ -323,10 +354,11 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
323354
// }
324355
#ifndef AMREX_USE_GPU
325356
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;
330362
#endif
331363

332364
amrex::Error("failed to solve");
@@ -335,10 +367,12 @@ void nse_hybrid_solver(T& state, amrex::Real eps=1.0e-10_rt) {
335367
// A newton-raphson solver for finding nse state used for calibrating
336368
// chemical potential of proton and neutron
337369

338-
template<typename T>
370+
template <typename T>
339371
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
342376

343377
// whether nse solver converged or not
344378

@@ -349,11 +383,11 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
349383
amrex::Array1D<amrex::Real, 1, 2> x;
350384
int flag = 0;
351385

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;
354388

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);
357391

358392
// store determinant for finding inverse jac
359393
amrex::Real det;
@@ -373,8 +407,8 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
373407
if (std::abs(d_mu_p) < eps * std::abs(x(1)) &&
374408
std::abs(d_mu_n) < eps * std::abs(x(2))) {
375409
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);
378412
break;
379413
}
380414

@@ -429,8 +463,8 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
429463

430464
// update constraint
431465

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);
434468
}
435469

436470
if (!converged) {
@@ -439,7 +473,7 @@ void nse_nr_solver(T& state, amrex::Real eps=1.0e-10_rt) {
439473
}
440474

441475
// Get the NSE state;
442-
template<typename T>
476+
template <typename T>
443477
AMREX_GPU_HOST_DEVICE AMREX_INLINE
444478
T get_actual_nse_state(T& state, amrex::Real eps=1.0e-10_rt,
445479
bool input_ye_is_valid=false) {
@@ -477,15 +511,23 @@ T get_actual_nse_state(T& state, amrex::Real eps=1.0e-10_rt,
477511
#endif
478512
}
479513

514+
nse_solver_data<T> state_data = {state, {0.0_rt}};
515+
480516
// Get nse_state without the exponent term
481517

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
483525

484526
// invoke newton-raphson or hybrj to solve chemical potential of proton and neutron
485527
// which are the exponent part of the nse mass fraction calculation
486528

487529
if (use_hybrid_solver) {
488-
nse_hybrid_solver(nse_state, eps);
530+
nse_hybrid_solver(state_data, eps);
489531
}
490532
else {
491533
bool singular_network = true;
@@ -504,18 +546,18 @@ T get_actual_nse_state(T& state, amrex::Real eps=1.0e-10_rt,
504546
amrex::Error("This network always results in singular jacobian matrix, thus can't find nse mass fraction using nr!");
505547
}
506548

507-
nse_nr_solver(state, eps);
549+
nse_nr_solver(state_data, eps);
508550
}
509551

510552
// Apply exponent for calculating nse mass fractions
511553

512-
apply_nse_exponent(nse_state);
554+
apply_nse_exponent(state_data.state, state_data.u_c);
513555

514556
// update mu_n and mu_p to input state
515557

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;
518560

519-
return nse_state;
561+
return state_data.state;
520562
}
521563
#endif

0 commit comments

Comments
 (0)