Source code for magic.libmagic

# -*- coding: utf-8 -*-
import scipy.interpolate as S
import numpy as np
import glob, os, re, sys
import subprocess as sp
from .npfile import *
try:
    from scipy.integrate import simps
except:
    from scipy.integrate import simpson as simps


[docs]def selectField(obj, field, labTex=True, ic=False): """ This function selects for you which field you want to display. It actually allows to avoid possible variables miss-spelling: i.e. 'Bphi'='bp'='Bp'='bphi' :param obj: a graphic output file :type obj: :py:class:`magic.MagicGraph` :param field: the name of the field one wants to select :type field: str :param labTex: when set to True, format the labels using LaTeX fonts :type labTex: bool :returns: a tuple that contains the selected physical field and its label :rtype: (numpy.ndarray, str) """ if field in ('Bp', 'bp', 'bphi', 'Bphi'): data = obj.Bphi if labTex: label = r'$B_{\phi}$' else: label = 'Bphi' if ic: data_ic = obj.Bphi_ic else: data_ic = None elif field in ('Bt', 'bt', 'btheta', 'Btheta'): data = obj.Btheta if labTex: label = r'$B_{\theta}$' else: label = 'Btheta' if ic: data_ic = obj.Btheta_ic else: data_ic = None elif field in ('Br', 'br'): data = obj.Br if labTex: label = r'$B_r$' else: label = 'Br' if ic: data_ic = obj.Br_ic else: data_ic = None elif field in ('pressure', 'pre', 'Pre', 'Pressure', 'press', 'Press'): data = obj.pre if labTex: label = r'$p$' else: label = 'p' data_ic = None elif field in ('composition', 'xi', 'Xi', 'Comp', 'comp', 'chem', 'Chem'): data = obj.xi if labTex: label = r'$\xi$' else: label = 'xi' data_ic = None elif field in ('phase', 'Phase'): data = obj.phase if labTex: label = r'$\Phi$' else: label = 'phi' data_ic = None elif field in ('Vr', 'vr', 'Ur', 'ur'): data = obj.vr if labTex: label = r'$v_r$' else: label = 'vr' data_ic = None elif field in ('Vtheta', 'vtheta', 'Utheta', 'utheta', 'vt', 'Vt', 'Ut', 'ut'): data = obj.vtheta if labTex: label = r'$v_{\theta}$' else: label = 'vtheta' data_ic = None elif field in ('Vphi', 'vphi', 'Uphi', 'uphi', 'up', 'Up', 'Vp', 'vp'): data = obj.vphi if labTex: label = r'$v_{\phi}$' else: label = 'vphi' data_ic = None elif field in ('entropy', 's', 'S'): data = obj.entropy label = 'Entropy' data_ic = None elif field in ('temperature', 't', 'T', 'temp'): data = obj.entropy label = 'Temperature' data_ic = None elif field == 'u2': data = obj.vphi**2+obj.vr**2+obj.vtheta**2 if labTex: label = r'$u^2$' else: label = 'u2' data_ic = None elif field in ('nrj'): temp0, rho0, beta = anelprof(obj.radius, obj.strat, obj.polind) data = 1./2.*rho0*(obj.vphi**2+obj.vr**2+obj.vtheta**2) if labTex: label = r'$E_{\hbox{kin}}$' else: label = 'Ekin' data_ic = None elif field in ('b2', 'B2', 'Emag', 'em', 'Em', 'emag'): data = obj.Bphi**2+obj.Br**2+obj.Btheta**2 if labTex: label = r'$B^2$' else: label = 'B2' if ic: data_ic = obj.Bphi_ic**2+obj.Br_ic**2+obj.Btheta_ic**2 else: data_ic = None elif field in ('vrconv', 'vrc'): data = obj.vr-obj.vr.mean(axis=0) if labTex: label = r'$v_{r}$ conv' else: label = 'vr conv' data_ic = None elif field in ('vtconv', 'vtc'): data = obj.vtheta-obj.vtheta.mean(axis=0) if labTex: label = r'$v_{\theta}$ conv' else: label = 'vt conv' data_ic = None elif field in ('vpconv', 'vpc'): data = obj.vphi-obj.vphi.mean(axis=0) if labTex: label = r'$v_{\phi}$ conv' else: label = 'vp conv' data_ic = None elif field == 'bpfluct': data = obj.Bphi-obj.Bphi.mean(axis=0) if labTex: label = r"$B_{\phi}'$" else: label = "Bp'" if ic: data_ic = obj.Bphi_ic-obj.Bphi_ic.mean(axis=0) else: data_ic = None elif field == 'brfluct': data = obj.Br-obj.Br.mean(axis=0) if labTex: label = r"$B_r'$" else: label = "Br'" if ic: data_ic = obj.Br_ic-obj.Br_ic.mean(axis=0) else: data_ic = None elif field == 'entropyfluct': data = obj.entropy-obj.entropy.mean(axis=0) if labTex: label = r"$s'$" else: label = "s'" data_ic = None elif field in ('tfluct', 'Tfluct'): data = obj.entropy-obj.entropy.mean(axis=0) if labTex: label = r"$T'$" else: label = "T'" data_ic = None elif field == 'xifluct': data = obj.xi-obj.xi.mean(axis=0) if labTex: label = r"$\xi'$" else: label = "xi'" data_ic = None elif field == 'prefluct': data = obj.pre-obj.pre.mean(axis=0) if labTex: label = r"$p'$" else: label = "p'" data_ic = None elif field == 'vrea': data = np.zeros_like(obj.vr) for i in range(obj.ntheta): data[:, i, :] = (obj.vr[:, i, :]-obj.vr[:, -i-1, :])/2. if labTex: label = r'$v_{r}$ ea' else: label = 'vr ea' data_ic = None elif field == 'vra': data = np.zeros_like(obj.vr) for i in range(obj.ntheta): data[:, i, :] = (obj.vr[:, i, :]+obj.vr[:, -i-1, :])/2. if labTex: label = r'$v_{r}$ es' else: label = 'vr es' data_ic = None elif field == 'vpea': data = np.zeros_like(obj.vr) for i in range(obj.ntheta): data[:, i, :] = (obj.vphi[:, i, :]-obj.vphi[:, -i-1, :])/2. if labTex: label = r'$v_{\phi}$ ea' else: label = r'vp ea' data_ic = None elif field == 'vpa': data = np.zeros_like(obj.vr) for i in range(obj.ntheta): data[:, i, :] = (obj.vphi[:, i, :]+obj.vphi[:, -i-1, :])/2. if labTex: label = r'$v_{\phi}$ es' else: label = r'vp es' data_ic = None elif field == 'tea': data = np.zeros_like(obj.vr) for i in range(obj.ntheta): data[:, i, :] = (obj.entropy[:, i, :]-obj.entropy[:, -i-1, :])/2. if labTex: label = r'$T$ ea' else: label = r'T ea' data_ic = None return data, data_ic, label
[docs]def avgField(time, field, tstart=None, std=False, fix_missing_series=False, tstop=None): """ This subroutine computes the time-average (and the std) of a time series >>> ts = MagicTs(field='misc', iplot=False, all=True) >>> nuavg = avgField(ts.time, ts.topnuss, 0.35) >>> print(nuavg) :param time: time :type time: numpy.ndarray :param field: the time series of a given field :type field: numpy.ndarray :param tstart: the starting time of the averaging :type tstart: float :param tstart: the stopping time of the averaging :type tstart: float :param std: when set to True, the standard deviation is also calculated :type std: bool :param fix_missing_series: when set to True, data equal to zero are ignored, this is done in case new columns have been added to the time series :type fix_missing_series: bool :returns: the time-averaged quantity :rtype: float """ if tstart is not None: mask = np.where(abs(time-tstart) == min(abs(time-tstart)), 1, 0) ind1 = np.nonzero(mask)[0][0] else: # the whole input array is taken! ind1 = 0 if tstop is not None: mask = np.where(abs(time-tstop) == min(abs(time-tstop)), 1, 0) ind2 = np.nonzero(mask)[0][0] else: # the whole input array is taken! ind2 = -1 # Now suppose data were not stored in the first part of the run # then the time series could look like 0,0,0,...,data,data,data # In that case starting index needs to be overwritten if fix_missing_series and len(field.shape) == 1 and field[ind1] == 0: mask = np.where(field != 0, 1, 0) mask = np.nonzero(mask)[0] if len(mask) > 0: ind_tmp = mask[0] if ind_tmp > ind1: ind1 = ind_tmp if time[ind1:ind2].shape[0] == 1: # Only one entry in the array if len(field.shape) > 1: avgField = field[ind1, ...] stdField = np.zeros_like(avgField) else: avgField = field[ind1] stdField = 0 else: if time[ind1] == time[ind2]: # Same time: this can happen for timestep.TAG if len(field.shape) > 1: avgField = field[-1, ...] stdField = np.zeros_like(avgField) else: avgField = field[-1] stdField = 0 else: fac = 1./(time[ind2]-time[ind1]) avgField = fac*np.trapz(field[ind1:ind2, ...], time[ind1:ind2], axis=0) if std: stdField = np.sqrt(fac*np.trapz((field[ind1:ind2, ...]-avgField)**2, time[ind1:ind2], axis=0)) if std: return avgField, stdField else: return avgField
[docs]def writeVpEq(par, tstart): """ This function computes the time-averaged surface zonal flow (and Rolc) and format the output >>> # Reads all the par.* files from the current directory >>> par = MagicTs(field='par', iplot=False, all=True) >>> # Time-average >>> st = writeVpEq(par, tstart=2.1) >>> print(st) :param par: a :py:class:`MagicTs <magic.MagicTs>` object containing the par file :type par: :py:class:`magic.MagicTs` :param tstart: the starting time of the averaging :type tstart: float :returns: a formatted string :rtype: str """ mask = np.where(abs(par.time-tstart) == min(abs(par.time-tstart)), 1, 0) ind = np.nonzero(mask)[0][0] fac = 1./(par.time.max()-par.time[ind]) avgReEq = fac*np.trapz(par.reEquat[ind:], par.time[ind:]) roEq = avgReEq*par.ek*(1.-par.radratio) avgRolC = fac*np.trapz(par.rolc[ind:], par.time[ind:]) st = '{:10.3e}{:5.2f}{:6.2f}{:11.3e}{:11.3e}{:11.3e}'.format(par.ek, par.strat, par.pr, par.ra, roEq, avgRolC) return st
[docs]def progressbar(it, prefix="", size=60): """ Fancy progress-bar for loops .. code-block:: python for i in progressbar(range(1000000)): x = i :type it: iterator :param prefix: prefix string before progress bar :type prefix: str :param size: width of the progress bar (in points of xterm width) :type size: int :type size: int """ count = len(it) def _show(_i): x = int(size*_i/count) sys.stdout.write("{}[{}{}] {}/{}\r".format(prefix, "#"*x, "."*(size-x), _i, count)) sys.stdout.flush() _show(0) for i, item in enumerate(it): yield item _show(i+1) sys.stdout.write("\n") sys.stdout.flush()
[docs]def scanDir(pattern, tfix=None): """ This function sorts the files which match a given input pattern from the oldest to the most recent one (in the current working directory) >>> dat = scanDir('log.*') >>> print(log) :param pattern: a classical regexp pattern :type pattern: str :param tfix: in case you want to add only the files that are more recent than a certain date, use tfix (computer 1970 format!!) :type tfix: float :returns: a list of files that match the input pattern :rtype: list """ dat = [(os.stat(i).st_mtime, i) for i in glob.glob(pattern)] dat.sort() if tfix is not None: out = [] for i in dat: if i[0] > tfix: out.append(i[1]) else: out = [i[1] for i in dat] return out
[docs]def symmetrize(data, ms, reversed=False): """ Symmetrise an array which is defined only with an azimuthal symmetry minc=ms :param data: the input array :type data: numpy.ndarray :param ms: the azimuthal symmetry :type ms: int :param reversed: set to True, in case the array is reversed (i.e. n_phi is the last column) :type reversed: bool :returns: an output array of dimension (data.shape[0]*ms+1) :rtype: numpy.ndarray """ if reversed: nphi = data.shape[-1]*ms+1 size = [nphi] size.insert(0,data.shape[-2]) if len(data.shape) == 3: size.insert(0,data.shape[-3]) out = np.zeros(size, dtype=data.dtype) for i in range(ms): out[..., i*data.shape[-1]:(i+1)*data.shape[-1]] = data out[..., -1] = out[..., 0] else: nphi = data.shape[0]*ms +1 size = [nphi] if len(data.shape) >= 2: size.append(data.shape[1]) if len(data.shape) == 3: size.append(data.shape[2]) out = np.zeros(size, dtype=data.dtype) for i in range(ms): out[i*data.shape[0]:(i+1)*data.shape[0], ...] = data out[-1, ...] = out[0, ...] return out
[docs]def fast_read(file, skiplines=0, binary=False, precision=np.float64): """ This function reads an input ascii table (can read both formatted or unformatted fortran) >>> # Read 'e_kin.test', skip the first 10 lines >>> data = fast_read('e_kin.test', skiplines=10) :param file: name of the input file :type file: str :param skiplines: number of header lines to be skept during reading :type skiplines: int :param binary: when set to True, try to read an unformatted binray Fortran file (default is False) :type binary: bool :param precision: single (np.float32) or double precision (np.float64) :type precision: str :returns: an array[nlines, ncols] that contains the data of the ascii file :rtype: numpy.ndarray """ if not binary: f = open(file, 'r') X = [] for k, line in enumerate(f.readlines()): st = line.replace('D', 'E') if k >= skiplines: X.append(st.split()) X = np.array(X, dtype=precision) f.close() else: f = npfile(file, endian='B') X = [] while 1: try: X.append(f.fort_read(precision)) except TypeError: break X = np.array(X, dtype=precision) f.close() return X
[docs]def anelprof(radius, strat, polind, g0=0., g1=0., g2=1.): """ This functions calculates the reference temperature and density profiles of an anelastic model. >>> rad = chebgrid(65, 1.5, 2.5) >>> temp, rho, beta = anelprof(rad, strat=5., polind=2.) :param radius: the radial gridpoints :type radius: numpy.ndarray :param polind: the polytropic index :type polind: float :param strat: the number of the density scale heights between the inner and the outer boundary :type strat: float :param g0: gravity profile: g=g0 :type g0: float :param g1: gravity profile: g=g1*r/r_o :type g1: float :param g2: gravity profile: g=g2*(r_o/r)**2 :type g2: float :returns: a tuple that contains the temperature profile, the density profile and the log-derivative of the density profile versus radius :rtype: (numpy.ndarray, numpy.ndarray, numpy.ndarray) """ if radius[-1] < radius[0]: ro = radius[0] radratio = radius[-1]/radius[0] else: ro = radius[-1] radratio = radius[0]/radius[-1] grav = g0 + g1 * radius/ro + g2 * (ro/radius)**2 ofr=( np.exp(strat/polind)-1. )/ ( g0+0.5*g1*(1.+radratio) +g2/radratio ) temp0 = -ofr*( g0*radius +0.5*g1*radius**2/ro-g2*ro**2/radius ) + \ 1.+ ofr*ro*(g0+0.5*g1-g2) rho0 = temp0**polind beta = -ofr*grav*polind/temp0 return temp0, rho0, beta
[docs]def fd_grid(nr, a, b, fd_stretching=0.3, fd_ratio=0.1): """ This function defines a stretched grid between a and b >>> r_icb = 0.5 ; r_cmb = 1.5; n_r_max=64 >>> rr = fd_grid(n_r_max, r_cmb, r_icb) :param nr: number of radial grid points :type nr: int :param a: upper boundary of the grid :type a: float :param b: lower boundary of the grid :type b: float :param fd_stretching: fraction of points in the bulk :type fd_stretching: float :param fd_ratio: ratio of minimum to maximum spacing :type fd_ratio: float :returns: the radial grid :returns: the radial grid :rtype: numpy.ndarray """ ratio1 = fd_stretching ratio2 = fd_ratio if abs(a-b-1.0) > 1.0e-12: sys.exit('Not implemented yet') rr = np.zeros(nr, dtype=np.float64) rr[0] = a if ratio2 == 1.0: # Regular grid dr_before = (a-b)/(float(nr)-1.) dr_after = dr_before for i in range(1, nr): rr[i]=rr[i-1]-dr_before else: n_boundary_points = int( float(nr-1)/(2.*(1+ratio1)) ) ratio1 = float(nr-1)/float(2.*n_boundary_points) -1. n_bulk_points = nr-1-2*n_boundary_points dr_after = np.exp(np.log(ratio2)/float(n_boundary_points)) dr_before = 1. for i in range(n_boundary_points): dr_before = dr_before*dr_after dr_before = 1./(float(n_bulk_points)+ \ 2.*dr_after*((1-dr_before)/(1.-dr_after))) for i in range(n_boundary_points): dr_before = dr_before*dr_after for i in range(1, n_boundary_points+1): rr[i] = rr[i-1]-dr_before dr_before = dr_before/dr_after for i in range(n_bulk_points): rr[n_boundary_points+1+i] = rr[n_boundary_points+i]-dr_before for i in range(n_boundary_points): dr_before = dr_before*dr_after rr[n_boundary_points+1+n_bulk_points+i] = \ rr[n_boundary_points+n_bulk_points+i]-dr_before return rr
[docs]def chebgrid(nr, a, b): """ This function defines a Gauss-Lobatto grid from a to b. >>> r_icb = 0.5 ; r_cmb = 1.5; n_r_max=65 >>> rr = chebgrid(n_r_max, r_icb, r_cmb) :param nr: number of radial grid points plus one (Nr+1) :type nr: int :param a: lower limit of the Gauss-Lobatto grid :type a: float :param b: upper limit of the Gauss-Lobatto grid :type b: float :returns: the Gauss-Lobatto grid :rtype: numpy.ndarray """ rst = (a+b)/(b-a) rr = 0.5*(rst+np.cos(np.pi*(1.-np.arange(nr+1.)/nr)))*(b-a) return rr
[docs]def matder(nr, z1, z2): """ This function calculates the derivative in Chebyshev space. >>> r_icb = 0.5 ; r_cmb = 1.5; n_r_max=65 >>> d1 = matder(n_r_max, r_icb, r_cmb) >>> # Chebyshev grid and data >>> rr = chebgrid(n_r_max, r_icb, r_cmb) >>> f = sin(rr) >>> # Radial derivative >>> df = dot(d1, f) :param nr: number of radial grid points :type nr: int :param z1: lower limit of the Gauss-Lobatto grid :type z1: float :param z2: upper limit of the Gauss-Lobatto grid :type z2: float :returns: a matrix of dimension (nr,nr) to calculate the derivatives :rtype: numpy.ndarray """ nrp = nr+1 w1 = np.zeros((nrp, nrp), dtype=np.float64) zl = z2-z1 for i in range(nrp): for j in range(nrp): w1[i, j] = spdel(i, j, nr, zl) return w1
[docs]def intcheb(f, nr, z1, z2): """ This function integrates an input function f defined on the Gauss-Lobatto grid. >>> print(intcheb(f, 65, 0.5, 1.5)) :param f: an input array :type: numpy.ndarray :param nr: number of radial grid points :type nr: int :param z1: lower limit of the Gauss-Lobatto grid :type z1: float :param z2: upper limit of the Gauss-Lobatto grid :type z2: float :returns: the integrated quantity :rtype: float """ func = lambda i, j: 2.*np.cos(np.pi*i*j/nr)/nr w1 = np.fromfunction(func, (nr+1, nr+1)) w1[:, 0] = w1[:, 0]/2. w1[:, nr] = w1[:, nr]/2. w1[0, :] = w1[0, :]/2. w1[nr, :] = w1[nr, :]/2. w2 = np.dot(w1, f) int = 0. for i in range(0, nr+1, 2): int = int-(z2-z1)/(i**2-1)*w2[i] return int
def spdel(kr, jr, nr, zl): if kr != nr : fac = 1. k = kr j = jr else: fac = -1. k = 0. j = nr-jr spdel = fac*dnum(k, j, nr)/den(k, j, nr) return -spdel*(2./zl) def dnum(k, j, nr): if k == 0: if (j == 0 or j == nr): dnum = 0.5 a = nr % 2 if a == 1: dnum = -dnum if j == 0: dnum = 1./3.*float(nr*nr)+1./6. return dnum dnum = 0.5*(float(nr)+0.5)*((float(nr)+0.5)+(1./np.tan(np.pi*float(j) \ /float(2.*nr)))**2)+1./8.-0.25/(np.sin(np.pi*float(j)/ \ float(2*nr))**2) - 0.5*float(nr*nr) return dnum dnum = ff(k+j, nr)+ff(k-j, nr) return dnum def ff(i, nr): if i == 0: return 0 ff = float(nr)*0.5/np.tan(np.pi*float(i)/float(2.*nr)) a = i % 2 if a == 0: ff = -ff return ff def den(k, j, nr): if k == 0: den = 0.5*float(nr) a = j % 2 if a == 1: den = -den if (j == 0 or j == nr): den = 1. return den den = float(nr)*np.sin(np.pi*float(k)/float(nr)) if (j == 0 or j == nr): den = 2.*den return den
[docs]def timeder(time, y): """ This routine computes the time derivative of an input array >>> ts = MagicTs(field='e_kin') >>> dEkdt = timeder(ts, ts.ekin_pol) :param time: an array that contains time :type time: numpy.ndarray :param y: an array which contains the field to be differentiated :type y: numpy.ndarray :returns: an array that contains the time derivative :rtype: numpy.ndarray """ out = np.gradient(y, time, edge_order=1) return out
[docs]def secondtimeder(time, y): """ This routine computes the second time derivative of an input array >>> ts = MagicTs(field='e_kin') >>> d2Ekdt2 = secondtimeder(ts, ts.ekin_pol) :param time: an array that contains time :type time: numpy.ndarray :param y: an array which contains the field to be differentiated two times :type y: numpy.ndarray :returns: an array that contains the second time derivative :rtype: numpy.ndarray """ tmp = np.gradient(y, time, edge_order=1) out = np.gradient(tmp, time, edge_order=1) return out
[docs]def phideravg(data, minc=1, order=4): """ phi-derivative of an input array >>> gr = MagicGraph() >>> dvphidp = phideravg(gr.vphi, minc=gr.minc) :param data: input array :type data: numpy.ndarray :param minc: azimuthal symmetry :type minc: int :param order: order of the finite-difference scheme (possible values are 2 or 4) :type order: int :returns: the phi-derivative of the input array :rtype: numpy.ndarray """ nphi = data.shape[0] dphi = 2.*np.pi/minc/(nphi-1.) if order == 2: der = (np.roll(data, -1, axis=0)-np.roll(data, 1, axis=0))/(2.*dphi) der[0, ...] = (data[1, ...]-data[-2, ...])/(2.*dphi) der[-1, ...] = der[0, ...] elif order == 4: der = ( -np.roll(data,-2,axis=0) \ +8.*np.roll(data,-1,axis=0) \ -8.*np.roll(data, 1,axis=0) \ +np.roll(data, 2,axis=0) )/(12.*dphi) der[1, ...] = (-data[3, ...]+8.*data[2, ...]-\ 8.*data[0, ...] +data[-2, ...])/(12.*dphi) der[-2, ...] = (-data[0, ...]+8.*data[-1, ...]-\ 8.*data[-3, ...]+data[-4, ...])/(12.*dphi) der[0, ...] = (-data[2, ...]+8.*data[1, ...]-\ 8.*data[-2, ...] +data[-3, ...])/(12.*dphi) der[-1, ...] = der[0, ...] return der
[docs]def rderavg(data, rad, exclude=False): """ Radial derivative of an input array >>> gr = MagiGraph() >>> dvrdr = rderavg(gr.vr, gr.radius) :param data: input array :type data: numpy.ndarray :param rad: radial grid :type rad: numpy.ndarray :param exclude: when set to True, exclude the first and last radial grid points and replace them by a spline extrapolation (default is False) :type exclude: bool :returns: the radial derivative of the input array :rtype: numpy.ndarray """ r1 = rad[0] r2 = rad[-1] nr = data.shape[-1] grid = chebgrid(nr-1, r1, r2) tol = 1e-6 # This is to determine whether Cheb der will be used diff = abs(grid-rad).max() if diff > tol: spectral = False grid = rad else: spectral = True if exclude: g = grid[::-1] gnew = np.linspace(r2, r1, 1000) if len(data.shape) == 2: for i in range(data.shape[0]): val = data[i, ::-1] tckp = S.splrep(g[1:-1], val[1:-1]) fnew = S.splev(gnew, tckp) data[i, 0] = fnew[-1] data[i, -1] = fnew[0] else: for j in range(data.shape[0]): for i in range(data.shape[1]): val = data[j, i, ::-1] tckp = S.splrep(g[1:-1], val[1:-1]) fnew = S.splev(gnew, tckp) data[j, i, 0] = fnew[-1] data[j, i, -1] = fnew[0] if spectral: d1 = matder(nr-1, r1, r2) if len(data.shape) == 1: der = np.dot(d1, data) elif len(data.shape) == 2: der = np.tensordot(data, d1, axes=[1, 1]) else: der = np.tensordot(data, d1, axes=[2, 1]) else: denom = np.roll(grid, -1) - np.roll(grid, 1) denom[0] = grid[1]-grid[0] denom[-1] = grid[-1]-grid[-2] der = (np.roll(data, -1, axis=-1)-np.roll(data, 1, axis=-1))/denom der[..., 0] = (data[..., 1]-data[..., 0])/(grid[1]-grid[0]) der[..., -1] = (data[..., -1]-data[..., -2])/(grid[-1]-grid[-2]) return der
[docs]def thetaderavg(data, order=4): """ Theta-derivative of an input array (finite differences) >>> gr = MagiGraph() >>> dvtdt = thetaderavg(gr.vtheta) :param data: input array :type data: numpy.ndarray :param order: order of the finite-difference scheme (possible values are 2 or 4) :type order: int :returns: the theta-derivative of the input array :rtype: numpy.ndarray """ if len(data.shape) == 3: # 3-D ntheta = data.shape[1] dtheta = np.pi/(ntheta-1.) if order == 2: der = (np.roll(data, -1, axis=1)-np.roll(data, 1, axis=1))/(2.*dtheta) der[:, 0, :] = (data[:, 1, :]-data[:, 0, :])/dtheta der[:, -1, :] = (data[:, -1, :]-data[:, -2, :])/dtheta elif order == 4: der = ( -np.roll(data,-2,axis=1) \ +8.*np.roll(data,-1,axis=1) \ -8.*np.roll(data, 1,axis=1) \ +np.roll(data, 2,axis=1) )/(12.*dtheta) der[:, 1, :] = (data[:, 2, :]-data[:, 0, :])/(2.*dtheta) der[:, -2, :] = (data[:, -1, :]-data[:, -3, :])/(2.*dtheta) der[:, 0, :] = (data[:, 1, :]-data[:, 0, :])/dtheta der[:, -1, :] = (data[:, -1, :]-data[:, -2, :])/dtheta elif len(data.shape) == 2: #2-D ntheta = data.shape[0] dtheta = np.pi/(ntheta-1.) if order == 2: der = (np.roll(data, -1, axis=0)-np.roll(data, 1, axis=0))/(2.*dtheta) der[0, :] = (data[1, :]-data[0, :])/dtheta der[-1, :] = (data[-1, :]-data[-2, :])/dtheta elif order == 4: der = (-np.roll(data,-2,axis=0)+8.*np.roll(data,-1,axis=0)-\ 8.*np.roll(data,1,axis=0)+np.roll(data,2,axis=0))/(12.*dtheta) der[1, :] = (data[2, :]-data[0, :])/(2.*dtheta) der[-2, :] = (data[-1, :]-data[-3, :])/(2.*dtheta) der[0, :] = (data[1, :]-data[0, :])/dtheta der[-1, :] = (data[-1, :]-data[-2, :])/dtheta return der
[docs]def zderavg(data, rad, colat=None, exclude=False): """ z derivative of an input array >>> gr = MagiGraph() >>> dvrdz = zderavg(gr.vr, gr.radius, colat=gr.colatitude) :param data: input array :type data: numpy.ndarray :param rad: radial grid :type rad: numpy.ndarray :param exclude: when set to True, exclude the first and last radial grid points and replace them by a spline extrapolation (default is False) :type exclude: bool :param colat: colatitudes (when not specified a regular grid is assumed) :type colat: numpy.ndarray :returns: the z derivative of the input array :rtype: numpy.ndarray """ if len(data.shape) == 3: # 3-D ntheta = data.shape[1] elif len(data.shape) == 2: # 2-D ntheta = data.shape[0] nr = data.shape[-1] if colat is not None: th = colat else: th = np.linspace(0., np.pi, ntheta) if len(data.shape) == 3: # 3-D thmD = np.zeros_like(data) for i in range(ntheta): thmD[:,i,:] = th[i] elif len(data.shape) == 2: # 2-D thmD = np.zeros((ntheta, nr), np.float64) for i in range(ntheta): thmD[i, :] = th[i] dtheta = thetaderavg(data) dr = rderavg(data, rad, exclude) dz = np.cos(thmD)*dr - np.sin(thmD)/rad*dtheta return dz
[docs]def sderavg(data, rad, colat=None, exclude=False): """ s derivative of an input array >>> gr = MagiGraph() >>> dvpds = sderavg(gr.vphi, gr.radius, colat=gr.colatitude) :param data: input array :type data: numpy.ndarray :param rad: radial grid :type rad: numpy.ndarray :param exclude: when set to True, exclude the first and last radial grid points and replace them by a spline extrapolation (default is False) :type exclude: bool :param colat: colatitudes (when not specified a regular grid is assumed) :type colat: numpy.ndarray :returns: the s derivative of the input array :rtype: numpy.ndarray """ if len(data.shape) == 3: # 3-D ntheta = data.shape[1] elif len(data.shape) == 2: # 2-D ntheta = data.shape[0] nr = data.shape[-1] if colat is not None: th = colat else: th = np.linspace(0., np.pi, ntheta) if len(data.shape) == 3: # 3-D thmD = np.zeros_like(data) for i in range(ntheta): thmD[:,i,:] = th[i] elif len(data.shape) == 2: # 2-D thmD = np.zeros((ntheta, nr), np.float64) for i in range(ntheta): thmD[i, :] = th[i] dtheta = thetaderavg(data) dr = rderavg(data, rad, exclude) ds = np.sin(thmD)*dr + np.cos(thmD)/rad*dtheta return ds
[docs]def cylSder(radius, data, order=4): """ This function computes the s derivative of an input array defined on a regularly-spaced cylindrical grid. >>> s = linspace(0., 1., 129 ; dat = cos(s) >>> ddatds = cylSder(s, dat) :param radius: cylindrical radius :type radius: numpy.ndarray :param data: input data :type data: numpy.ndarray :param order: order of the finite-difference scheme (possible values are 2 or 4) :type order: int :returns: s derivative :rtype: numpy.ndarray """ ns = data.shape[-1] ds = (radius[-1]-radius[0])/(ns-1.) if order == 2: der = (np.roll(data, -1, axis=-1)-np.roll(data, 1, axis=-1))/(2.*ds) der[..., 0] = (data[..., 1]-data[..., 0])/ds der[..., -1] = (data[..., -1]-data[..., -2])/ds elif order == 4: der = ( -np.roll(data,-2,axis=-1) \ +8.*np.roll(data,-1,axis=-1) \ -8.*np.roll(data, 1,axis=-1) \ +np.roll(data, 2,axis=-1) )/(12.*ds) der[..., 1] = (data[..., 2]-data[..., 0])/(2.*ds) der[..., -2] = (data[..., -1]-data[..., -3])/(2.*ds) der[..., 0] = (data[..., 1]-data[..., 0])/ds der[..., -1] = (data[..., -1]-data[..., -2])/ds return der
[docs]def cylZder(z, data): """ This function computes the z derivative of an input array defined on a regularly-spaced cylindrical grid. >>> z = linspace(-1., 1., 129 ; dat = cos(z) >>> ddatdz = cylZder(z, dat) :param z: height of the cylinder :type z: numpy.ndarray :param data: input data :type data: numpy.ndarray :returns: z derivative :rtype: numpy.ndarray """ nz = data.shape[1] dz = (z[-1]-z[0])/(nz-1.) der = (np.roll(data, -1, axis=1)-np.roll(data, 1, axis=1))/(2.*dz) der[:, 0, :] = (data[:, 1, :]-data[:, 0, :])/dz der[:, -1, :] = (data[:, -1, :]-data[:, -2, :])/dz return der
[docs]def getCpuTime(file): """ This function calculates the CPU time from one given log file :param file: the log file you want to analyze :type file: file :returns: the total CPU time :rtype: float """ threads_old = re.compile(r'[\s]*\![\s]*nThreads\:[\s]*(.*)') threads_new = re.compile(r'[\s]*\![\s]*Number of OMP threads\:[\s]*(.*)') ranks = re.compile(r'[\s]*\![\s\w]*ranks[\s\w]*\:[\s]*(.*)') runTime = re.compile(r'[\s\!\w]*time:[\s]*([0-9]*)d[\s\:]*([0-9]*)h[\s\:]*([0-9]*)m[\s\:]*([0-9]*)s[\s\:]*([0-9]*)ms.*') runTime_new = re.compile(r' \! Total run time:[\s]*([0-9]*)[\s]*h[\s]*([0-9]*)[\s]*m[\s]*([0-9]*)[\s]*s[\s]*([0-9]*)[\s]*ms[\s]*') f = open(file, 'r') tab = f.readlines() nThreads = 1 # In case a pure MPI version is used nRanks = 1 # In case the old OpenMP version is used realTime = 0. for line in tab: if threads_old.match(line): nThreads = int(threads_old.search(line).groups()[0]) elif threads_new.match(line): nThreads = int(threads_new.search(line).groups()[0]) elif ranks.match(line): nRanks = int(ranks.search(line).groups()[0]) elif runTime.match(line): days = int(runTime.search(line).groups()[0]) hours = int(runTime.search(line).groups()[1]) min = int(runTime.search(line).groups()[2]) sec = int(runTime.search(line).groups()[3]) ms = int(runTime.search(line).groups()[4]) realTime = 24*days+hours+1./60*min+1./3600*sec+1./3.6e6*ms elif runTime_new.match(line): hours = int(runTime_new.search(line).groups()[0]) min = int(runTime_new.search(line).groups()[1]) sec = int(runTime_new.search(line).groups()[2]) ms = int(runTime_new.search(line).groups()[3]) realTime = hours+1./60*min+1./3600*sec+1./3.6e6*ms f.close() cpuTime = nThreads*nRanks*realTime return cpuTime
[docs]def ReadBinaryTimeseries(infile, ncols, datatype='f8', endianness='>'): """ This function reads binary timeseries. It is then faster than the fast_read function. :param infile: the file to read :type infile: string :param ncols: number of columns of the file :type ncols: int :param datatype: 'f8' = 64-bit floating-point number 'f4' = 32-bit floating-point number :type datatype: string :param endianness: '>' = big-endian ; '<' = small-endian :type endianness: string :returns: an array[nlines, ncols] that contains the data of the binary file :rtype: numpy.ndarray """ DUMM = endianness+'i4' FTYP = endianness+datatype size = os.path.getsize(infile) #nline = size/(2*4 + ncols*4) # line = 2*i4 + ncols*f4 typeG = np.dtype([('dum1',DUMM,1), ('line',FTYP,ncols), ('dum2',DUMM,1)]) with open(infile,'rb') as f: data = np.fromfile(f,dtype=typeG,count=size)['line'] return data
[docs]def getTotalRunTime(): """ This function calculates the total CPU time of one run directory :returns: the total RUN time :rtype: float """ logFiles = glob.glob('log.*') totCpuTime = 0 for file in logFiles: totCpuTime += getCpuTime(file) return totCpuTime
[docs]def prime_factors(n): """ This function returns all prime factors of a number :type n: int :returns: all prime factors :rtype: list """ i = 2 factors = [] while i * i <= n: if n % i: i += 1 else: n //= i factors.append(i) if n > 1: factors.append(n) return factors
[docs]def horizontal_mean(field, colat, std=False): """ This function computes the horizontal mean (and the standard deviation) of an input array of size (nphi,ntheta) or (nphi,ntheta,nr) :param field: the input array :type field: numpy.ndarray :param colat: an array that contains the colatitudes :type colat: numpy.ndarray :param std: a boolean if one also wants to compute the standard deviation :type std: bool :returns: the average value or radial profile :rtype: float """ field_m = field.mean(axis=0) # Azimuthal average field_mean = 0.5 * simps(field_m.T*np.sin(colat), x=colat, axis=-1) if std: dat = (field-field_mean)**2 dat_m = dat.mean(axis=0) dat_mean = 0.5 * simps(dat_m.T*np.sin(colat), x=colat, axis=-1) field_std = np.sqrt(dat_mean) return field_mean, field_std else: return field_mean
[docs]def cleanBIS(dir='.'): """ This function renames all files with a trailing '_BIS' in a given directory. :param dir: the working directory :type dir: str """ files = glob.glob('*_BIS') for file in files: cmd = 'mv {} {}'.format(file, file[:-4]) print(cmd) sp.call(cmd, shell=True, stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'))