Skip to content

Commit 5c3f826

Browse files
authored
Merge pull request #16 from PPMstar/code-comparison
Code comparison
2 parents c188afe + 40cf0d9 commit 5c3f826

File tree

1 file changed

+173
-58
lines changed

1 file changed

+173
-58
lines changed

ppmpy/ppm.py

Lines changed: 173 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -747,10 +747,13 @@ def compute_Xcld(self, fname, num_type='ndump'):
747747
Xcld = fv*rho_cld/rho
748748

749749
if self.__isRprofSet:
750-
fv = self.get('FV', fname, num_type=num_type, resolution='l')
751-
airmu = self.get('airmu', fname, num_type=num_type)
752-
cldmu = self.get('cldmu', fname, num_type=num_type)
753-
Xcld = fv/((1. - fv)*(airmu/cldmu) + fv)
750+
if 'FV' in self.get_dump(self.get_dump_list()[0]).get_lr_variables():
751+
fv = self.get('FV', fname, num_type=num_type, resolution='l')
752+
airmu = self.get('airmu', fname, num_type=num_type)
753+
cldmu = self.get('cldmu', fname, num_type=num_type)
754+
Xcld = fv/((1. - fv)*(airmu/cldmu) + fv)
755+
else:
756+
Xcld = self.get('X1', fname, num_type=num_type, resolution='l')
754757

755758
return Xcld
756759

