Skip to content

Commit 2db3c00

Browse files
authored
Merge pull request #65 from simonsobs/dev
Dev
2 parents bec63dc + 8a470e2 commit 2db3c00

File tree

8 files changed

+77
-69
lines changed

8 files changed

+77
-69
lines changed

INSTALL.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ brackets, later versions also probably work):
1010
* Pillow (5.1.0)
1111
* astropy (4.0)
1212
* PyYAML (3.12)
13-
* `CCL <https://github.com/LSSTDESC/CCL>`_ (2.1 or later)
13+
* `CCL <https://github.com/LSSTDESC/CCL>`_ (3.0.0+)
1414
* mpi4py (3.0.0)
1515
* colorcet (1.0.0; https://github.com/bokeh/colorcet/releases)
1616

bin/nemoMass

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,7 @@ def calcMass(tab, massOptions, QFit, fRelWeightsDict, mockSurvey, otherMassEstim
105105
106106
"""
107107

108-
try:
109-
refMassDef=ccl.halos.MassDef(massOptions['delta'], massOptions['rhoType'],
110-
c_m_relation = massOptions['concMassRelation'])
111-
except:
112-
refMassDef=ccl.halos.MassDef(massOptions['delta'], massOptions['rhoType'])
108+
refMassDef=ccl.halos.MassDef(massOptions['delta'], massOptions['rhoType'])
113109

114110
if 'relativisticCorrection' not in massOptions.keys():
115111
massOptions['relativisticCorrection']=True
@@ -209,11 +205,9 @@ def calcMass(tab, massOptions, QFit, fRelWeightsDict, mockSurvey, otherMassEstim
209205
if 'concMassRelation' not in massDefDict.keys():
210206
massDefDict['concMassRelation']=None
211207
thisLabel='M%d%s' % (massDefDict['delta'], massDefDict['rhoType'][0])
212-
try:
213-
thisMassDef=ccl.halos.MassDef(massDefDict['delta'], massDefDict['rhoType'], c_m_relation = massDefDict['concMassRelation'])
214-
except:
215-
thisMassDef=ccl.halos.MassDef(massDefDict['delta'], massDefDict['rhoType'])
216-
thisMass=signals.MDef1ToMDef2(resultDict[label]*1e14, row['redshift'], refMassDef, thisMassDef, mockSurvey.cosmoModel)/1e14
208+
thisMassDef=ccl.halos.MassDef(massDefDict['delta'], massDefDict['rhoType'])
209+
thisMass=signals.MDef1ToMDef2(resultDict[label]*1e14, row['redshift'], refMassDef, thisMassDef, mockSurvey.cosmoModel,
210+
c_m_relation = massDefDict['concMassRelation'])/1e14
217211
row[thisLabel+suffix]=thisMass
218212
row[thisLabel+suffix+'_errPlus']=(row[label+suffix+'_errPlus']/row[label+suffix])*row[thisLabel+suffix]
219213
row[thisLabel+suffix+'_errMinus']=(row[label+suffix+'_errMinus']/row[label+suffix])*row[thisLabel+suffix]
@@ -362,8 +356,6 @@ if __name__ == '__main__':
362356
mockSurvey=MockSurvey.MockSurvey(minMass, areaDeg2, zMin, zMax, H0, Om0, Ob0, sigma8, ns,
363357
rhoType = massOptions['rhoType'], delta = massOptions['delta'])
364358

365-
# Seems like multiprocessing doesn't work under slurm...
366-
# So use MPI instead: just divide up catalog among processes
367359
tab.add_column(atpy.Column(np.arange(len(tab)), "sortIndex"))
368360
if config.MPIEnabled == True:
369361
numRowsPerProcess=int(np.ceil(len(tab)/config.size))
@@ -372,7 +364,7 @@ if __name__ == '__main__':
372364
if config.rank == config.size-1:
373365
endIndex=len(tab)
374366
tab=tab[startIndex:endIndex]
375-
367+
376368
tab=calcMass(tab, massOptions, Q, fRelWeightsDict, mockSurvey, otherMassEstimates = otherMassEstimates)
377369

378370
if config.MPIEnabled == True:

bin/nemoMock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def makeParser():
5454
fixed_SNR > this value.", default = 4.0, type = float)
5555
parser.add_argument("-Q", "--Q-source", dest="QSource", help = "Source of the Q function data - either\
5656
'fit' (the 'classic' method) or 'injection' (for Q based on source injection test\
57-
results.", default = 'fit')
57+
results).", default = 'fit')
5858
#parser.add_argument("-M", "--mpi", dest="MPIEnabled", action="store_true", help="""Enable MPI. If you
5959
#want to use this, run with e.g., mpiexec -np 4 nemoMock selFnDir mocksDir -M""",
6060
#default = False)

nemo/MockSurvey.py

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class MockSurvey(object):
6363
"""
6464
def __init__(self, minMass, areaDeg2, zMin, zMax, H0, Om0, Ob0, sigma8, ns, zStep = 0.01,
6565
enableDrawSample = False, delta = 500, rhoType = 'critical',
66-
transferFunction = 'boltzmann_camb', massFunction = 'Tinker08'):
66+
transferFunction = 'boltzmann_camb', massFunction = 'Tinker08',
67+
c_m_relation = 'Bhattacharya13'):
6768
"""Create a MockSurvey object, for performing calculations of cluster counts or generating mock
6869
catalogs. The Tinker et al. (2008) halo mass function is used (hardcoded at present, but in
6970
principle this can easily be swapped for any halo mass function supported by CCL).
@@ -89,7 +90,9 @@ def __init__(self, minMass, areaDeg2, zMin, zMax, H0, Om0, Ob0, sigma8, ns, zSte
8990
'boltzmann_camb').
9091
massFunction (:obj:`str`): Name of the mass function to use, currently either 'Tinker08' or
9192
'Tinker10'. Mass function calculations are done by CCL.
92-
93+
c_m_relation ('obj':`str`): Name of the concentration -- mass relation to assume, as understood by
94+
CCL (this may be used internally for conversion between mass definitions, as needed).
95+
9396
"""
9497

9598
if areaDeg2 == 0:
@@ -104,11 +107,8 @@ def __init__(self, minMass, areaDeg2, zMin, zMax, H0, Om0, Ob0, sigma8, ns, zSte
104107

105108
self.delta=delta
106109
self.rhoType=rhoType
107-
if self.delta == 200:
108-
c_m_relation='Bhattacharya13'
109-
else:
110-
c_m_relation=None
111-
self.mdef=ccl.halos.MassDef(self.delta, self.rhoType, c_m_relation = c_m_relation)
110+
self.c_m_relation=c_m_relation
111+
self.mdef=ccl.halos.MassDef(self.delta, self.rhoType)
112112
self.transferFunction=transferFunction
113113
self.massFuncName=massFunction
114114

@@ -129,7 +129,10 @@ def __init__(self, minMass, areaDeg2, zMin, zMax, H0, Om0, Ob0, sigma8, ns, zSte
129129
self.log10M.max()+(self.log10M[1]-self.log10M[0])/2, len(self.log10M)+1)
130130

131131
# Below is needed for Q calc when not using M500c definition (for now at least)
132-
self._M500cDef=ccl.halos.MassDef(500, "critical")
132+
if self.delta != 500 and self.rhoType != 'critical':
133+
self._M500cDef=ccl.halos.MassDef(500, "critical")
134+
self._transToM500c=ccl.halos.mass_translator(mass_in = self.mdef, mass_out = self._M500cDef,
135+
concentration = self.c_m_relation)
133136

134137
self.enableDrawSample=enableDrawSample
135138
self.update(H0, Om0, Ob0, sigma8, ns)
@@ -161,18 +164,16 @@ def _get_new_cosmo(self, H0, Om0, Ob0, sigma8, ns):
161164
self.Ob0=Ob0
162165
self.sigma8=sigma8
163166
self.ns=ns
164-
self.cosmoModel = ccl.Cosmology(Omega_c=Om0-Ob0,
165-
Omega_b=Ob0,
166-
h=0.01*H0,
167-
sigma8=sigma8,
168-
n_s=ns,
169-
transfer_function=self.transferFunction)
167+
self.cosmoModel=ccl.Cosmology(Omega_c=Om0-Ob0,
168+
Omega_b=Ob0,
169+
h=0.01*H0,
170+
sigma8=sigma8,
171+
n_s=ns,
172+
transfer_function=self.transferFunction)
170173
if self.massFuncName == 'Tinker10':
171-
self.mfunc=ccl.halos.MassFuncTinker10(self.cosmoModel,
172-
self.mdef)
174+
self.mfunc=ccl.halos.MassFuncTinker10(mass_def = self.mdef)
173175
elif self.massFuncName == 'Tinker08':
174-
self.mfunc=ccl.halos.MassFuncTinker08(self.cosmoModel,
175-
self.mdef)
176+
self.mfunc=ccl.halos.MassFuncTinker08(mass_def = self.mdef)
176177

177178

178179
def update(self, H0, Om0, Ob0, sigma8, ns):
@@ -205,11 +206,8 @@ def update(self, H0, Om0, Ob0, sigma8, ns):
205206
interpLim_minLog10M500c=self.log10M.min()
206207
interpLim_maxLog10M500c=self.log10M.max()
207208
else:
208-
interpLim_minLog10M500c=np.log10(self.mdef.translate_mass(self.cosmoModel, self.M.min(),
209-
self.a[k], self._M500cDef))
210-
interpLim_maxLog10M500c=np.log10(self.mdef.translate_mass(self.cosmoModel, self.M.max(),
211-
self.a[k], self._M500cDef))
212-
209+
interpLim_minLog10M500c=np.log10(self._transToM500c(self.cosmoModel, self.M.min(), self.a[k]))
210+
interpLim_maxLog10M500c=np.log10(self._transToM500c(self.cosmoModel, self.M.max(), self.a[k]))
213211
zk=self.z[k]
214212
interpPoints=100
215213
fitM500s=np.power(10, np.linspace(interpLim_minLog10M500c, interpLim_maxLog10M500c, interpPoints))
@@ -251,8 +249,7 @@ def _cumulativeNumberDensity(self, z):
251249
"""
252250

253251
h=self.cosmoModel['h']
254-
dndlnM=self.mfunc.get_mass_function(self.cosmoModel,
255-
self.M, 1/(1+z)) / np.log(10) #/ h**3
252+
dndlnM=self.mfunc(self.cosmoModel, self.M, 1/(1+z)) / np.log(10)
256253
dndM=dndlnM/self.M
257254
ngtm=integrate.cumtrapz(dndlnM[::-1], np.log(self.M), initial = 0)[::-1]
258255

@@ -293,8 +290,7 @@ def _doClusterCount(self):
293290
zShellMin=zRange[i]
294291
zShellMax=zRange[i+1]
295292
zShellMid=(zShellMax+zShellMin)/2.
296-
dndlnM=self.mfunc.get_mass_function(self.cosmoModel, self.M,
297-
1./(1+zShellMid)) * norm_mfunc
293+
dndlnM=self.mfunc(self.cosmoModel, self.M, 1./(1+zShellMid)) * norm_mfunc
298294
dndM = dndlnM / self.M
299295
n=dndM * np.gradient(self.M)
300296
numberDensity.append(n)
@@ -362,7 +358,7 @@ def drawSample(self, y0Noise, scalingRelationDict, QFit = None, wcs = None, phot
362358
tileName = None, SNRLimit = None, makeNames = False, z = None, numDraws = None,\
363359
areaDeg2 = None, applySNRCut = False, applyPoissonScatter = True,\
364360
applyIntrinsicScatter = True, applyNoiseScatter = True,\
365-
applyRelativisticCorrection = True, verbose = False):
361+
applyRelativisticCorrection = True, verbose = False, biasModel = None):
366362
"""Draw a cluster sample from the mass function, generating mock y0~ values (called `fixed_y_c` in
367363
Nemo catalogs) by applying the given scaling relation parameters, and then (optionally) applying
368364
a survey selection function.
@@ -555,9 +551,7 @@ def drawSample(self, y0Noise, scalingRelationDict, QFit = None, wcs = None, phot
555551
if self.delta == 500 and self.rhoType == "critical":
556552
log10M500cs[mask]=log10Ms[mask]
557553
else:
558-
log10M500cs[mask]=np.log10(self.mdef.translate_mass(self.cosmoModel, np.power(10, log10Ms[mask]),
559-
1/(1+zk), self._M500cDef))
560-
554+
log10M500cs[mask]=np.log10(self._transToM500c(self.cosmoModel, np.power(10, log10Ms[mask]), 1/(1+zk)))
561555
theta500s=interpolate.splev(log10M500cs[mask], self.theta500Splines[k], ext = 3)
562556
if QFit is not None:
563557
Qs[mask]=QFit.getQ(theta500s, z = zk, tileName = tileName)
@@ -609,8 +603,15 @@ def drawSample(self, y0Noise, scalingRelationDict, QFit = None, wcs = None, phot
609603
tab.add_column(atpy.Column(true_y0s/1e-4, 'true_fixed_y_c'))
610604
tab.add_column(atpy.Column(measured_y0s/1e-4, 'fixed_y_c'))
611605
tab.add_column(atpy.Column(y0Noise/1e-4, 'fixed_err_y_c'))
606+
tab['true_fixed_SNR']=tab['true_fixed_y_c']/tab['fixed_err_y_c'] # True truth, but pre-intrinsic and measurement scatter
607+
# tab['true_fixed_SNR']=(scattered_y0s/1e-4)/tab['fixed_err_y_c'] # With intrinsic scatter, no measurement scatter
608+
# tab['true_fixed_SNR']=tab['fixed_y_c']/tab['fixed_err_y_c'] # Like forced photometry case on a real map at true location
609+
# Apply optimization bias first, then it'll feed through to SNR automatically
610+
if biasModel is not None:
611+
corrFactors=biasModel['func'](tab['true_fixed_SNR'], biasModel['params'][0], biasModel['params'][1], biasModel['params'][2])
612+
tab['fixed_y_c']=tab['fixed_y_c']*corrFactors
613+
tab['fixed_SNR']=tab['fixed_y_c']/tab['fixed_err_y_c']
612614

613-
tab.add_column(atpy.Column(measured_y0s/y0Noise, 'fixed_SNR'))
614615
tab.add_column(atpy.Column(zs, 'redshift'))
615616
tab.add_column(atpy.Column(zErrs, 'redshiftErr'))
616617
if photFilterLabel is not None and tileName is not None:
@@ -619,7 +620,7 @@ def drawSample(self, y0Noise, scalingRelationDict, QFit = None, wcs = None, phot
619620

620621
# Apply selection?
621622
if applySNRCut == True:
622-
selMask=measured_y0s > y0Noise*SNRLimit
623+
selMask=tab['fixed_SNR'] > tab['fixed_err_y_c']*SNRLimit
623624
tab=tab[selMask]
624625
t1=time.time()
625626

nemo/completeness.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,10 @@ def __init__(self, selFnDir, SNRCut, configFileName = None, footprint = None, zS
135135
downsampleRMS = True, applyMFDebiasCorrection = True, applyRelativisticCorrection = True,
136136
setUpAreaMask = False, enableCompletenessCalc = True, delta = 500, rhoType = 'critical',
137137
massFunction = 'Tinker08', maxTheta500Arcmin = None, method = 'fast',
138-
QSource = 'fit', noiseCut = None):
138+
QSource = 'fit', noiseCut = None, biasModel = None):
139139

140140
self.SNRCut=SNRCut
141+
self.biasModel=biasModel
141142
if footprint == 'full':
142143
footprint=None
143144
self.footprint=footprint
@@ -436,9 +437,14 @@ def update(self, H0, Om0, Ob0, sigma8, ns, scalingRelationDict = None):
436437
log_y0=np.log(y0Grid)
437438
compMz=np.zeros(log_y0.shape)
438439
for i in range(len(RMSTab)):
440+
if self.biasModel is not None:
441+
trueSNR=y0Grid/RMSTab['y0RMS'][i]
442+
corrFactors=self.biasModel['func'](trueSNR, self.biasModel['params'][0], self.biasModel['params'][1], self.biasModel['params'][2])
443+
else:
444+
corrFactors=np.ones(y0Grid.shape)
439445
# With intrinsic scatter [may not be quite right, but a good approximation]
440446
totalLogErr=np.sqrt((RMSTab['y0RMS'][i]/y0Grid)**2 + self.scalingRelationDict['sigma_int']**2)
441-
sfi=stats.norm.sf(y0Lim[i], loc = y0Grid, scale = totalLogErr*y0Grid)
447+
sfi=stats.norm.sf(y0Lim[i], loc = y0Grid*corrFactors, scale = totalLogErr*(y0Grid*corrFactors))
442448
compMz=compMz+sfi*areaWeights[i]
443449
# No intrinsic scatter, Gaussian noise
444450
#sfi=stats.norm.sf(y0Lim[i], loc = y0Grid, scale = RMSTab['y0RMS'][i])
@@ -450,6 +456,7 @@ def update(self, H0, Om0, Ob0, sigma8, ns, scalingRelationDict = None):
450456
compMzCube=np.array(compMzCube)
451457
y0GridCube=np.array(y0GridCube)
452458
self.compMz=np.average(compMzCube, axis = 0, weights = self.fracArea)
459+
# self.compMz=np.einsum('ijk,i->jk', np.nan_to_num(compMzCube), self.fracArea) # Equivalent, for noting
453460
self.y0TildeGrid=np.average(y0GridCube, axis = 0, weights = self.fracArea)
454461

455462

@@ -466,10 +473,9 @@ def _makeSignalGrids(self, applyQ = True, tileName = None):
466473
zk=zRange[i]
467474
k=np.argmin(abs(self.mockSurvey.z-zk))
468475
if self.mockSurvey.delta != 500 or self.mockSurvey.rhoType != "critical":
469-
log10M500s=np.log10(self.mockSurvey.mdef.translate_mass(self.mockSurvey.cosmoModel,
470-
self.mockSurvey.M,
471-
self.mockSurvey.a[k],
472-
self.mockSurvey._M500cDef))
476+
log10M500s=np.log10(self.mockSurvey._transToM500c(self.mockSurvey.cosmoModel,
477+
self.mockSurvey.M,
478+
self.mockSurvey.a[k]))
473479
else:
474480
log10M500s=self.mockSurvey.log10M
475481
theta500s_zk=interpolate.splev(log10M500s, self.mockSurvey.theta500Splines[k])
@@ -613,7 +619,8 @@ def generateMockSample(self, mockOversampleFactor = None, applyPoissonScatter =
613619
applyPoissonScatter = applyPoissonScatter,
614620
applyIntrinsicScatter = True,
615621
applyNoiseScatter = True,
616-
applyRelativisticCorrection = self.applyRelativisticCorrection)
622+
applyRelativisticCorrection = self.applyRelativisticCorrection,
623+
biasModel = self.biasModel)
617624
if mockTab is not None and len(mockTab) > 0:
618625
mockTabsList.append(mockTab)
619626
tab=atpy.vstack(mockTabsList)
@@ -1349,8 +1356,9 @@ def calcCompleteness(RMSTab, SNRCut, tileName, mockSurvey, massOptions, QFit, pl
13491356
if massOptions['delta'] == 500 and massOptions['rhoType'] == "critical":
13501357
log10M500cs=mockSurvey.log10M
13511358
else:
1352-
log10M500cs=np.log10(mockSurvey.mdef.translate_mass(mockSurvey.cosmoModel, np.power(10, mockSurvey.log10M),
1353-
1/(1+zk), mockSurvey._M500cDef))
1359+
log10M500cs=np.log10(mockSurvey._transToM500c(mockSurvey.cosmoModel,
1360+
np.power(10, mockSurvey.log10M),
1361+
1/(1+zk)))
13541362

13551363
theta500s_zk=interpolate.splev(log10M500cs, mockSurvey.theta500Splines[k])
13561364
Qs_zk=QFit.getQ(theta500s_zk, z = zk, tileName = tileName)
@@ -1362,7 +1370,7 @@ def calcCompleteness(RMSTab, SNRCut, tileName, mockSurvey, massOptions, QFit, pl
13621370

13631371
# For some cosmological parameters, we can still get the odd -ve y0
13641372
y0Grid[y0Grid <= 0] = 1e-9
1365-
1373+
13661374
# Calculate completeness using area-weighted average
13671375
# NOTE: RMSTab that is fed in here can be downsampled in noise resolution for speed
13681376
areaWeights=RMSTab['areaDeg2']/RMSTab['areaDeg2'].sum()

nemo/pipelines.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def filterMapsAndMakeCatalogs(config, rootOutDir = None, useCachedFilters = Fals
6060
"""
6161

6262
# Multi-pass pipeline, enabled with use of filterSets parameter in config file
63-
if config.filterSets != [] and useCachedFilters == False:
63+
if config.filterSets != [] and useCachedFilters == False and useCachedFilteredMaps == False:
6464
# If we wanted to save results from each step, could set-up filterSet specific diagnostics dir here
6565
if rootOutDir is None:
6666
rootOutDir=config.rootOutDir

0 commit comments

Comments
 (0)