Skip to content

Commit 4701e9c

Browse files
authored
Merge pull request #6224 from gassmoeller/fix_dynamic_core
Fix dynamic core boundary temperature plugin
2 parents 2ac3b92 + 1665d04 commit 4701e9c

File tree

8 files changed

+198
-40
lines changed

8 files changed

+198
-40
lines changed
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed: The boundary temperature plugin 'dynamic core' used to
2+
crash when the inner core was completely molten or completely
3+
solid. This is fixed now.
4+
<br>
5+
(Francesco Radica, Rene Gassmoeller, 2025/02/06)

source/boundary_temperature/dynamic_core.cc

+68-40
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ namespace aspect
356356
return (r0+r1)/2.;
357357
}
358358

359+
360+
359361
template <int dim>
360362
bool
361363
DynamicCore<dim>::solve_time_step(double &X, double &T, double &R) const
@@ -386,6 +388,8 @@ namespace aspect
386388
double dT1 = get_dT(R_1);
387389
double dT2 = get_dT(R_2);
388390

391+
// If the temperature difference at the core-mantle boundary and at the
392+
// inner-outer core boundary have the same sign, we have a fully molten or fully solid core.
389393
if (dT0 >= 0. && dT2 >= 0.)
390394
{
391395
// Fully molten core
@@ -399,48 +403,64 @@ namespace aspect
399403
dT1 = 0;
400404
}
401405
else
402-
while (!(dT1==0 || steps>max_steps))
403-
{
404-
// If solution is out of the interval, then something is wrong.
405-
if (dT0*dT2>0)
406-
{
407-
this->get_pcout()<<"Step: "<<steps<<std::endl
408-
<<" R=["<<R_0/1e3<<","<<R_2/1e3<<"]"<<"(km)"
409-
<<" dT0="<<dT0<<", dT2="<<dT2<<std::endl
410-
<<"Q_CMB="<<core_data.Q<<std::endl
411-
<<"Warning: Solution for inner core radius can not be found! Mid-point is used."<<std::endl;
412-
AssertThrow(dT0*dT2<=0,ExcMessage("No single solution for inner core!"));
413-
}
414-
else if (dT0*dT1 < 0.)
415-
{
416-
R_2 = R_1;
417-
dT2 = dT1;
418-
}
419-
else if (dT2*dT1 < 0.)
420-
{
421-
R_0 = R_1;
422-
dT0 = dT1;
423-
}
424-
R_1 = (R_0 + R_2) / 2.;
425-
dT1 = get_dT(R_1);
426-
++steps;
427-
}
406+
{
407+
// Use bisection method to find R_1 such that dT1 = 0
408+
while (!(dT1==0 || steps>max_steps))
409+
{
410+
// If solution is out of the interval, then something is wrong.
411+
if (dT0*dT2>0)
412+
{
413+
this->get_pcout()<<"Step: "<<steps<<std::endl
414+
<<" R=["<<R_0/1e3<<","<<R_2/1e3<<"]"<<"(km)"
415+
<<" dT0="<<dT0<<", dT2="<<dT2<<std::endl
416+
<<"Q_CMB="<<core_data.Q<<std::endl
417+
<<"Warning: Solution for inner core radius can not be found! Mid-point is used."<<std::endl;
418+
AssertThrow(dT0*dT2<=0,ExcMessage("No single solution for inner core!"));
419+
}
420+
else if (dT0*dT1 < 0.)
421+
{
422+
R_2 = R_1;
423+
dT2 = dT1;
424+
}
425+
else if (dT2*dT1 < 0.)
426+
{
427+
R_0 = R_1;
428+
dT0 = dT1;
429+
}
430+
431+
// Update R_1 and recalculate dT1
432+
R_1 = (R_0 + R_2) / 2.;
433+
dT1 = get_dT(R_1);
434+
++steps;
435+
}
436+
}
428437

429438
// Calculate new R,T,X
430439
R = R_1;
431440
T = get_Tc(R);
432441
X = get_X(R);
433442

