Units in Astrobear
Astrobear uses something like rationalized electromagnetic units (extra factor of in the electric and magnetic fields) - or Lorentz-Heaviside but scaling the field by and the charge density (and current ) by
Using the approach in the appendix of Jackson, we have
This allows us to write Maxwell's equations as
as well as
| Lorentz Force Law | |
| Coulomb's Law |
Most of the time we don't care about , , or , however for the Hall MHD terms, we need to determine the electron charge in our system of units.
For our system of equations, the elementary charge is
MHD equations
Using the system of units described above, we can write the following simplified equations
| Induction equation | |
| Ampere's equation (without Maxwell's correction) | |
| Lorentz Force Law | |
| Poynting's theorem (electromagnetic energy) | |
| Poynting vector |
Equations for momentum, magnetic energy, and magnetic field
Subsituting Ampere's equation and the definition of the Poynting vector into the equations for momentum, magnetic energy, and the magnetic field, we arrive at
Ohm's Law
Not the momentum equation only involves the magnetic field and is the same regardless of the electric field . Note it is also already in a form involving a total divergence making it straightforward to implement in a conservative finite volume scheme. The energy and induction equation, however, will depend on our choice of .
The field can be calculated using a Generalized Ohm's law
Under under certain conditions, we can ignore various terms resulting in the following approximations
| Resistive + Hall MHD | |
| Resistive MHD | |
| Ideal MHD |
Ideal MHD
For ideal MHD we substitute
into the equations above and after some vector calculus (including a very messy vector quadruple product with a del operator), manage to write the energy term as a total divergence (and the field as a curl)
Resistive MHD
For resistive MHD, it will be convenient to define the resistive component of the electric field
Then our total electric field is given by
We then need to add this contribution to the induction equation and the energy equation where it enters both the Poynting flux and the work done on the the charge distribution .
However the work done on the charge distribution would need to also be added to the thermal energy cancelling any changes in the total energy due to the term. This leaves us with.
Note the resistive part of the induction equation
can be expanded as
Which hints to the diffusive nature of the resistive term. It also involves a second derivative so the time stepping criteria will be and an implicit solve might be worth considering.
Hall+Resistive MHD
As in resistive MHD, it will be convenient to define the Hall component of the electric field
Then our total electric field is given by
Again we need to add the additional electric field component to the energy equation (in both the Poynting Flux and the work on the charge distribution) as well as the induction equation. Also note in this case the electric field is perpendicular to the current - so it does no work on the charge distribution.
So as in the Resistive case, only the Poynting Flux term needs to be added to the energy equation.
MHD
To enable MHD:
- In
physics.data, setlMHDtoT.
If the code is using constrained transport, then the logical flag MaintainAuxArrays will be .True.. In that case you will need to initialize the face averaged magnetic field values in the aux array in your module file. The following diagram displays the various aux variables and what positions they correspond to - as well as the positions of the EMF as well as the vector potential.
Here is an example for an initial magnetic field that is
dx = (/(merge(1d0,0d0,nDim >= i),i=1,3)/)*levels(Info%level)%dx
DO i=1, Info%mX(1)
x = Info%xBounds(1,1)+(REAL(i)-.5d0)*dx(1)
DO j=1, Info%mX(2)
y = Info%xBounds(2,1)+(REAL(j)-.5d0)*dx(2)
DO k=1, Info%mX(3)
z = Info%xBounds(3,1)+(REAL(k)-.5d0)*dx(3)
Info%q(i,j,k,iBx)=Bx(x,y,z)
Info%q(i,j,k,iBy)=By(x,y,z)
Info%q(i,j,k,iBz)=Bz(x,y,z)
END DO
END DO
END DO
IF (MaintainAuxArrays) THEN
IF (nDim >= 1) THEN
!Note the +1 in the i loop DO statement and the -1d0 in
!the x coordinate calculation instead of -.5d0
DO i=1, Info%mX(1)+1
x = Info%xBounds(1,1)+(REAL(i)-1d0)*dx(1)
DO j=1, Info%mX(2)
y = Info%xBounds(2,1)+(REAL(j)-.5d0)*dx(2)
DO k=1, Info%mX(3)
z = Info%xBounds(3,1)+(REAL(k)-.5d0)*dx(3)
Info%aux(i,j,k,1)=Bx(x,y,z)
END DO
END DO
END DO
!Now we can update the cell centered value with the adjacent face average
Info%q(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),iBx)=.5d0 * (&
Info%aux(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),1) + &
Info%aux(2:Info%mX(1)+1,1:Info%mX(2),1:Info%mX(3),1) )
IF (nDim >= 2) THEN
!Note the +1 in the j loop DO statement and the -1d0 in
!the y coordinate calculation instead of -.5d0
DO i=1, Info%mX(1)
x = Info%xBounds(1,1)+(REAL(i)-.5d0)*dx(1)
DO j=1, Info%mX(2)+1
y = Info%xBounds(2,1)+(REAL(j)-1d0)*dx(2)
DO k=1, Info%mX(3)
z = Info%xBounds(3,1)+(REAL(k)-.5d0)*dx(3)
Info%aux(i,j,k,2)=By(x,y,z)
END DO
END DO
END DO
!Now we can update the cell centered value with the adjacent face average
Info%q(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),iBy)=.5d0 * (&
Info%aux(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),2) + &
Info%aux(1:Info%mX(1),2:Info%mX(2)+1,1:Info%mX(3),2) )
IF (nDim >= 3) THEN
!Note the +1 in the k loop DO statement and the -1d0 in
!the z coordinate calculation instead of -.5d0
DO i=1, Info%mX(1)
x = Info%xBounds(1,1)+(REAL(i)-.5d0)*dx(1)
DO j=1, Info%mX(2)
y = Info%xBounds(2,1)+(REAL(j)-.5d0)*dx(2)
DO k=1, Info%mX(3)+1
z = Info%xBounds(3,1)+(REAL(k)-1d0)*dx(3)
Info%aux(i,j,k,3)=Bz(x,y,z)
END DO
END DO
END DO
!Now we can update the cell centered value with the adjacent face average
Info%q(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),iBz)=.5d0 * (&
Info%aux(1:Info%mX(1),1:Info%mX(2),1:Info%mX(3),3) + &
Info%aux(1:Info%mX(1),1:Info%mX(2),2:Info%mX(3)+1,3) )
END IF
END IF
END IF
END IF
Of course the above example works for any field - but does not impose the divergence free constraint. Presumably the functions Bx, By, and Bz would take care of that to the degree they are accurate face averages - but a better approach is to instead approximate the vector potential at cell edges and then discretely difference it to get the face average fields.
IF (nDim == 2) THEN
!In 2D the only component of the vector potential that matters is Az
ALLOCATE(A(1:Info%mX(1)+1,1:Info%mX(2)+1,1,1))
DO i=1, Info%mX(1)+1
x = Info%xBounds(1,1)+(REAL(i)-1d0)*dx(1)
DO j=1, Info%mX(2)+1
y = Info%xBounds(2,1)+(REAL(j)-1d0)*dx(2)
A(i,j,1,1)=Az(x,y,z)
END DO
END DO
!Then take the curl of A to get B
FORALL(i=1:Info%mX(1)+1,j=1:Info%mX(2))
Info%aux(i,j,1,1)=+(A(i,j+1,1,1)-A(i,j,1,1))/dx(2)
END FORALL
FORALL(i=1:Info%mX(1),j=1:Info%mX(2)+1)
Info%aux(i,j,1,2)=-(A(i+1,j,1,1)-A(i,j,1,1))/dx(1)
END FORALL
ELSEIF (nDim == 3) THEN
!Now the vector potential is a 3 component vector
ALLOCATE(A(1:Info%mX(1)+1,1:Info%mX(2)+1,1:Info%mX(3)+1,3))
!We could do this with three separate loops to avoid calculating
!A(:,:,Info%mX(3)+1,3),A(:,Info%mX(2)+1,:,2), and A(Info%mX(1)+1,:,:,1)
!Since these do not get used in taking the curl...
DO i=1, Info%mX(1)+1
x = Info%xBounds(1,1)+(REAL(i)-1d0)*dx(1)
DO j=1, Info%mX(2)+1
y = Info%xBounds(2,1)+(REAL(j)-1d0)*dx(2)
DO k=1, Info%mX(3)+1
z = Info%xBounds(3,1)+(REAL(k)-1d0)*dx(3)
A(i,j,k,1)=Ax(x+half*dx(1),y,z)
A(i,j,k,2)=Ay(x,y+half*dx(2),z)
A(i,j,k,3)=Az(x,y,z+half*dx(3))
END DO
END DO
END DO
!Then take the curl of A to get B
FORALL(i=1:Info%mX(1)+1,j=1:Info%mX(2),k=1:Info%mX(3))
Info%aux(i,j,k,1)=+(A(i,j+1,k,3)-A(i,j,k,3))/dx(2)-(A(i,j,k+1,2)-A(i,j,k,2))/dx(3)
END FORALL
FORALL(i=1:Info%mX(1),j=1:Info%mX(2)+1,k=1:Info%mX(3))
Info%aux(i,j,k,2)=+(A(i,j,k+1,1)-A(i,j,k,1))/dx(3)-(A(i+1,j,k,3)-A(i,j,k,3))/dx(1)
END FORALL
FORALL(i=1:Info%mX(1),j=1:Info%mX(2),k=1:Info%mX(3)+1)
Info%aux(i,j,k,3)=+(A(i+1,j,k,2)-A(i,j,k,2))/dx(1)-(A(i,j+1,k,1)-A(i,j,k,1))/dx(2)
END FORALL
END IF
