1D hydrostatic equilibrium with Fipy #996
Replies: 5 comments 13 replies
-
Can you clarify, is |
Beta Was this translation helpful? Give feedback.
-
Since your equations are hyperbolic, FiPy is going to have trouble. Flow is not my thing and I've previously had trouble trying to figure out what "outflow" means in the context of our Stokes problem. I am generally distrustful of 1D formulations; when written as partial derivatives, it's impossible to tell the difference between divergences and gradients, which need to be discretized differently. When written with vector operators, as you have, you end up with divergences of scalars, which makes my head hurt. TLDR, I'd recommend writing your equations in vector form and only reduce to the 1D form at the very end. |
Beta Was this translation helpful? Give feedback.
-
Today, I've gone deeper into this. I've found that the boundary conditions are not the cause of the non-equilibrium, but rather the calculation of the gradient in the momentum equation. In the boundary points, both the gaussGrad or the leastSquaresGrad would have a value which does not match the required value of the boundary condition in the respective position. This then leads to spurious (i.e. numerical) forces, which destabilises the system. I'm pasting the full code below, but the key point is that I've changed the momentum equation from eq_momentum = (fipy.TransientTerm(var=mom)
== - fipy.ConvectionTerm(coeff=v*[[1.]],var=mom)
- (gamma-1)*Eth.leastSquaresGrad[0]
- fipy.ImplicitSourceTerm(coeff=g,var=dens)
) to tmpterm = ((gamma-1)*Eth.leastSquaresGrad[0]).value
tmpterm[0]= -g*basevalue*np.exp(-mesh.cellCenters[0].value[0]/scaleheight)
tmpterm[-1]= -g*basevalue*np.exp(-mesh.cellCenters[0].value[-1]/scaleheight)
gradp = fipy.CellVariable(name="gradp", mesh=mesh, value=tmpterm, hasOld=True)
eq_momentum = (fipy.TransientTerm(var=mom)
== - fipy.ConvectionTerm(coeff=v*[[1.]],var=mom)
- gradp
- fipy.ImplicitSourceTerm(coeff=g,var=dens)
) The latter is a dirty hack, where I replace the first and last point of the gradient with the gradient that is expected in this condition. With that hack, the atmosphere remains stable for around 1000 iterations. I suspect after that time other numerical errors creep in, but that is not the issue that is being discussed here. Could you suggest a more proper way to include the gradient in the momentum equation? It seems the gradient is numerically computed from the boundary value, and not the boundary gradient. It is this latter that is constrained in this example. Best regards, Tom """
Smallest workable example of hydrostatic equilibrium. Norbert 22.01.2024
"""
import fipy
import numpy as np
import pylab as plt
#system of units (relative to CGS):
# user specified units
unit_length = 1.e8 # cm
unit_numberdensity = 1.e9 # cm^-3
unit_temperature = 1.e6 # K
# constants
He_abundance = 0.1
mp = 1.67e-24 # g
kB = 1.3806488e-16 # erg K^-1
miu0 = 4*np.pi
gamma=5./3.
R_sun = 6.957e10/unit_length # cm
a = 1.0 + 4.0*He_abundance
b = 2.0 + 3.0*He_abundance
# derived units
unit_density = a*mp*unit_numberdensity
unit_pressure = b*unit_numberdensity*kB*unit_temperature
unit_velocity = (unit_pressure/unit_density)**0.5
unit_time = unit_length/unit_velocity
L = 1.e10/unit_length # in cm
nx = 1000
dx = L/nx
mesh = fipy.Grid1D(dx=L / nx, nx=nx)
x = mesh.cellCenters[0]
x.name='x'
g = 2.74e4 * unit_length/unit_velocity**2 # test with constant g
T_ini = 1.e6/unit_temperature # in K
dens=fipy.CellVariable(name="dens",mesh=mesh,value=0., hasOld=True)
scaleheight = 5.e9/unit_length #in cm
scaleheight = T_ini*unit_temperature/(g*unit_velocity**2/unit_length)* kB/mp/a*b /unit_length
basevalue = 1.e-15/unit_density # in g cm^{-3}
dens.setValue(basevalue*np.exp(-mesh.cellCenters/scaleheight))
dens.faceValue.constrain(basevalue*np.exp(-mesh.faceCenters[0].value[0]/scaleheight), where=mesh.facesLeft)
dens.faceValue.constrain(basevalue*np.exp(-mesh.faceCenters[0].value[-1]/scaleheight), where=mesh.facesRight)
vr=fipy.CellVariable(name="vr",mesh=mesh,value=0.)
basevalue_v = 0.e0/unit_velocity # cm/s
vr.setValue(basevalue_v*np.exp(vr.mesh.cellCenters/scaleheight))
Ek=dens*pow(vr,2)/2
mom = fipy.CellVariable(name="mom", mesh=mesh, value=0., hasOld=True)
mom.setValue(dens*vr)
mom0=dens.value[0]*vr.value[0]
mom.faceValue.constrain(0.0, where=mesh.facesLeft)
mom.faceValue.constrain(0.0, where=mesh.facesRight)
v=(mom/dens).arithmeticFaceValue
divv=v.divergence
Eth = fipy.CellVariable(name="Eth", mesh=mesh, value=dens.value*T_ini/(gamma-1), hasOld=True)
E0=Eth.value[0]
Eth.faceGrad.constrain(-g*basevalue*np.exp(-mesh.faceCenters[0].value[0]/scaleheight)/(gamma-1), where=mesh.facesLeft)
Eth.faceGrad.constrain(-g*basevalue*np.exp(-mesh.faceCenters[0].value[-1]/scaleheight)/(gamma-1), where=mesh.facesRight)
eq_continuity = (fipy.TransientTerm(var=dens)
== - fipy.ConvectionTerm(coeff=v*[[1.]], var=dens)
)
eq_eth = (fipy.TransientTerm(var=Eth)
== - fipy.ConvectionTerm(coeff=v*[[1.]],var=Eth)
- fipy.ImplicitSourceTerm(coeff=divv,var=Eth)*(gamma-1)
)
tmpterm = ((gamma-1)*Eth.leastSquaresGrad[0]).value
tmpterm[0]= -g*basevalue*np.exp(-mesh.cellCenters[0].value[0]/scaleheight)
tmpterm[-1]= -g*basevalue*np.exp(-mesh.cellCenters[0].value[-1]/scaleheight)
gradp = fipy.CellVariable(name="gradp", mesh=mesh, value=tmpterm, hasOld=True)
eq_momentum = (fipy.TransientTerm(var=mom)
== - fipy.ConvectionTerm(coeff=v*[[1.]],var=mom)
# - (gamma-1)*Eth.leastSquaresGrad[0]
- gradp
- fipy.ImplicitSourceTerm(coeff=g,var=dens)
)
eqn = eq_continuity & eq_momentum & eq_eth
viewer = fipy.viewers.matplotlibViewer.matplotlib1DViewer.Matplotlib1DViewer(vars=(dens, mom/dens*unit_velocity/1.e6, Eth*(gamma-1)),
datamin=-0.5, datamax=1.,legend='upper left', title = '1D hydro solution')
viewer.axes.legend([r'Density ($10^{-12}\ \mathrm{kg\ m^{-3}}$)',r'Velocity ($\mathrm{10\ km/s}$)',r'Pressure (0.03 Pa)'])
viewer.plot()
steps=5000
# 60% of maximal sound speed in the domain
cflcond = 0.6 * dx/np.max(np.sqrt(gamma**2*Eth.value/dens.value))
timeStepDuration = cflcond
t=np.arange(steps)*timeStepDuration
for step in np.arange(steps):
Eth.updateOld()
dens.updateOld()
mom.updateOld()
gradp.updateOld()
eqn.solve(dt=timeStepDuration)
if step%100 ==0:
print('done step',step, ', time', step*timeStepDuration*unit_time)
viewer.plot() |
Beta Was this translation helpful? Give feedback.
-
I think you are right in saying that the current representation of the system is probably the culprit. What is often done to achieve that is to reformulate the as where - (gamma-1)*fipy.ConvectionTerm(coeff=[[1.]],var=Eth) does not seem to be the correct understanding on my side. Could you refer me to an example implementation of a ConvectionTerm written in terms of the unit tensor? |
Beta Was this translation helpful? Give feedback.
-
I haven't forgotten about you! Using Note that I shifted from solving for momentum to solving for velocity, as I believe this is necessary for applying the pressure corrector to the continuity equation. I'd have thought the Rhie-Chow interpolation would have dealt with the ringing, but I obviously don't know what I'm doing. Smaller time steps, more sweeps, relaxation, and different types of |
Beta Was this translation helpful? Give feedback.
-
I have originally asked this question on stackoverflow.com, but
jeguyer
suggested that I move it here. Here is my original description:I am trying to implement the compressible Euler equations including a full energy equation in Fipy. In the code below, I aim to obtain a hydrostatic solution ($dp/dx = -\rho g$ ) in the presence of gravity. However, the code does not seem to do the job properly. I am not sure what could be wrong with these boundary conditions:
I am expecting the 1D system to reach equilibrium, which does not seem to happen.
EDIT: The equations that I'm trying to solve are the following (conservation of mass, momentum, and total energy):
where the pressure$p = (\gamma-1)(E - \rho v_x^2/2)$ is a derived quantity.
Note that in the code written above I have instead implemented the equation for (specific) internal energy:
related to pressure as$p = (\gamma-1)E_{th}$ , but meanwhile I believe that the equation for total energy should be implemented.
The boundary conditions would be the following: free flow in/out of the system, thus Neumann-type zero gradient for momentum. Density can also be the same, to allow changes. However, the pressure should be stratified according to the hydrostatic formula in the boundary to prevent draining of the system, at both ends of the 1D domain, therefore its gradient has to be given by$dp/dx = -\rho g$ . However, if we do not implement the internal energy as one of the equations but the total energy equation instead, I am not entirely sure how to implement this hydrostatic boundary condition for the total energy.
Beta Was this translation helpful? Give feedback.
All reactions