# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
from magic import scanDir, MagicSetup, Movie, chebgrid, rderavg, AvgField
import os, pickle
try:
from scipy.integrate import simps
except:
from scipy.integrate import simpson as simps
json_model = {'phys_params': [],
'time_series': { 'heat': ['topnuss', 'botnuss']},
'spectra': {},
'radial_profiles': {}}
[docs]class ThetaHeat(MagicSetup):
r"""
This class allows to conduct some analysis of the latitudinal
variation of the heat transfer. It relies on the movie files
:ref:`ATmov.TAG <secMovieFile>` and :ref:`AHF_mov.TAG <secMovieFile>`.
As it's a bit time-consuming, the calculations are stored in a
python.pickle file to quicken future usage of the data.
Since this function is supposed to use time-averaged quantities, the usual
procedure is first to define the initial averaging time using
:py:class:`AvgField <magic.AvgField>`: (this needs to be done only once)
>>> a = AvgField(tstart=2.58)
Once the ``tInitAvg`` file exists, the latitudinal heat transfer analysis
can be done using:
>>> # For chunk-averages over 10^\degree in the polar and equatorial regions.
>>> th = ThetaHeat(angle=10)
>>> # Formatted output
>>> print(th)
"""
[docs] def __init__(self, iplot=False, angle=10, pickleName='thHeat.pickle',
quiet=False):
"""
:param iplot: a boolean to toggle the plots on/off
:type iplot: bool
:param angle: the integration angle in degrees
:type angle: float
:pickleName: calculations a
:param quiet: a boolean to switch on/off verbose outputs
:type quiet: bool
"""
angle = angle * np.pi / 180
if os.path.exists('tInitAvg'):
f = open('tInitAvg', 'r')
tstart = float(f.readline())
f.close()
logFiles = scanDir('log.*')
tags = []
for lg in logFiles:
nml = MagicSetup(quiet=True, nml=lg)
if nml.start_time > tstart:
if os.path.exists('ATmov.{}'.format(nml.tag)):
tags.append(nml.tag)
if len(tags) == 0:
tags = [nml.tag]
if not quiet:
print('Only 1 tag: {}'.format(tags))
MagicSetup.__init__(self, quiet=True, nml=logFiles[-1])
a = AvgField(model=json_model, write=False)
self.nuss = 0.5 * (a.topnuss_av+a.botnuss_av)
else:
logFiles = scanDir('log.*')
if len(logFiles) > 0:
MagicSetup.__init__(self, quiet=True, nml=logFiles[-1])
if not os.path.exists(pickleName):
# reading ATmov
k = 0
for tag in tags:
f = 'ATmov.{}'.format(tag)
if os.path.exists(f):
if k == 0:
m = Movie(file=f, iplot=False)
print(f)
else:
m += Movie(file=f, iplot=False)
print(f)
k += 1
# reading AHF_mov
kk = 0
for tag in tags:
f = 'AHF_mov.{}'.format(tag)
if os.path.exists(f):
if kk == 0:
m1 = Movie(file=f, iplot=False)
print(f)
else:
m1 += Movie(file=f, iplot=False)
print(f)
kk += 1
self.tempmean = m.data[0, ...].mean(axis=0)
self.tempstd = m.data[0, ...].std(axis=0)
self.colat = m.theta
if kk > 0: # i.e. at least one AHF_mov file has been found
self.fluxmean = m1.data[0, ...].mean(axis=0)
self.fluxstd = m1.data[0, ...].std(axis=0)
else:
self.fluxmean = rderavg(self.tempmean, m.radius, exclude=False)
self.fluxstd = rderavg(self.tempstd, m.radius, exclude=False)
# Pickle saving
try:
with open(pickleName, 'wb') as f:
pickle.dump([self.colat, self.tempmean, self.tempstd,
self.fluxmean, self.fluxstd], f)
except PermissionError:
print('No write access in the current directory')
else:
with open(pickleName, 'rb') as f:
dat = pickle.load(f)
if len(dat) == 5:
self.colat, self.tempmean, self.tempstd, \
self.fluxmean, self.fluxstd = dat
else:
self.colat, self.tempmean, self.fluxmean = dat
self.fluxstd = np.zeros_like(self.fluxmean)
self.tempstd = np.zeros_like(self.fluxmean)
self.ri = self.radratio/(1.-self.radratio)
self.ro = 1./(1.-self.radratio)
self.ntheta, self.nr = self.tempmean.shape
if not hasattr(self, 'radial_scheme') or \
(self.radial_scheme=='CHEB' and self.l_newmap==False):
# Redefine to get double precision
self.radius = chebgrid(self.nr-1, self.ro, self.ri)
else:
self.radius = m.radius
th2D = np.zeros((self.ntheta, self.nr), dtype=self.radius.dtype)
#self.colat = np.linspace(0., np.pi, self.ntheta)
for i in range(self.ntheta):
th2D[i, :] = self.colat[i]
self.temprmmean = 0.5*simps(self.tempmean*np.sin(th2D), x=th2D, axis=0)
self.temprmstd = 0.5*simps(self.tempstd*np.sin(th2D), x=th2D, axis=0)
sinTh = np.sin(self.colat)
# Conducting temperature profile (Boussinesq only!)
if self.ktops == 1 and self.kbots == 1:
self.tcond = self.ri*self.ro/self.radius-self.ri+self.temprmmean[0]
self.fcond = -self.ri*self.ro/self.radius**2
elif self.ktops == 1 and self.kbots != 1:
qbot = -1.
ttop = self.temprmmean[0]
self.fcond = -self.ri**2 / self.radius**2
self.tcond = self.ri**2/self.radius - self.ri**2/self.ro + ttop
else:
if os.path.exists('pscond.dat'):
dat = np.loadtxt('pscond.dat')
self.tcond = dat[:, 1]
self.fcond = rderavg(self.tcond, self.radius)
self.nusstopmean = self.fluxmean[:, 0] / self.fcond[0]
self.nussbotmean = self.fluxmean[:, -1] / self.fcond[-1]
self.nusstopstd = self.fluxstd[:, 0] / self.fcond[0]
self.nussbotstd = self.fluxstd[:, -1] / self.fcond[-1]
# Close to the equator
mask2D = (th2D>=np.pi/2.-angle/2.)*(th2D<=np.pi/2+angle/2.)
mask = (self.colat>=np.pi/2.-angle/2.)*(self.colat<=np.pi/2+angle/2.)
fac = 1./simps(sinTh[mask], x=self.colat[mask])
self.nussBotEq = fac*simps(self.nussbotmean[mask]*sinTh[mask], x=self.colat[mask])
self.nussTopEq = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
sinC = sinTh.copy()
sinC[~mask] = 0.
fac = 1./simps(sinC, x=self.colat)
tempC = self.tempmean.copy()
tempC[~mask2D] = 0.
self.tempEqmean = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
tempC = self.tempstd.copy()
tempC[~mask2D] = 0.
self.tempEqstd = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
dtempEq = rderavg(self.tempEqmean, self.radius)
self.betaEq = dtempEq[len(dtempEq)//2]
# 45\deg inclination
mask2D = (th2D>=np.pi/4.-angle/2.)*(th2D<=np.pi/4+angle/2.)
mask = (self.colat>=np.pi/4.-angle/2.)*(self.colat<=np.pi/4+angle/2.)
fac = 1./simps(np.sin(self.colat[mask]), x=self.colat[mask])
nussBot45NH = fac*simps(self.nussbotmean[mask]*sinTh[mask], x=self.colat[mask])
nussTop45NH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
sinC = sinTh.copy()
sinC[~mask] = 0.
fac = 1./simps(sinC, x=self.colat)
tempC = self.tempmean.copy()
tempC[~mask2D] = 0.
temp45NH = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
mask2D = (th2D>=3.*np.pi/4.-angle/2.)*(th2D<=3.*np.pi/4+angle/2.)
mask = (self.colat>=3.*np.pi/4.-angle/2.)*(self.colat<=3.*np.pi/4+angle/2.)
fac = 1./simps(np.sin(self.colat[mask]), x=self.colat[mask])
nussBot45SH = fac*simps(self.nussbotmean[mask]*sinTh[mask], x=self.colat[mask])
nussTop45SH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
sinC = sinTh.copy()
sinC[~mask] = 0.
fac = 1./simps(sinC, x=self.colat)
tempC = self.tempmean.copy()
tempC[~mask2D] = 0.
temp45SH = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
self.nussTop45 = 0.5*(nussTop45NH+nussTop45SH)
self.nussBot45 = 0.5*(nussBot45NH+nussBot45SH)
self.temp45 = 0.5*(temp45NH+temp45SH)
dtemp45 = rderavg(self.temp45, self.radius)
self.beta45 = dtemp45[len(dtemp45)//2]
# Polar regions
mask2D = (th2D<=angle/2.)
mask = (self.colat<=angle/2.)
fac = 1./simps(np.sin(self.colat[mask]), x=self.colat[mask])
nussBotPoNH = fac*simps(self.nussbotmean[mask]*sinTh[mask], x=self.colat[mask])
nussTopPoNH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
sinC = sinTh.copy()
sinC[~mask] = 0.
fac = 1./simps(sinC, x=self.colat)
tempC = self.tempmean.copy()
tempC[~mask2D] = 0.
tempPolNHmean = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
tempC = self.tempstd.copy()
tempC[~mask2D] = 0.
tempPolNHstd = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
mask2D = (th2D>=np.pi-angle/2.)
mask = (self.colat>=np.pi-angle/2.)
fac = 1./simps(np.sin(self.colat[mask]), self.colat[mask])
nussBotPoSH = fac*simps(self.nussbotmean[mask]*sinTh[mask], x=self.colat[mask])
nussTopPoSH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
sinC = sinTh.copy()
sinC[~mask] = 0.
fac = 1./simps(sinC, x=self.colat)
tempC = self.tempmean.copy()
tempC[~mask2D] = 0.
tempPolSHmean = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
tempC = self.tempstd.copy()
tempC[~mask2D] = 0.
tempPolSHstd = fac*simps(tempC*np.sin(th2D), x=th2D, axis=0)
self.nussBotPo = 0.5*(nussBotPoNH+nussBotPoSH)
self.nussTopPo = 0.5*(nussTopPoNH+nussTopPoSH)
self.tempPolmean = 0.5*(tempPolNHmean+tempPolSHmean)
self.tempPolstd= 0.5*(tempPolNHstd+tempPolSHstd)
dtempPol = rderavg(self.tempPolmean, self.radius)
self.betaPol = dtempPol[len(dtempPol)//2]
# Inside and outside TC
angleTC = np.arcsin(self.ri/self.ro)
mask2D = (th2D<=angleTC)
mask = (self.colat<=angleTC)
fac = 1./simps(np.sin(self.colat[mask]), x=self.colat[mask])
nussITC_NH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
mask2D = (th2D>=np.pi-angleTC)
mask = (self.colat>=np.pi-angleTC)
fac = 1./simps(np.sin(self.colat[mask]), self.colat[mask])
nussITC_SH = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
self.nussITC = 0.5*(nussITC_NH+nussITC_SH)
mask2D = (th2D>=angleTC)*(th2D<=np.pi-angleTC)
mask = (self.colat>=angleTC)*(self.colat<=np.pi-angleTC)
fac = 1./simps(sinTh[mask], self.colat[mask])
self.nussOTC = fac*simps(self.nusstopmean[mask]*sinTh[mask], x=self.colat[mask])
if iplot:
self.plot()
if not quiet:
print(self)
[docs] def __str__(self):
"""
Formatted outputs
>>> th = ThetaHeat()
>>> print(th)
"""
if self.ek == -1:
ek = 0. # to avoid the -1 for the non-rotating cases
else:
ek = self.ek
if self.mode == 0:
st ='{:9.3e}{:9.2e}{:9.2e}{:9.2e}{:5.2f}'.format(self.ra, ek,
self.pr, self.prmag, self.strat)
else:
st = '{:.3e}{:12.5e}{:5.2f}{:6.2f}{:6.2f}'.format(self.ra, ek,
self.strat, self.pr, self.radratio)
st += '{:12.5e}'.format(self.nuss)
st += '{:12.5e}{:12.5e}{:12.5e}'.format(self.nussBotEq, self.nussBot45,
self.nussBotPo)
st += '{:12.5e}{:12.5e}{:12.5e}'.format(self.nussTopEq, self.nussTop45,
self.nussTopPo)
st += ' {:12.5e} {:12.5e} {:12.5e}'.format(self.betaEq, self.beta45,
self.betaPol)
return st
[docs] def plot(self):
"""
Plotting function
"""
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(self.radius, self.tcond-self.tcond[0], 'k--', label='Cond. temp.')
ax.fill_between(self.radius,
self.temprmmean-self.temprmstd-self.temprmmean[0],
self.temprmmean+self.temprmstd-self.temprmmean[0],
alpha=0.2)
ax.plot(self.radius, self.temprmmean-self.temprmmean[0], ls='-', c='C0',
lw=2, label='Mean temp.')
ax.fill_between(self.radius,
self.tempEqmean-self.tempEqstd-self.temprmmean[0],
self.tempEqmean+self.tempEqstd-self.temprmmean[0],
alpha=0.2)
ax.plot(self.radius, self.tempEqmean-self.temprmmean[0],
ls='-.', c='C1', lw=2, label='Temp. equat')
ax.fill_between(self.radius,
self.tempPolmean-self.tempPolstd-self.temprmmean[0],
self.tempPolmean+self.tempPolstd-self.temprmmean[0],
alpha=0.2)
ax.plot(self.radius, self.tempPolmean-self.temprmmean[0],
ls='--', c='C2', lw=2, label='Temp. Pol')
ax.set_xlim(self.ri, self.ro)
ax.set_ylim(0., 1.)
ax.set_ylabel('T')
ax.set_xlabel('r')
ax.legend(loc='upper right', frameon=False)
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
ax1.fill_between(self.colat*180./np.pi, self.nusstopmean-self.nusstopstd,
self.nusstopmean+self.nusstopstd, alpha=0.2)
ax1.plot(self.colat*180./np.pi, self.nusstopmean, ls='-', color='C0',
lw=2, label='Top Nu')
ax1.fill_between(self.colat*180./np.pi, self.nussbotmean-self.nussbotstd,
self.nussbotmean+self.nussbotstd, alpha=0.2)
ax1.plot(self.colat*180./np.pi, self.nussbotmean, ls='--', c='C1',
lw=2, label='Bot Nu')
ax1.set_xlim(0., 180.)
ax1.set_ylabel('Nu')
ax1.set_xlabel('Theta')
ax1.legend(loc='upper right', frameon=False)
ax1.axvline(180./np.pi*np.arcsin(self.ri/self.ro), color='k', linestyle='--')
ax1.axvline(180-180./np.pi*np.arcsin(self.ri/self.ro), color='k', linestyle='--')
if __name__ == '__main__':
t = ThetaHeat(iplot=True)
plt.show()