@@ -1321,13 +1324,15 @@ def bound_rad(self, cycles, r_min, r_max, var='ut', \
13211324
if return_var_scale_height:
13221325
Hv = np.zeros(len(cycle_list))
13231326

1327+
# 'Y' is the default radial coordinate. 'R' is only used in rprof
1328+
# output from runs in spherical geometry (of the stratification, not of
1329+
# the grid).
1330+
rvar = 'Y'
1331+
if self.__isRprofSet and self.get_geometry() == 'spherical':
1332+
rvar = 'R'
13241333
# The grid is assumed to be static, so we get the radial
13251334
# scale only once at cycle_list[0].
1326-
if self.__isyprofile:
1327-
r = self.get('Y', cycle_list[0], resolution='l')
1328-
1329-
if self.__isRprofSet:
1330-
r = self.get('R', cycle_list[0], resolution='l')
1335+
r = self.get(rvar, cycle_list[0], resolution='l')
13311336

13321337
idx_r_min = np.argmin(np.abs(r - r_min))
13331338
idx_r_max = np.argmin(np.abs(r - r_max))
@@ -1475,20 +1480,32 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
14751480
rt = rb + offset*np.abs(Hv)
14761481
print('Done.')
14771482

1483+
geometry = self.get_geometry()
1484+
1485+
# 'Y' is the default radial coordinate. 'R' is only used in rprof
1486+
# output from runs in spherical geometry (of the stratification, not of
1487+
# the grid).
1488+
rvar = 'Y'
1489+
if self.__isRprofSet and geometry == 'spherical':
1490+
rvar = 'R'
14781491
# The grid is assumed to be static, so we get the radial
14791492
# scale only once at cycles[0].
1480-
if self.__isyprofile:
1481-
r = self.get('Y', cycles[0], resolution='l')
1482-
1483-
if self.__isRprofSet:
1484-
r = self.get('R', cycles[0], resolution='l')
1493+
r = self.get(rvar, cycles[0], resolution='l')
14851494

14861495
r_cell_top = 0.5*(np.roll(r, +1) + r)
14871496
r_cell_top[0] = r_cell_top[1] + (r_cell_top[1] - r_cell_top[2])
1488-
r3_cell_top = r_cell_top**3
1489-
dr3 = r3_cell_top - np.roll(r3_cell_top, -1)
1490-
dr3[-1] = dr3[-2] + (dr3[-2] - dr3[-3])
1491-
dV = (4./3.)*np.pi*dr3
1497+
1498+
if geometry == 'spherical':
1499+
r3_cell_top = r_cell_top**3
1500+
dr3 = r3_cell_top - np.roll(r3_cell_top, -1)
1501+
dr3[-1] = dr3[-2] + (dr3[-2] - dr3[-3])
1502+
dV = (4./3.)*np.pi*dr3
1503+
else:
1504+
dr = r_cell_top - np.roll(r_cell_top, -1)
1505+
dr[-1] = dr[-2] + (dr[-2] - dr[-3])
1506+
# We do not know the horizontal dimensions of the Cartesian domain,
1507+
# so we assume the horizontal area is unity.
1508+
dV = dr
14921509

14931510
t = np.zeros(len(cycles))
14941511
burn_rate = np.zeros(len(cycles))
@@ -1509,8 +1526,11 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
15091526
rho = self.get('Rho', cyc, resolution='l')
15101527

15111528
if self.__isRprofSet:
1512-
rho = self.get('Rho0', cyc, resolution='l') + \
1513-
self.get('Rho1', cyc, resolution='l')
1529+
if 'Rho1' in self.get_dump(self.get_dump_list()[0]).get_all_variables():
1530+
rho = self.get('Rho0', cyc, resolution='l') + \
1531+
self.get('Rho1', cyc, resolution='l')
1532+
else:
1533+
rho = self.get('RHO', cyc, resolution='l')
15141534

15151535
if burn_func is not None:
15161536
rhodot = burn_func(cyc, **burn_args)
@@ -1556,10 +1576,17 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
15561576
f_jph = 0.5*(f_j + f_jpo)
15571577

15581578
s = (f_jph - f_jmh)/(r_jph - r_jmh)
1559-
V_j = (4./3.)*np.pi*(r_jmh**3 - r_jph**3)
1560-
f0 = f_j - np.pi*(r_jmh**4 - r_jph**4)/V_j*s
1561-
mir[i] += (4./3.)*np.pi*(rt[i]**3 - r_jph**3)*f0 + \
1562-
np.pi*(rt[i]**4 - r_jph**4)*s
1579+
if geometry == 'spherical':
1580+
V_j = (4./3.)*np.pi*(r_jmh**3 - r_jph**3)
1581+
f0 = f_j - np.pi*(r_jmh**4 - r_jph**4)/V_j*s
1582+
mir[i] += (4./3.)*np.pi*(rt[i]**3 - r_jph**3)*f0 + \
1583+
np.pi*(rt[i]**4 - r_jph**4)*s
1584+
else:
1585+
# We again assume that the horizontal area is unity in
1586+
# Cartesian geometry.
1587+
V_j = r_jmh - r_jph
1588+
f0 = f_j - 0.5*s*(r_jmh**2 - r_jph**2)/V_j
1589+
mir[i] += (rt[i] - r_jph)*f0 + 0.5*s*(rt[i]**2 - r_jph**2)
15631590

15641591
if burn_func is not None:
15651592
# We have to use the minus sign, because rhodot < 0.
@@ -1574,10 +1601,17 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
15741601
print('{:d}/{:d} cycles processed.'.format(i+1, len(cycles)))
15751602
print('Done.')
15761603

1577-
mir *= 1e27/nuconst.m_sun
15781604
# mb = mass burnt
15791605
mb = integrate.cumtrapz(burn_rate, x=t, initial=0.)
1580-
mb *= 1e27/nuconst.m_sun
1606+
1607+
dimensional = False
1608+
if geometry == 'spherical':
1609+
dimensional = True
1610+
1611+
if dimensional:
1612+
mir *= 1e27/nuconst.m_sun
1613+
mb *= 1e27/nuconst.m_sun
1614+
15811615
mtot = mir + mb
15821616

15831617
fit_idx0 = 0
@@ -1603,21 +1637,38 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
16031637
if show_plots:
16041638
cb = utils.colourblind
16051639
pl.close(ifig0); fig1=pl.figure(ifig0)
1606-
pl.plot(t/60., rt, color=cb(5), ls='-', label=r'r$_\mathrm{top}$')
1607-
pl.plot(t/60., rb, color=cb(8), ls='--', label=r'r$_\mathrm{b}$')
1608-
pl.plot(t[fit_range]/60., rt_fit, color=cb(4), ls='-')
1609-
pl.plot(t[fit_range]/60., rb_fit, color=cb(4), ls='-')
1610-
pl.xlabel('t / min')
1611-
pl.ylabel('r / Mm')
1640+
tunit = 1.
1641+
if dimensional:
1642+
tunit = 60.
1643+
if geometry == 'spherical':
1644+
pl.plot(t/tunit, rt, color=cb(5), ls='-', label=r'r$_\mathrm{top}$')
1645+
pl.plot(t/tunit, rb, color=cb(8), ls='--', label=r'r$_\mathrm{b}$')
1646+
else:
1647+
pl.plot(t/tunit, rt, color=cb(5), ls='-', label=r'y$_\mathrm{top}$')
1648+
pl.plot(t/tunit, rb, color=cb(8), ls='--', label=r'y$_\mathrm{b}$')
1649+
pl.plot(t[fit_range]/tunit, rt_fit, color=cb(4), ls='-')
1650+
pl.plot(t[fit_range]/tunit, rb_fit, color=cb(4), ls='-')
1651+
if dimensional:
1652+
pl.xlabel('t / min')
1653+
pl.ylabel('r / Mm')
1654+
else:
1655+
pl.xlabel('t')
1656+
pl.ylabel('y')
16121657
xfmt = ScalarFormatter(useMathText=True)
16131658
pl.gca().xaxis.set_major_formatter(xfmt)
16141659
pl.legend(loc = 0)
16151660
fig1.tight_layout()
16161661

1617-
print('rb is the radius of the convective boundary.')
1618-
print('drb/dt = {:.2e} km/s\n'.format(1e3*rb_fc[0]))
1619-
print('rt is the upper limit for mass integration.')
1620-
print('drt/dt = {:.2e} km/s'.format(1e3*rt_fc[0]))
1662+
if dimensional:
1663+
print('rb is the radius of the convective boundary.')
1664+
print('drb/dt = {:.2e} km/s\n'.format(1e3*rb_fc[0]))
1665+
print('rt is the upper limit for mass integration.')
1666+
print('drt/dt = {:.2e} km/s'.format(1e3*rt_fc[0]))
1667+
else:
1668+
print('yb is the vertical coordinate of the convective boundary.')
1669+
print('dyb/dt = {:.2e}\n'.format(rb_fc[0]))
1670+
print('yt is the upper limit for mass integration.')
1671+
print('dyt/dt = {:.2e}'.format(rt_fc[0]))
16211672

16221673
min_val = np.min([np.min(mtot), np.min(mb), np.min(mtot_fit)])
16231674
max_val = np.max([np.max(mtot), np.max(mb), np.max(mtot_fit)])
@@ -1632,28 +1683,41 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
16321683
parts = mdot_str.split('e')
16331684
mantissa = float(parts[0])
16341685
exponent = int(parts[1])
1635-
lbl = (r'$\dot{{\mathrm{{M}}}}_\mathrm{{e}} = {:.2f} '
1636-
r'\times 10^{{{:d}}}$ M$_\odot$ s$^{{-1}}$').\
1637-
format(mantissa, exponent)
1638-
pl.plot(t[fit_range]/60., mtot_fit/10**oom, color=cb(4), \
1686+
if dimensional:
1687+
lbl = (r'$\dot{{\mathrm{{M}}}}_\mathrm{{e}} = {:.2f} '
1688+
r'\times 10^{{{:d}}}$ M$_\odot$ s$^{{-1}}$').\
1689+
format(mantissa, exponent)
1690+
else:
1691+
lbl = (r'$\dot{{\mathrm{{M}}}}_\mathrm{{e}} = {:.2f} '
1692+
r'\times 10^{{{:d}}}$').format(mantissa, exponent)
1693+
pl.plot(t[fit_range]/tunit, mtot_fit/10**oom, color=cb(4), \
16391694
ls='-', label=lbl, zorder=100)
16401695

16411696
lbl = ''
16421697
if burn_func is not None:
1643-
pl.plot(t/60., mir/10**oom, ':', color=cb(3), label='present')
1644-
pl.plot(t/60., mb/10**oom, '--', color=cb(6), label='burnt')
1698+
pl.plot(t/tunit, mir/10**oom, ':', color=cb(3), label='present')
1699+
pl.plot(t/tunit, mb/10**oom, '--', color=cb(6), label='burnt')
16451700
lbl = 'total'
1646-
pl.plot(t/60., mtot/10**oom, color=cb(5), label=lbl)
1701+
pl.plot(t/tunit, mtot/10**oom, color=cb(5), label=lbl)
16471702

16481703

16491704
pl.ylim((min_val/10**oom, max_val/10**oom))
1650-
pl.xlabel('t / min')
1651-
sub = 'e'
1652-
ylbl = r'M$_{:s}$ / 10$^{{{:d}}}$ M$_\odot$'.format(sub, oom)
1653-
if oom == 0.:
1654-
ylbl = r'M$_{:s}$ / M$_\odot$'.format(sub)
16551705

1656-
pl.ylabel(ylbl)
1706+
if dimensional:
1707+
pl.xlabel('t / min')
1708+
sub = 'e'
1709+
ylbl = r'M$_{:s}$ / 10$^{{{:d}}}$ M$_\odot$'.format(sub, oom)
1710+
if oom == 0.:
1711+
ylbl = r'M$_{:s}$ / M$_\odot$'.format(sub)
1712+
pl.ylabel(ylbl)
1713+
else:
1714+
pl.xlabel('t')
1715+
sub = 'e'
1716+
ylbl = r'M$_{:s}$ / 10$^{{{:d}}}$'.format(sub, oom)
1717+
if oom == 0.:
1718+
ylbl = r'M$_{:s}$'.format(sub)
1719+
pl.ylabel(ylbl)
1720+
16571721
yfmt = FormatStrFormatter('%.1f')
16581722
fig2.gca().yaxis.set_major_formatter(yfmt)
16591723
fig2.tight_layout()
@@ -1663,7 +1727,10 @@ def entr_rate(self, cycles, r_min, r_max, var='ut', criterion='min_grad', \
16631727

16641728
print('Resolution: {:d}^3'.format(2*len(r)))
16651729
print('mtot_fc = ', mtot_fc)
1666-
print('Entrainment rate: {:.3e} M_Sun/s'.format(mdot))
1730+
if dimensional:
1731+
print('Entrainment rate: {:.3e} M_Sun/s'.format(mdot))
1732+
else:
1733+
print('Entrainment rate: {:.3e}'.format(mdot))
16671734

16681735
if return_time_series:
16691736
return t, mir, mb
@@ -9051,7 +9118,7 @@ class RprofSet(PPMtools):
90519118
of PPMstar 2.0.
90529119
'''
90539120

9054-
def __init__(self, dir_name, verbose=3, cache_rprofs=True):
9121+
def __init__(self, dir_name, verbose=3, cache_rprofs=True, geometry='spherical'):
90559122
'''
90569123
Init method.
90579124
@@ -9061,6 +9128,14 @@ def __init__(self, dir_name, verbose=3, cache_rprofs=True):
90619128
Name of the directory to be searched for .rprof files.
90629129
verbose: integer
90639130
Verbosity level as defined in class Messenger.
9131+
cache_rprofs: boolean
9132+
If true all .rprof files loaded from disk will be put into a
9133+
RAM-based cache.
9134+
geometry: 'spherical' or 'cartesian'
9135+
Geometry of the simulation's 3D computational grid. This flag
9136+
changes the definition of high- and low-resolution data as well as
9137+
the behaviour of some methods (such as boundary search, mass
9138+
integrals).
90649139
'''
90659140

90669141
PPMtools.__init__(self,verbose=verbose)
@@ -9090,6 +9165,14 @@ def __init__(self, dir_name, verbose=3, cache_rprofs=True):
90909165
'rprof data by simulation time.')
90919166
self.__messenger.warning(wrng)
90929167

9168+
if geometry in ('spherical', 'cartesian'):
9169+
self.__geometry = geometry
9170+
else:
9171+
wrng = ("Unknown geometry ('{:s}'). Using 'spherical' instead. "
9172+
"Unexpected results may occur.".format(geometry))
9173+
self.__messenger.warning(wrng)
9174+
self.__geometry = 'spherical'
9175+
90939176
self.__is_valid = True
90949177

90959178
def __find_dumps(self, dir_name):
@@ -9179,6 +9262,13 @@ def get_run_id(self):
91799262

91809263
return str(self.__run_id)
91819264

9265+
def get_geometry(self):
9266+
'''
9267+
Returns the geometry identifier.
9268+
'''
9269+
9270+
return str(self.__geometry)
9271+
91829272
def get_history(self):
91839273
'''
91849274
Returns the RprofHistory object associated with this instance if available
@@ -9224,10 +9314,10 @@ def get_dump(self, dump):
92249314
if dump in self.__rprof_cache:
92259315
rp = self.__rprof_cache[dump]
92269316
else:
9227-
rp = Rprof(file_path)
9317+
rp = Rprof(file_path, geometry=self.__geometry)
92289318
self.__rprof_cache[dump] = rp
92299319
else:
9230-
rp = Rprof(file_path)
9320+
rp = Rprof(file_path, geometry=self.__geometry)
92319321

92329322
return rp
92339323

@@ -9722,7 +9812,7 @@ class Rprof:
97229812
PPMstar 2.0.
97239813
'''
97249814

9725-
def __init__(self, file_path, verbose=3):
9815+
def __init__(self, file_path, verbose=3, geometry='spherical'):
97269816
'''
97279817
Init method.
97289818
@@ -9732,10 +9822,24 @@ def __init__(self, file_path, verbose=3):
97329822
Path to the .rprof file.
97339823
verbose: integer
97349824
Verbosity level as defined in class Messenger.
9825+
geometry: 'spherical' or 'cartesian'
9826+
Geometry of the simulation's 3D computational grid. This flag
9827+
changes the definition of high- and low-resolution data as well as
9828+
the behaviour of some methods (such as boundary search, mass
9829+
integrals).
97359830
'''
97369831

97379832
self.__is_valid = False
97389833
self.__messenger = Messenger(verbose=verbose)
9834+
9835+
if geometry in ('spherical', 'cartesian'):
9836+
self.__geometry = geometry
9837+
else:
9838+
wrng = ("Unknown geometry ('{:s}'). Using 'spherical' instead. "
9839+
"Unexpected results may occur.".format(geometry))
9840+
self.__messenger.warning(wrng)
9841+
self.__geometry = 'spherical'
9842+
97399843
if self.__read_rprof_file(file_path) != 0:
97409844
return
97419845

@@ -9842,11 +9946,22 @@ def __read_rprof_file(self, file_path):
98429946
# The first number is always the number of radial zones.
98439947
n = int(sline[0])
98449948

9845-
# n should equal Nx/2 for standard-resolution columns
9846-
# and Nx for high-resolution ones.
9949+
# Is this a table of high-resolution variables?
98479950
is_hr = False
9848-
if n > self.__header_data['Nx']/2:
9849-
is_hr = True
9951+
if self.__geometry == 'spherical':
9952+
# Nx is the number of computational cells along every dimension
9953+
# of the cubical PPMstar grid. The number of radial bins is
9954+
# Nx/2 for standard-resolution columns and Nx for
9955+
# high-resolution ones (computed from PPB sub-cell data).
9956+
if n > self.__header_data['Nx']/2:
9957+
is_hr = True
9958+
else:
9959+
# Nx is the number of computational cells along the vertical
9960+
# dimension of the computational grid. The number of vertical
9961+
# bins is Nx for standard-resolution columns and 2*Nx for
9962+
# high-resolution ones (computed from PPB sub-cell data).
9963+
if n > self.__header_data['Nx']:
9964+
is_hr = True
98509965

98519966
# Register the columns, skipping the 1st one (IR) and any
98529967
# that are already registered (some columns appear in

0 commit comments

Comments
 (0)