Skip to content

Commit d0d151f

Browse files
Kinetic energy fraction (#329)
* Coded a function to compute the efficiency factor in Hydrodynamics * Coded it for the template model as well * Made a test for the efficiency factor * Replaced simps by simpson * Try something to make the tests work * added a test for the efficiency factor in the 2-step * Added the formula in the docstring --------- Co-authored-by: Jorinde van de Vis <jorindevandevis@outlook.com>
1 parent 5ddb8d1 commit d0d151f

4 files changed

+200
-19
lines changed

src/WallGo/hydrodynamics.py

+91-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import numpy as np
88
import numpy.typing as npt
99
from scipy.optimize import root_scalar, root, minimize_scalar
10-
from scipy.integrate import solve_ivp
10+
from scipy.integrate import solve_ivp, simpson
1111
from .exceptions import WallGoError
1212
from .thermodynamics import Thermodynamics
1313
from .hydrodynamicsTemplateModel import HydrodynamicsTemplateModel
@@ -484,7 +484,7 @@ def matching(
484484
return vp, vm, Tp, Tm
485485

486486
def shockDE(
487-
self, v: float, xiAndT: np.ndarray
487+
self, v: float, xiAndT: np.ndarray, shockWave: bool=True
488488
) -> Tuple[npt.ArrayLike, npt.ArrayLike]:
489489
r"""
490490
Hydrodynamic equations for the self-similar coordinate :math:`\xi = r/t` and
@@ -498,6 +498,9 @@ def shockDE(
498498
xiAndT : array
499499
Values of the self-similar coordinate :math:`\xi = r/t` and
500500
the temperature :math:`T`
501+
shockWave : bool, optional
502+
If True, the integration happens in the shock wave. If False, it happens in
503+
the rarefaction wave. Default is True.
501504
502505
Returns
503506
-------
@@ -515,20 +518,19 @@ def shockDE(
515518
{"v": v, "xi": xi, "T": T},
516519
)
517520

521+
if shockWave:
522+
csq = self.thermodynamics.csqHighT(T)
523+
else:
524+
csq = self.thermodynamics.csqLowT(T)
518525
eq1 = (
519526
gammaSq(v)
520527
* (1.0 - v * xi)
521-
* (boostVelocity(xi, v) ** 2 / self.thermodynamics.csqHighT(T) - 1.0)
528+
* (boostVelocity(xi, v) ** 2 / csq - 1.0)
522529
* xi
523530
/ 2.0
524531
/ v
525532
)
526-
eq2 = (
527-
self.thermodynamics.wHighT(T)
528-
/ self.thermodynamics.dpHighT(T)
529-
* gammaSq(v)
530-
* boostVelocity(xi, v)
531-
)
533+
eq2 = T * gammaSq(v) * boostVelocity(xi, v)
532534
return [eq1, eq2]
533535

534536
def solveHydroShock(self, vw: float, vp: float, Tp: float) -> float:
@@ -558,7 +560,7 @@ def shock(v: float, xiAndT: np.ndarray | list) -> float:
558560
xi, T = xiAndT
559561
return float(boostVelocity(xi, v) * xi - self.thermodynamics.csqHighT(T))
560562

561-
shock.terminal = True # What's happening here?
563+
shock.terminal = True
562564
xi0T0 = [vw, Tp]
563565
vpcent = boostVelocity(vw, vp)
564566
if shock(vpcent, xi0T0) > 0:
@@ -903,6 +905,85 @@ def shock(
903905
)
904906
return float(sol.root)
905907

908+
def efficiencyFactor(self, vw: float) -> float:
909+
r"""
910+
Computes the efficiency factor
911+
:math:`\kappa=\frac{4}{v_w^3 \alpha_n w_n}\int d\xi \xi^2 v^2\gamma^2 w`.
912+
913+
Parameters
914+
----------
915+
vw : float
916+
Wall velocity.
917+
918+
Returns
919+
-------
920+
float
921+
Value of the efficiency factor :math:`\kappa`.
922+
923+
"""
924+
# Separates the efficiency factor into a contribution from the shock wave and
925+
# the rarefaction wave.
926+
kappaSW = 0.0
927+
kappaRW = 0.0
928+
929+
vp, vm, Tp, Tm = self.findMatching(vw)
930+
931+
# If deflagration or hybrid, computes the shock wave contribution
932+
if vw < self.vJ:
933+
def shock(v: float, xiAndT: np.ndarray | list) -> float:
934+
xi, T = xiAndT
935+
return float(boostVelocity(xi, v)*xi - self.thermodynamics.csqHighT(T))
936+
937+
shock.terminal = True
938+
xi0T0 = [vw, Tp]
939+
vpcent = boostVelocity(vw, vp)
940+
if shock(vpcent, xi0T0) < 0 and vw != vp:
941+
# Integrate the shock wave
942+
solShock = solve_ivp(
943+
self.shockDE,
944+
[vpcent, 1e-10],
945+
xi0T0,
946+
events=shock,
947+
rtol=self.rtol,
948+
atol=0,
949+
) # solve differential equation all the way from v = v+ to v = 0
950+
vPlasma = solShock.t
951+
xi = solShock.y[0]
952+
T = solShock.y[1]
953+
enthalpy = np.array([self.thermodynamics.wHighT(t) for t in T])
954+
955+
# Integrate the solution to get kappa
956+
kappaSW = 4 * simpson(
957+
y=xi**2*vPlasma**2*gammaSq(vPlasma)*enthalpy,
958+
x=xi
959+
) / (vw**3*self.thermodynamics.wHighT(self.Tnucl)*self.template.alN)
960+
961+
# If hybrid or detonation, computes the rarefaction wave contribution
962+
if vw**2 > self.thermodynamics.csqLowT(Tm):
963+
xi0T0 = [vw, Tm]
964+
vmcent = boostVelocity(vw, vm)
965+
# Integrate the rarefaction wave
966+
solRarefaction = solve_ivp(
967+
self.shockDE,
968+
[vmcent, 1e-10],
969+
xi0T0,
970+
rtol=self.rtol,
971+
atol=0,
972+
args=(False,)
973+
) # solve differential equation all the way from v = v- to v = 0
974+
vPlasma = solRarefaction.t
975+
xi = solRarefaction.y[0]
976+
T = solRarefaction.y[1]
977+
enthalpy = np.array([self.thermodynamics.wLowT(t) for t in T])
978+
979+
# Integrate the solution to get kappa
980+
kappaRW = -4 * simpson(
981+
y=xi**2*vPlasma**2*gammaSq(vPlasma)*enthalpy,
982+
x=xi
983+
) / (vw**3*self.thermodynamics.wHighT(self.Tnucl)*self.template.alN)
984+
985+
return kappaSW + kappaRW
986+
906987
def _mappingT(self, TpTm: list[float]) -> list[float]:
907988
"""
908989
Maps the variables Tp and Tm, which are constrained to

src/WallGo/hydrodynamicsTemplateModel.py

+76-9
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import warnings
77
import logging
88
import numpy as np
9-
from scipy.integrate import solve_ivp
9+
from scipy.integrate import solve_ivp, simpson
1010
from scipy.optimize import root_scalar, minimize_scalar, OptimizeResult
1111
from .exceptions import WallGoError
12-
from .helpers import gammaSq
12+
from .helpers import gammaSq, boostVelocity
1313
from .thermodynamics import Thermodynamics
1414

1515

@@ -328,23 +328,31 @@ def solveAlpha(self, vw: float, constraint: bool = True) -> float:
328328
except ValueError as exc:
329329
raise WallGoError("alpha can not be found", data={"vw": vw}) from exc
330330

331-
def _dxiAndWdv(self, v: float, xiAndW: np.ndarray) -> np.ndarray:
331+
def _dxiAndWdv(
332+
self, v: float, xiAndW: np.ndarray, shockWave: bool=True
333+
) -> np.ndarray:
332334
"""
333335
Fluid equations in the shock wave as a function of v.
334336
"""
335337
xi, w = xiAndW
338+
if shockWave:
339+
csq = self.cs2
340+
else:
341+
csq = self.cb2
336342
muXiV = (xi - v) / (1 - xi * v)
337-
dwdv = w * (1 + 1 / self.cs2) * muXiV / (1 - v**2)
343+
dwdv = w * (1 + 1 / csq) * muXiV / (1 - v**2)
338344
if v != 0:
339345
dxidv = (
340-
xi * (1 - v * xi) * (muXiV**2 / self.cs2 - 1) / (2 * v * (1 - v**2))
346+
xi * (1 - v * xi) * (muXiV**2 / csq - 1) / (2 * v * (1 - v**2))
341347
)
342348
else:
343349
# If v = 0, dxidv is set to a very high value
344350
dxidv = 1e50
345351
return np.array([dxidv, dwdv])
346352

347-
def integratePlasma(self, v0: float, vw: float, wp: float) -> OptimizeResult:
353+
def integratePlasma(
354+
self, v0: float, vw: float, wp: float, shockWave: bool=True
355+
) -> OptimizeResult:
348356
"""
349357
Integrates the fluid equations in the shock wave until it reaches the shock
350358
front.
@@ -358,6 +366,9 @@ def integratePlasma(self, v0: float, vw: float, wp: float) -> OptimizeResult:
358366
Wall velocity.
359367
wp : float
360368
Enthalpy just in front of the wall.
369+
shockWave : bool, optional
370+
If True, the integration happens in the shock wave. If False, it happens in
371+
the rarefaction wave. Default is True.
361372
362373
Returns
363374
-------
@@ -366,15 +377,16 @@ def integratePlasma(self, v0: float, vw: float, wp: float) -> OptimizeResult:
366377
367378
"""
368379

369-
def event(v: float, xiAndW: np.ndarray) -> float:
380+
def event(v: float, xiAndW: np.ndarray, shockWave: bool=True) -> float:
370381
# Function that is 0 at the shock wave front. Is used by solve_ivp to stop
371382
# the integration
372383
xi = xiAndW[0]
373384
return float((xi * (xi - v) / (1 - xi * v) - self.cs2) * v)
374385

375-
event.terminal = True
386+
event.terminal = shockWave
376387
sol = solve_ivp(
377-
self._dxiAndWdv, (v0, 1e-10), [vw, wp], events=event, rtol=self.rtol, atol=0
388+
self._dxiAndWdv, (v0, 1e-10), [vw, wp],
389+
events=event, rtol=self.rtol/10, atol=0, args=(shockWave,)
378390
)
379391
return sol
380392

@@ -673,3 +685,58 @@ def detonationVAndT(self, vw: float) -> tuple[float, ...]:
673685
vm = (part + np.sqrt(part**2 - 4 * self.cb2 * vp**2)) / (2 * vp)
674686
Tm = self._findTm(vm, vp, self.Tnucl)
675687
return vp, vm, self.Tnucl, Tm
688+
689+
def efficiencyFactor(self, vw: float) -> float:
690+
r"""
691+
Computes the efficiency factor
692+
:math:`\kappa=\frac{4}{v_w^3 \alpha_n w_n}\int d\xi \xi^2 v^2\gamma^2 w`..
693+
694+
Parameters
695+
----------
696+
vw : float
697+
Wall velocity.
698+
699+
Returns
700+
-------
701+
float
702+
Value of the efficiency factor :math:`\kappa`.
703+
704+
"""
705+
# Separates the efficiency factor into a contribution from the shock wave and
706+
# the rarefaction wave.
707+
kappaSW = 0.0
708+
kappaRW = 0.0
709+
710+
vp, vm, Tp, Tm = self.findMatching(vw)
711+
712+
# Computes the enthalpies (in units where w_s(Tn)=1)
713+
wp = (Tp/self.Tnucl)**self.mu
714+
wm = gammaSq(vp)*vp*wp/(gammaSq(vm)*vm)
715+
716+
# If deflagration or hybrid, computes the shock wave contribution
717+
if vw < self.vJ:
718+
solShock = self.integratePlasma(boostVelocity(vw, vp), vw, wp)
719+
vPlasma = solShock.t
720+
xi = solShock.y[0]
721+
enthalpy = solShock.y[1]
722+
723+
# Integrate the solution to get kappa
724+
kappaSW = 4 * simpson(
725+
y=xi**2*vPlasma**2*gammaSq(vPlasma)*enthalpy,
726+
x=xi
727+
) / (vw**3 * self.alN)
728+
729+
# If hybrid or detonation, computes the rarefaction wave contribution
730+
if vw > self.cb:
731+
solRarefaction = self.integratePlasma(boostVelocity(vw, vm), vw, wm, False)
732+
vPlasma = solRarefaction.t
733+
xi = solRarefaction.y[0]
734+
enthalpy = solRarefaction.y[1]
735+
736+
# Integrate the solution to get kappa
737+
kappaRW = -4 * simpson(
738+
y=xi**2*vPlasma**2*gammaSq(vPlasma)*enthalpy,
739+
x=xi
740+
) / (vw**3 * self.alN)
741+
742+
return kappaSW + kappaRW

tests/test_HydroTemplateModel.py

+16
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ def test_findvwLTE():
118118
res2[i] = hydroTemplate.findvwLTE()
119119
np.testing.assert_allclose(res1,res2,rtol = 10**-4,atol = 0)
120120

121+
def test_efficiencyFactor():
122+
res1,res2 = np.zeros(N),np.zeros(N)
123+
psiN = 1-0.5*rng.random(N)
124+
alN = (1-psiN)/3+10**(-3*rng.random(N)-0.5)
125+
cs2 = 1/4+(1/3-1/4)*rng.random(N)
126+
cb2 = cs2-(1/3-1/4)*rng.random(N)
127+
for i in range(N):
128+
model = TestModelTemplate(alN[i],psiN[i],cb2[i],cs2[i],1,1)
129+
hydrodynamics = WallGo.Hydrodynamics(model,tmax,tmin,1e-8,1e-8)
130+
hydroTemplate = WallGo.HydrodynamicsTemplateModel(model)
131+
vMin = max(hydroTemplate.vMin, hydrodynamics.vMin)
132+
vw = vMin + (1-vMin)*rng.random()
133+
res1[i] = hydrodynamics.efficiencyFactor(vw)
134+
res2[i] = hydroTemplate.efficiencyFactor(vw)
135+
np.testing.assert_allclose(res1,res2,rtol = 10**-2,atol = 0)
136+
121137
def test_findHydroBoundaries():
122138
res1,res2 = np.zeros((N,5)),np.zeros((N,5))
123139
psiN = 1-0.5*rng.random(N)

tests/test_Hydrodynamics.py

+17
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,23 @@ def test_findMatching():
218218
res = hydrodynamics.findMatching(0.9)
219219
np.testing.assert_allclose(res,(0.9, 0.894957,0.9,0.918446),rtol = 10**-2,atol = 0)
220220

221+
222+
# Test efficiency factor in two-step model
223+
def test_efficiencyFactor():
224+
model1 = TestModel2Step(0.2,0.1,0.4,0.7)
225+
hydrodynamics = WallGo.Hydrodynamics(model1, tmax, tmin, rtol, atol)
226+
res = hydrodynamics.efficiencyFactor(0.4)
227+
assert res == pytest.approx(0.140972, rel = 0.01)
228+
res = hydrodynamics.efficiencyFactor(0.6)
229+
assert res == pytest.approx(0.334666, rel = 0.01)
230+
model1 = TestModel2Step(0.2,0.1,0.4,0.9)
231+
hydrodynamics = WallGo.Hydrodynamics(model1, tmax, tmin, rtol, atol)
232+
res = hydrodynamics.efficiencyFactor(0.4)
233+
assert res == pytest.approx(0.0399354, rel = 0.01)
234+
res = hydrodynamics.efficiencyFactor(0.6)
235+
assert res == pytest.approx(0.197849, rel = 0.01)
236+
237+
221238
### Test local thermal equilibrium solution in bag model
222239

223240

0 commit comments

Comments
 (0)