443+
// Check the signs of dT at the boundaries to classify the solution
434444
if (dT0<0. && dT2>0.)
435445
{
436-
// Normal solution
446+
// Core partially molten, freezing from the inside, normal solution
437447
return true;
438448
}
439449
else if (dT0>0. && dT2<0.)
440450
{
441-
// Snowing core solution
451+
// Core partially molten, snowing core solution
442452
return false;
443453
}
454+
else if (dT0 >= 0. && dT2 >= 0.)
455+
{
456+
// Core fully molten, normal solution
457+
return true;
458+
}
459+
else if (dT0 <= 0. && dT2 <= 0.)
460+
{
461+
// Core fully solid, normal solution
462+
return true;
463+
}
444464
else
445465
{
446466
// No solution found.
@@ -681,21 +701,29 @@ namespace aspect
681701
DynamicCore<dim>::
682702
get_heat_solution(const double Tc, const double r, const double X, double &Eh) const
683703
{
684-
double It = numbers::signaling_nan<double>();
685-
if (D>L)
704+
if (r==Rc)
686705
{
687-
const double B = std::sqrt(1./(1./Utilities::fixed_power<2>(L)-1./Utilities::fixed_power<2>(D)));
688-
It = 4*numbers::PI*Rho_cen/get_T(Tc,0)*(-Utilities::fixed_power<2>(B)*Rc/2*std::exp(-Utilities::fixed_power<2>(Rc/B))+Utilities::fixed_power<3>(B)/std::sqrt(numbers::PI)/4*std::erf(Rc/B));
689-
It -= 4*numbers::PI*Rho_cen/get_T(Tc,0)*(-Utilities::fixed_power<2>(B)*r/2*std::exp(-Utilities::fixed_power<2>(r/B))+Utilities::fixed_power<3>(B)/std::sqrt(numbers::PI)/4*std::erf(r/B));
706+
// No energy change rate if the inner core is fully frozen.
707+
Eh = 0.;
690708
}
691709
else
692710
{
693-
const double B = std::sqrt(1./(Utilities::fixed_power<-2>(D)-Utilities::fixed_power<-2>(L)));
694-
It = 4*numbers::PI*Rho_cen/get_T(Tc,0)*(Utilities::fixed_power<2>(B)*Rc/2*std::exp(Utilities::fixed_power<2>(Rc/B))-Utilities::fixed_power<2>(B)*fun_Sn(B,Rc,100)/2);
695-
It -= 4*numbers::PI*Rho_cen/get_T(Tc,0)*(Utilities::fixed_power<2>(B)*r/2*std::exp(Utilities::fixed_power<2>(r/B))-Utilities::fixed_power<2>(B)*fun_Sn(B,r,100)/2);
711+
double It = numbers::signaling_nan<double>();
712+
if (D>L)
713+
{
714+
const double B = std::sqrt(1./(1./Utilities::fixed_power<2>(L)-1./Utilities::fixed_power<2>(D)));
715+
It = 4*numbers::PI*Rho_cen/get_T(Tc,0)*(-Utilities::fixed_power<2>(B)*Rc/2*std::exp(-Utilities::fixed_power<2>(Rc/B))+Utilities::fixed_power<3>(B)/std::sqrt(numbers::PI)/4*std::erf(Rc/B));
716+
It -= 4*numbers::PI*Rho_cen/get_T(Tc,0)*(-Utilities::fixed_power<2>(B)*r/2*std::exp(-Utilities::fixed_power<2>(r/B))+Utilities::fixed_power<3>(B)/std::sqrt(numbers::PI)/4*std::erf(r/B));
717+
}
718+
else
719+
{
720+
const double B = std::sqrt(1./(Utilities::fixed_power<-2>(D)-Utilities::fixed_power<-2>(L)));
721+
It = 4*numbers::PI*Rho_cen/get_T(Tc,0)*(Utilities::fixed_power<2>(B)*Rc/2*std::exp(Utilities::fixed_power<2>(Rc/B))-Utilities::fixed_power<2>(B)*fun_Sn(B,Rc,100)/2);
722+
It -= 4*numbers::PI*Rho_cen/get_T(Tc,0)*(Utilities::fixed_power<2>(B)*r/2*std::exp(Utilities::fixed_power<2>(r/B))-Utilities::fixed_power<2>(B)*fun_Sn(B,r,100)/2);
723+
}
724+
const double Cc = 4*numbers::PI*Utilities::fixed_power<2>(r)*get_rho(r)*X/(Mc-get_mass(r));
725+
Eh = Rh*(It-(Mc-get_mass(r))/get_T(Tc,r))*Cc;
696726
}
697-
const double Cc = 4*numbers::PI*Utilities::fixed_power<2>(r)*get_rho(r)*X/(Mc-get_mass(r));
698-
Eh = Rh*(It-(Mc-get_mass(r))/get_T(Tc,r))*Cc;
699727
}
700728

701729

@@ -705,12 +733,12 @@ namespace aspect
705733
DynamicCore<dim>::
706734
get_gravity_heating(const double Tc, const double r, const double X, double &Qg, double &Eg) const
707735
{
708-
const double Cc = 4*numbers::PI*Utilities::fixed_power<2>(r)*get_rho(r)*X/(Mc-get_mass(r));
709-
const double C_2 = 3./16.*Utilities::fixed_power<2>(L) - 0.5*Utilities::fixed_power<2>(Rc)*(1.-3./10.*Utilities::fixed_power<2>(Rc/L));
710736
if (r==Rc)
711737
Qg = 0.;
712738
else
713739
{
740+
const double Cc = 4*numbers::PI*Utilities::fixed_power<2>(r)*get_rho(r)*X/(Mc-get_mass(r));
741+
const double C_2 = 3./16.*Utilities::fixed_power<2>(L) - 0.5*Utilities::fixed_power<2>(Rc)*(1.-3./10.*Utilities::fixed_power<2>(Rc/L));
714742
Qg = (8./3.*Utilities::fixed_power<2>(numbers::PI*Rho_cen)*constants::big_g*(
715743
((3./20.*Utilities::fixed_power<5>(Rc)-Utilities::fixed_power<2>(L)*Utilities::fixed_power<3>(Rc)/8.-C_2*Utilities::fixed_power<2>(L)*Rc)*std::exp(-Utilities::fixed_power<2>(Rc/L))
716744
+C_2/2.*Utilities::fixed_power<3>(L)*std::sqrt(numbers::PI)*std::erf(Rc/L))

tests/dynamic_core_fully_molten.prm

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# A simple setup for Earth convection model in a 2d shell
2+
# where the core mantle boundary (CMB) temperature dynamically evolves through time.
3+
#
4+
# This is a variation of dynamic_core.prm with a fully molten core.
5+
6+
include $ASPECT_SOURCE_DIR/tests/dynamic_core.prm
7+
8+
subsection Boundary temperature model
9+
subsection Dynamic core
10+
set Inner temperature = 6000
11+
end
12+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
Number of active cells: 768 (on 4 levels)
3+
Number of degrees of freedom: 10,656 (6,528+864+3,264)
4+
5+
Dynamic core initialized as:
6+
Tc(K) Ri(km) Xi dT/dt(K/year) dR/dt(km/year) dX/dt(1/year)
7+
6000 0 0.042 0 0 0
8+
*** Timestep 0: t=0 years, dt=0 years
9+
Solving temperature system... 0 iterations.
10+
Solving Stokes system (GMG)... 16+0 iterations.
11+
12+
Postprocessing:
13+
CMB heat flux out of the core 1.43 TW,
14+
15+
*** Timestep 1: t=598995 years, dt=598995 years
16+
Dynamic core data updated.
17+
Tc(K) Ri(km) Xi dT/dt(K/year) dR/dt(km/year) dX/dt(1/year)
18+
6000.07 0 0.042 1.12778e-07 0 0
19+
Solving temperature system... 14 iterations.
20+
Solving Stokes system (GMG)... 16+0 iterations.
21+
22+
Postprocessing:
23+
CMB heat flux out of the core 1.43 TW,
24+
25+
*** Timestep 2: t=1e+06 years, dt=401005 years
26+
Dynamic core data updated.
27+
Tc(K) Ri(km) Xi dT/dt(K/year) dR/dt(km/year) dX/dt(1/year)
28+
6000.11 0 0.042 1.14788e-07 0 0
29+
Solving temperature system... 17 iterations.
30+
Solving Stokes system (GMG)... 15+0 iterations.
31+
32+
Postprocessing:
33+
CMB heat flux out of the core 1.31 TW,
34+
35+
Termination requested by criterion: end time
36+
37+
38+
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# 1: Time step number
2+
# 2: Time (years)
3+
# 3: Time step size (years)
4+
# 4: Number of mesh cells
5+
# 5: Number of Stokes degrees of freedom
6+
# 6: Number of temperature degrees of freedom
7+
# 7: Iterations for temperature solver
8+
# 8: Iterations for Stokes solver
9+
# 9: Velocity iterations in Stokes preconditioner
10+
# 10: Schur complement iterations in Stokes preconditioner
11+
# 11: CMB heat flux out of the core (TW)
12+
# 12: CMB Temperature (K)
13+
# 13: Inner core radius (km)
14+
# 14: Light element concentration (%)
15+
# 15: Excess entropy (W/K)
16+
0 0.000000000000e+00 0.000000000000e+00 768 7392 3264 0 15 17 17 1.429e+00 6000.00 0.00 4.2000 -2.864e+07
17+
1 5.989948582733e+05 5.989948582733e+05 768 7392 3264 14 15 17 17 1.429e+00 6000.07 0.00 4.2000 -1.799e+08
18+
2 1.000000000000e+06 4.010051417267e+05 768 7392 3264 17 14 16 16 1.305e+00 6000.11 0.00 4.2000 -1.827e+08

tests/dynamic_core_fully_solid.prm

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# A simple setup for Earth convection model in a 2d shell
2+
# where the core mantle boundary (CMB) temperature dynamically evolves through time.
3+
#
4+
# This is a variation of dynamic_core.prm with a fully frozen core.
5+
6+
include $ASPECT_SOURCE_DIR/tests/dynamic_core.prm
7+
8+
subsection Boundary temperature model
9+
subsection Dynamic core
10+
set Inner temperature = 1000
11+
end
12+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
Number of active cells: 768 (on 4 levels)
3+
Number of degrees of freedom: 10,656 (6,528+864+3,264)
4+
5+
Dynamic core initialized as:
6+
Tc(K) Ri(km) Xi dT/dt(K/year) dR/dt(km/year) dX/dt(1/year)
7+
1000 3481 0.084 0 0 0
8+
*** Timestep 0: t=0 years, dt=0 years
9+
Solving temperature system... 0 iterations.
10+
Solving Stokes system (GMG)... 16+0 iterations.
11+
12+
Postprocessing:
13+
CMB heat flux out of the core 0.175 TW,
14+
15+
*** Timestep 1: t=1e+06 years, dt=1e+06 years
16+
Dynamic core data updated.
17+
Tc(K) Ri(km) Xi dT/dt(K/year) dR/dt(km/year) dX/dt(1/year)
18+
1000.13 3481 0.084 1.33889e-07 0 0
19+
Solving temperature system... 9 iterations.
20+
Solving Stokes system (GMG)... 16+0 iterations.
21+
22+
Postprocessing:
23+
CMB heat flux out of the core 0.175 TW,
24+
25+
Termination requested by criterion: end time
26+
27+
28+
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 1: Time step number
2+
# 2: Time (years)
3+
# 3: Time step size (years)
4+
# 4: Number of mesh cells
5+
# 5: Number of Stokes degrees of freedom
6+
# 6: Number of temperature degrees of freedom
7+
# 7: Iterations for temperature solver
8+
# 8: Iterations for Stokes solver
9+
# 9: Velocity iterations in Stokes preconditioner
10+
# 10: Schur complement iterations in Stokes preconditioner
11+
# 11: CMB heat flux out of the core (TW)
12+
# 12: CMB Temperature (K)
13+
# 13: Inner core radius (km)
14+
# 14: Light element concentration (%)
15+
# 15: Excess entropy (W/K)
16+
0 0.000000000000e+00 0.000000000000e+00 768 7392 3264 0 15 17 17 1.755e-01 1000.00 3481.00 8.4000 8.412e+08
17+
1 1.000000000000e+06 1.000000000000e+06 768 7392 3264 9 15 17 17 1.755e-01 1000.13 3481.00 8.4000 -2.367e+08

0 commit comments

Comments
 (0)