# -*- coding: utf-8 -*-
import numpy as np
import os
import re
from .log import MagicSetup
from .libmagic import scanDir
from magic.setup import buildSo
from .npfile import npfile
if buildSo:
try:
import magic.greader_single as Gsngl
import magic.greader_double as Gdble
readingMode = 'f2py'
except ImportError:
readingMode = 'python'
# print('read with {}'.format(readingMode))
else:
readingMode = 'python'
print(readingMode)
def getGraphEndianness(filename):
"""
This function determines the endianness of the graphic files and
also tries to detect wheter there record markers or not.
:param filename: input of the filename
:type filename: str
:returns: the endianness of the file ('B'='big_endian' or
'l'='little_endian') and the presence of record
markers
:rtype: list
"""
try:
f = npfile(filename, endian='B')
try:
st = f.fort_read('S20')
if len(st) > 1:
raise TypeError
endian = 'B'
except TypeError:
endian = 'l'
access = 'rm'
except ValueError:
f = open(filename, 'rb')
# Little endian
version = np.fromfile(f, np.int32, count=1)[0]
endian = 'l'
if abs(version) > 100:
f.close()
f = open(filename, 'rb')
version = np.fromfile(f, '>i4', count=1)[0]
endian = 'B'
access = 'st'
f.close()
return endian, access
[docs]class MagicGraph(MagicSetup):
"""
This class allows to read the 3-D graphic outputs of the MagIC code
(:ref:`G_#.TAG <secGraphFile>` and G_ave.TAG) files. Those are
binary unformatted outputs, there are therefore two ways to load them:
* If buildLib=True in magic.cfg and the fortran libraries were correctly
built, then the reader uses a fortran program that is expected to be
much faster than the pure python routine.
* If buildLib=False, then a pure python program is used to read the G
files.
>>> # Regular G files
>>> gr = MagicGraph(ivar=1, tag='N0m2a')
>>> print(gr.vr.shape) # shape of vr
>>> print(gr.ek) # print ekman number
>>> print(gr.minc) # azimuthal symmetry
>>> # Averaged G file with double precision
>>> gr = MagicGraph(ave=True, tag='N0m2', precision=np.float64)
"""
[docs] def __init__(self, ivar=None, datadir='.', quiet=True,
ave=False, tag=None, precision=np.float32):
"""
:param ave: when set to True, it tries to find an average
G file (G_ave.TAG)
:type ave: bool
:param ivar: the number of the G file
:type ivar: int
:param tag: extension TAG of the G file. If not specified, the
most recent G_#.TAG file found in the directory will
be selected.
:type tag: str
:param quiet: when set to True, makes the output silent
:type quiet: bool
:param datadir: directory of the G file (default is . )
:type datadir: str
:param precision: single or double precision (default np.float32)
:type precision: str
"""
self.precision = precision
if ave:
self.name = 'G_ave'
else:
self.name = 'G_'
if tag is not None:
if ivar is not None:
file = '{}{}.{}'.format(self.name, ivar, tag)
filename = os.path.join(datadir, file)
else:
pattern = os.path.join(datadir, '{}*{}'.format(self.name, tag))
files = scanDir(pattern)
if len(files) != 0:
filename = files[-1]
else:
print('No such tag... try again')
return
if os.path.exists('log.{}'.format(tag)):
MagicSetup.__init__(self, datadir=datadir, quiet=True,
nml='log.{}'.format(tag))
else:
if ivar is not None:
pattern = os.path.join(datadir, '{}{}*'.format(self.name, ivar))
files = scanDir(pattern)
filename = files[-1]
else:
pattern = os.path.join(datadir, '{}*'.format(self.name))
files = scanDir(pattern)
filename = files[-1]
# Determine the setup
mask = re.compile(r'.*\.(.*)')
ending = mask.search(files[-1]).groups(0)[0]
if os.path.exists('log.{}'.format(ending)):
MagicSetup.__init__(self, datadir=datadir, quiet=True,
nml='log.{}'.format(ending))
if not os.path.exists(filename):
print('No such file')
return
if not quiet:
print('Reading {}'.format(filename))
# Get file endianness
endian, access = getGraphEndianness(filename)
if readingMode != 'python':
if self.precision == np.float32:
G = Gsngl.greader_single
elif self.precision == np.float64:
G = Gdble.greader_double
if access == 'st':
G.readg_stream(filename, endian=endian)
elif access == 'rm':
G.readg(filename, endian=endian)
self.nr = int(G.nr)
self.n_r_ic_max = int(G.nric)-1
self.ntheta = int(G.nt)
self.npI = int(G.np)
self.minc = int(G.minc)
self.time = G.time
self.ra = G.ra
self.ek = G.ek
self.pr = G.pr
self.raxi = G.raxi
self.sc = G.sc
self.prmag = G.prmag
self.radratio = G.radratio
self.sigma = G.sigma
if self.npI == self.ntheta*2:
self.npI = int(self.npI/self.minc)
self.nphi = int(self.npI*self.minc+1)
self.radius = G.radius
self.colatitude = G.colat
self.entropy = G.entropy
self.vr = G.vr
self.vtheta = G.vt
self.vphi = G.vp
self.pre = G.pre
self.xi = G.xi
self.phase = G.phase
if self.prmag != 0:
self.Br = G.br
self.Btheta = G.bt
self.Bphi = G.bp
if self.prmag != 0 and self.n_r_ic_max > 1:
self.radius_ic = G.radius_ic
self.Br_ic = G.br_ic
self.Btheta_ic = G.bt_ic
self.Bphi_ic = G.bp_ic
else:
if access == 'rm':
self.read_record_marker(filename, endian, quiet=quiet)
elif access == 'st':
self.read_stream(filename, endian)
[docs] def read_stream(self, filename, endian):
"""
This function is used to read a Graphic file that has no record marker.
:param filename: name of the graphic file
:type filename: str
:param endian: endianness of the file
:type endian: str
"""
if endian == 'B':
prefix = '>'
else:
prefix = ''
if self.precision == np.float32:
suffix = 4
else:
suffix = 8
f = open(filename, 'rb')
# Header
fmt = '{}i{}'.format(prefix, suffix)
version = np.fromfile(f, fmt, count=1)[0]
fmt = '{}S64'.format(prefix)
runID = np.fromfile(f, fmt, count=1)[0]
fmt = '{}f{}'.format(prefix, suffix)
self.time = np.fromfile(f, fmt, count=1)[0]
if version > 13:
self.ra, self.pr, self.raxi, self.sc, self.ek, self.stef, self.prmag, \
self.radratio, self.sigma = np.fromfile(f, fmt, count=9)
else:
self.ra, self.pr, self.raxi, self.sc, self.ek, self.prmag, self.radratio, \
self.sigma = np.fromfile(f, fmt, count=8)
self.stef = 0.
fmt = '{}i{}'.format(prefix, suffix)
self.nr, self.ntheta, self.npI, self.minc, self.n_r_ic_max = \
np.fromfile(f, fmt, count=5)
if self.npI == self.ntheta*2:
self.npI = int(self.npI/self.minc)
self.nphi = self.npI*self.minc+1
if version > 13:
l_heat, l_chem, l_phase, l_mag, l_press, l_cond_ic = \
np.fromfile(f, fmt, count=6)
else:
l_heat, l_chem, l_mag, l_press, l_cond_ic = np.fromfile(f, fmt, count=5)
l_phase = False
fmt = '{}f{}'.format(prefix, suffix)
self.colatitude = np.fromfile(f, fmt, count=self.ntheta)
self.radius = np.fromfile(f, fmt, count=self.nr)
if ( l_mag != 0 and self.n_r_ic_max > 1 ):
self.radius_ic = np.fromfile(f, fmt, count=self.n_r_ic_max)
self.vr = np.zeros((self.npI, self.ntheta, self.nr), self.precision)
self.vtheta = np.zeros_like(self.vr)
self.vphi = np.zeros_like(self.vr)
if l_heat != 0:
self.entropy = np.zeros_like(self.vr)
if l_chem != 0:
self.xi = np.zeros_like(self.vr)
if l_phase != 0:
self.phase = np.zeros_like(self.vr)
if l_press != 0:
self.pre = np.zeros_like(self.vr)
if l_mag != 0:
self.Br = np.zeros_like(self.vr)
self.Btheta = np.zeros_like(self.vr)
self.Bphi = np.zeros_like(self.vr)
if self.n_r_ic_max > 1:
self.Br_ic = np.zeros((self.npI, self.ntheta, self.n_r_ic_max), \
self.precision)
self.Btheta_ic = np.zeros_like(self.Br_ic)
self.Bphi_ic = np.zeros_like(self.Br_ic)
# Outer core
for i in range(self.nr):
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.vr[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.vtheta[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.vphi[:, :, i] = dat.reshape(self.npI, self.ntheta)
if l_heat != 0:
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.entropy[:, :, i] = dat.reshape(self.npI, self.ntheta)
if l_chem != 0:
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.xi[:, :, i] = dat.reshape(self.npI, self.ntheta)
if l_phase != 0:
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.phase[:, :, i] = dat.reshape(self.npI, self.ntheta)
if l_press != 0:
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.pre[:, :, i] = dat.reshape(self.npI, self.ntheta)
if l_mag != 0:
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Br[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Btheta[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Bphi[:, :, i] = dat.reshape(self.npI, self.ntheta)
# Inner core
if ( l_mag != 0 and self.n_r_ic_max > 1 ):
for i in range(self.n_r_ic_max):
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Br_ic[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Btheta_ic[:, :, i] = dat.reshape(self.npI, self.ntheta)
dat = np.fromfile(f, fmt, count=self.ntheta*self.npI)
self.Bphi_ic[:, :, i] = dat.reshape(self.npI, self.ntheta)
f.close()
[docs] def read_record_marker(self, filename, endian, quiet=True):
"""
This function is used to read a Graphic file that contains record markers.
:param filename: name of the graphic file
:type filename: str
:param endian: endianness of the file
:type endian: str
:param quiet: when set to True, makes the output silent
:type quiet: bool
"""
# Read data
inline = npfile(filename, endian=endian)
# Read the header
version = inline.fort_read('|S20')[0]
version = version.rstrip()
runID = inline.fort_read('|S64')[0]
self.time, n_r_max, n_theta_max, self.npI, n_r_ic_max, minc, \
nThetaBs, self.ra, self.ek, self.pr, self.prmag, \
self.radratio, self.sigma = inline.fort_read(self.precision)
self.nr = int(n_r_max)
self.ntheta = int(n_theta_max)
self.npI = int(self.npI)
self.minc = int(minc)
if self.npI == self.ntheta*2:
self.npI = int(self.npI/self.minc)
self.nphi = self.npI*self.minc+1
self.n_r_ic_max = int(n_r_ic_max) - 1
nThetaBs = int(nThetaBs)
if not quiet:
st = 'Rayleigh = {:.1e}, Ekman = {:.1e}, Prandtl = {:.1e}'.format(
self.ra, self.ek, self.pr)
print(st)
print('nr = {}, nth = {}, nphi = {}'.format(
self.nr, self.ntheta, self.npI))
self.colatitude = inline.fort_read(self.precision)
self.radius = np.zeros((self.nr), self.precision)
if self.prmag != 0 and self.n_r_ic_max > 1:
self.radius_ic = np.zeros((self.n_r_ic_max), self.precision)
entropy = np.zeros((self.npI, self.ntheta, self.nr),
self.precision)
vr = np.zeros_like(entropy)
vtheta = np.zeros_like(entropy)
vphi = np.zeros_like(entropy)
if self.prmag != 0:
Br = np.zeros_like(entropy)
Btheta = np.zeros_like(entropy)
Bphi = np.zeros_like(entropy)
if self.sigma != 0:
Br_ic = np.zeros((self.npI, self.ntheta, self.n_r_ic_max),
self.precision)
Btheta_ic = np.zeros_like(Br_ic)
Bphi_ic = np.zeros_like(Br_ic)
if version == b'Graphout_Version_8' or \
version == b'Graphout_Version_10' or \
version == b'Graphout_Version_12':
pressure = np.zeros_like(entropy)
if version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
xi = np.zeros_like(entropy)
for k in range(self.nr*nThetaBs):
# radius and Thetas in this block
ir, rad, ilat1, ilat2 = inline.fort_read(self.precision)
ir = int(ir)
ilat1 = int(ilat1) - 1
ilat2 = int(ilat2) - 1
self.radius[ir] = rad
nth_loc = ilat2 - ilat1 + 1
if version == b'Graphout_Version_9' or \
version == b'Graphout_Version_10' or \
version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
if self.prmag != 0:
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
entropy[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vr[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vtheta[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vphi[:, ilat1:ilat2+1, ir] = data.T
if version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
xi[:, ilat1:ilat2+1, ir] = data.T
if version == b'Graphout_Version_10' or \
version == b'Graphout_Version_12':
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
pressure[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Br[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Btheta[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Bphi[:, ilat1:ilat2+1, ir] = data.T
else:
# vectorize
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
entropy[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vr[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vtheta[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
vphi[:, ilat1:ilat2+1, ir] = data.T
if version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
xi[:, ilat1:ilat2+1, ir] = data.T
if version == b'Graphout_Version_10' or \
version == b'Graphout_Version_12':
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
pressure[:, ilat1:ilat2+1, ir] = data.T
else:
if self.prmag != 0:
# To vectorize, one must also read the end-of-line
# symbol that accounts for 2 additionnal floats
# For the last line of the chunk, we should not
# read the last symbol
if version == b'Graphout_Version_8':
data = inline.fort_read(
self.precision,
shape=(8*(self.npI+2)*nth_loc-2))
# Add 2 zeros for the last lines
data = np.append(data, 0.)
data = np.append(data, 0.)
data = data.reshape((8, nth_loc, self.npI+2))
else:
data = inline.fort_read(self.precision,
shape=(7*(self.npI+2)*nth_loc-2))
# Add 2 zeros for the last lines
data = np.append(data, 0.)
data = np.append(data, 0.)
data = data.reshape((7, nth_loc, self.npI+2))
data = data[:, :, :-2:]
entropy[:, ilat1:ilat2+1, ir] = data[0, ...].T
vr[:, ilat1:ilat2+1, ir] = data[1, ...].T
vtheta[:, ilat1:ilat2+1, ir] = data[2, ...].T
vphi[:, ilat1:ilat2+1, ir] = data[3, ...].T
if version == b'Graphout_Version_8':
pressure[:, ilat1:ilat2+1, ir] = data[4, ...].T
Br[:, ilat1:ilat2+1, ir] = data[5, ...].T
Btheta[:, ilat1:ilat2+1, ir] = data[6, ...].T
Bphi[:, ilat1:ilat2+1, ir] = data[7, ...].T
else:
Br[:, ilat1:ilat2+1, ir] = data[4, ...].T
Btheta[:, ilat1:ilat2+1, ir] = data[5, ...].T
Bphi[:, ilat1:ilat2+1, ir] = data[6, ...].T
else:
if version == b'Graphout_Version_8':
data = inline.fort_read(
self.precision,
shape=(5*(self.npI+2)*nth_loc-2))
# Add 2 zeros for the last lines
data = np.append(data, 0.)
data = np.append(data, 0.)
data = data.reshape((5, nth_loc, self.npI+2))
else:
data = inline.fort_read(
self.precision,
shape=(4*(self.npI+2)*nth_loc-2))
# Add 2 zeros for the last lines
data = np.append(data, 0.)
data = np.append(data, 0.)
data = data.reshape((4, nth_loc, self.npI+2))
data = data[:, :, :-2:]
entropy[:, ilat1:ilat2+1, ir] = data[0, ...].T
vr[:, ilat1:ilat2+1, ir] = data[1, ...].T
vtheta[:, ilat1:ilat2+1, ir] = data[2, ...].T
vphi[:, ilat1:ilat2+1, ir] = data[3, ...].T
if version == b'Graphout_Version_8':
pressure[:, ilat1:ilat2+1, ir] = data[4, ...].T
if self.prmag != 0 and self.n_r_ic_max > 1:
for k in range(self.n_r_ic_max-1):
# radius and Thetas in this block
ir, rad, ilat1, ilat2 = inline.fort_read(self.precision)
ir = ir-self.nr+1
ir = int(ir)
ilat1 = int(ilat1) - 1
ilat2 = int(ilat2) - 1
self.radius_ic[ir] = rad
nth_loc = ilat2 - ilat1 + 1
if version == b'Graphout_Version_9' or \
version == b'Graphout_Version_10' or \
version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Br_ic[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Btheta_ic[:, ilat1:ilat2+1, ir] = data.T
data = inline.fort_read(self.precision,
shape=(nth_loc, self.npI))
Bphi_ic[:, ilat1:ilat2+1, ir] = data.T
else:
data = inline.fort_read(self.precision,
shape=(3*(self.npI+2)*nth_loc-2))
data = np.append(data, 0.)
data = np.append(data, 0.)
data = data.reshape((3, nth_loc, self.npI+2))
data = data[:, :, :-2:]
Br_ic[:, ilat1:ilat2+1, ir] = data[0, ...].T
Btheta_ic[:, ilat1:ilat2+1, ir] = data[1, ...].T
Bphi_ic[:, ilat1:ilat2+1, ir] = data[2, ...].T
Br_ic[:, :, 0] = Br[:, :, -1]
Bt_ic[:, :, 0] = Bt[:, :, -1]
Bp_ic[:, :, 0] = Bp[:, :, -1]
inline.close()
# Sorting of data (strange hemispherical way that the magic
# code uses
entropy = self.rearangeLat(entropy)
vr = self.rearangeLat(vr)
vtheta = self.rearangeLat(vtheta)
vphi = self.rearangeLat(vphi)
if self.prmag != 0:
Br = self.rearangeLat(Br)
Btheta = self.rearangeLat(Btheta)
Bphi = self.rearangeLat(Bphi)
if self.n_r_ic_max > 1:
Br_ic = self.rearangeLat(Br_ic)
Btheta_ic = self.rearangeLat(Btheta_ic)
Bphi_ic = self.rearangeLat(Bphi_ic)
if version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
xi = self.rearangeLat(xi)
if version == b'Graphout_Version_8' or \
version == b'Graphout_Version_10' or \
version == b'Graphout_Version_12':
pressure = self.rearangeLat(pressure)
# Normalise r
self.radius = self.radius/(1.-self.radratio)
if self.prmag != 0 and self.n_r_ic_max > 1:
self.radius_ic = self.radius_ic/(1.-self.radratio)
# Full solution by repeating minc times the structure
self.nphi = self.minc * self.npI + 1
self.entropy = entropy
self.vr = vr
self.vtheta = vtheta
self.vphi = vphi
if self.prmag != 0:
self.Br = Br
self.Btheta = Btheta
self.Bphi = Bphi
if self.n_r_ic_max > 1:
self.Br_ic = Br_ic
self.Btheta_ic = Btheta_ic
self.Bphi_ic = Bphi_ic
if version == b'Graphout_Version_11' or \
version == b'Graphout_Version_12':
self.xi = xi
if version == b'Graphout_Version_8' or \
version == b'Graphout_Version_10' or \
version == b'Graphout_Version_12':
self.pre = pressure
[docs] def rearangeLat(self, field):
"""
This function is used to unfold the colatitudes
:param field: input array with MagIC ordering of colatitudes (i.e.
successively Northern Hemisphere and Southern
Hemisphere)
:type field: numpy.ndarray
:return: an array with the regular ordering of the colatitudes
:rtype: numpy.ndarray
"""
even = field[:, ::2, :]
odd = field[:, 1::2, :]
return np.concatenate((even, odd[:, ::-1, :]), axis=1)