diff --git a/constraints/constraints/projectedCorrelationFunction_SDSS_z0.07.xml b/constraints/constraints/projectedCorrelationFunction_SDSS_z0.07.xml deleted file mode 100644 index 853931bb9..000000000 --- a/constraints/constraints/projectedCorrelationFunction_SDSS_z0.07.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - SDSS $z=0.07$ stellar mass-selected projected correlation function - - 0.07 - 1.0e8 - 2.0e08 - 1.0e15 - constraints/scripts/projectedCorrelationFunction_SDSS_z0.07.pl - diff --git a/constraints/scripts/projectedCorrelationFunction_SDSS_z0.07.pl b/constraints/scripts/projectedCorrelationFunction_SDSS_z0.07.pl deleted file mode 100755 index 12902022b..000000000 --- a/constraints/scripts/projectedCorrelationFunction_SDSS_z0.07.pl +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use Cwd; -use lib $ENV{'GALACTICUS_EXEC_PATH'}."/perl"; -use XML::Simple; -use PDL; -use PDL::NiceSlice; -use PDL::IO::HDF5; -use Data::Dumper; -use Galacticus::Options; -use Galacticus::Constraints::Covariances; -use Galacticus::Constraints::DiscrepancyModels; - -# Compute likelihood (and make a plot) for a Galacticus model given the projected correlation function data from Hearin et -# al. (2013; http://adsabs.harvard.edu/abs/2013arXiv1310.6747H). - -# Get name of input and output files. -die("projectedCorrelationFunction_SDSS_z0.07.pl [options]") - unless ( scalar(@ARGV) >= 1 ); -my $self = $0; -my $galacticusFileName = $ARGV[0]; -# Create a hash of named arguments. -my $iArg = -1; -my %arguments = - ( - quiet => 0 - ); -&Galacticus::Options::Parse_Options(\@ARGV,\%arguments); - -# Object to hold all data. -my $data; - -# Load observational data. -my $observations = new PDL::IO::HDF5("data/observations/correlationFunctions/Projected_Correlation_Functions_Hearin_2013.hdf5"); -my $massMinimum = $observations->dataset('massMinimum')->get(); -my $separation = $observations->dataset('separation' )->get(); -my $correlationFunction = $observations->dataset('projectedCorrelationFunctionObserved')->get(); -my $covarianceMatrix = $observations->dataset('covariance')->get(); -for(my $entry=0;$entry<3;++$entry) { - $data->{'observed'}->{$entry}->{'massMinimum' } = log10($massMinimum->(($entry))); - $data->{'observed'}->{$entry}->{'separation' } = $separation; - $data->{'observed'}->{$entry}->{'correlationFunction'} = $correlationFunction->(:,($entry)); - $data->{'observed'}->{$entry}->{'error' } = $covarianceMatrix->diagonal(0,1)->($entry*nelem($separation):($entry+1)*nelem($separation)-1)->sqrt(); -} -$data->{'observed'}->{'combined'}->{'correlationFunctionCovariance'} = $covarianceMatrix; - -# Load model data. -my $galacticusFile = new PDL::IO::HDF5($galacticusFileName); -my $correlationGroup = $galacticusFile->group('analysis')->group('sdssClusteringZ0.07'); -foreach ( "separation", "correlationFunction", "correlationFunctionCovariance" ) { - $data->{'model'}->{$_} = $correlationGroup->dataset($_)->get(); -} - -# Create a plot of the correlation function. -if ( exists($arguments{'plotFile'}) ) { -use GnuPlot::PrettyPlots; -use GnuPlot::LaTeX; -use XMP::MetaData; - # Iterate over masses. - for(my $entry=0;$entry<3;++$entry) { - # Declare variables for GnuPlot; - my ($gnuPlot, $plotFileEPS, $plot); - # Open a pipe to GnuPlot. - (my $mass = $data->{'observed'}->{$entry}->{'massMinimum'}) =~ s/\./p/; - my $suffix = "_lgM".$mass.".eps"; - ($plotFileEPS = $arguments{'plotFile'}) =~ s/\.pdf$/$suffix/; - open($gnuPlot,"|gnuplot 1>/dev/null 2>&1"); - print $gnuPlot "set terminal epslatex color colortext lw 2 solid 7\n"; - print $gnuPlot "set output '".$plotFileEPS."'\n"; - print $gnuPlot "set lmargin screen 0.15\n"; - print $gnuPlot "set rmargin screen 0.95\n"; - print $gnuPlot "set bmargin screen 0.15\n"; - print $gnuPlot "set tmargin screen 0.95\n"; - print $gnuPlot "set key spacing 1.2\n"; - print $gnuPlot "set key at screen 0.4,0.2\n"; - print $gnuPlot "set key left\n"; - print $gnuPlot "set key bottom\n"; - print $gnuPlot "set logscale xy\n"; - print $gnuPlot "set mxtics 10\n"; - print $gnuPlot "set mytics 10\n"; - print $gnuPlot "set format x '\$10^{\%L}\$'\n"; - print $gnuPlot "set format y '\$10^{\%L}\$'\n"; - print $gnuPlot "set xrange [0.15:25.0]\n"; - print $gnuPlot "set yrange [1.0:1000.0]\n"; - print $gnuPlot "set title offset 0,-0.5 'Projected correlation function at \$z\\approx 0.07\$ for \$\\log_{10}(M_\\star/M_\\odot) > ".$data->{'observed'}->{$entry}->{'massMinimum'}."\$'\n"; - print $gnuPlot "set xlabel 'Separation; \$r_{\\rm p}\$ [Mpc]'\n"; - print $gnuPlot "set ylabel 'Projected correlation; \$w_{\\rm p}(r_{\\rm p})\$ [Mpc]'\n"; - &GnuPlot::PrettyPlots::Prepare_Dataset(\$plot, - $data->{'observed'}->{$entry}->{'separation' }, - $data->{'observed'}->{$entry}->{'correlationFunction'}, - errorUp => $data->{'observed'}->{$entry}->{'error'}, - errorDown => $data->{'observed'}->{$entry}->{'error'}, - style => "point", - symbol => [6,7], - weight => [5,3], - color => $GnuPlot::PrettyPlots::colorPairs{'cornflowerBlue'}, - title => "Hearin et al. (2013)" - ); - my $separationCount = nelem($data->{'model'}->{'separation'}); - my $modelError = - sqrt( - $data - ->{'model'} - ->{'correlationFunctionCovariance'} - ->diagonal(0,1) - ->($entry*$separationCount:($entry+1)*$separationCount-1) - ); - &GnuPlot::PrettyPlots::Prepare_Dataset(\$plot, - $data->{'model'}->{'separation' }, - $data->{'model'}->{'correlationFunction'}->(:,($entry)), - errorUp => $modelError, - errorDown => $modelError, - style => "point", - symbol => [6,7], - weight => [5,3], - color => $GnuPlot::PrettyPlots::colorPairs{'redYellow'}, - title => "Galacticus" - ); - &GnuPlot::PrettyPlots::Plot_Datasets($gnuPlot,\$plot); - close($gnuPlot); - &GnuPlot::LaTeX::GnuPlot2PDF($plotFileEPS,margin => 1); - } -} - -# Construct combined datasets. -my $separationCount = nelem( $data->{'observed'}->{'0'}->{'separation'}); -$data->{'observed'}->{'combined'}->{'separation' } = pdl zeroes(3*$separationCount ); -$data->{'observed'}->{'combined'}->{'correlationFunction' } = pdl zeroes(3*$separationCount ); -$data->{'observed'}->{'combined'}->{'correlationFunctionError' } = pdl zeroes(3*$separationCount ); -for(my $entry=0;$entry<3;++$entry) { - $data->{'observed'}->{'combined'}->{'separation' }->($entry*$separationCount:($entry+1)*$separationCount-1) - .= $data->{'observed'}->{$entry}->{'separation' }; - $data->{'observed'}->{'combined'}->{'correlationFunction' }->($entry*$separationCount:($entry+1)*$separationCount-1) - .= $data->{'observed'}->{$entry}->{'correlationFunction'}; - $data->{'observed'}->{'combined'}->{'correlationFunctionError'}->($entry*$separationCount:($entry+1)*$separationCount-1) - .= $data->{'observed'}->{$entry}->{'error'}; -} -$data->{'model'}->{'combined'}->{'correlationFunction' } = $data->{'model'}->{'correlationFunction' }->flat(); -$data->{'model'}->{'combined'}->{'correlationFunctionCovariance'} = $data->{'model'}->{'correlationFunctionCovariance'}; -$data->{'model'}->{'combined'}->{'correlationFunctionError' } = sqrt($data->{'model'}->{'correlationFunctionCovariance'}->diagonal(0,1)); - -# Apply discrepancies. -&Galacticus::Constraints::DiscrepancyModels::Apply_Discrepancies( - "discrepancySdssClusteringZ0.07.hdf5" , - $arguments {'modelDiscrepancies' }, - $data ->{'model'}->{'combined'}->{'correlationFunction' }, - $data ->{'model'}->{'combined'}->{'correlationFunctionError' }, - $data ->{'model'}->{'combined'}->{'correlationFunctionCovariance'} - ) - if ( exists($arguments{'modelDiscrepancies'}) ); - -# Output the results to file if requested. -if ( exists($arguments{'resultFile'}) ) { - my $resultsFile = new PDL::IO::HDF5(">".$arguments{'resultFile'}); - $resultsFile->dataset('x' )->set($data->{'observed'}->{'combined'}->{'separation' }); - $resultsFile->dataset('y' )->set($data->{'model' }->{'combined'}->{'correlationFunction' }); - $resultsFile->dataset('error' )->set($data->{'model' }->{'combined'}->{'correlationFunctionError' }); - $resultsFile->dataset('covariance' )->set($data->{'model' }->{'combined'}->{'correlationFunctionCovariance'}); - $resultsFile->dataset('yData' )->set($data->{'observed'}->{'combined'}->{'correlationFunction' }); - $resultsFile->dataset('covarianceData')->set($data->{'observed'}->{'combined'}->{'correlationFunctionCovariance'}); -} - -# Compute the likelihood: -if ( exists($arguments{'outputFile'}) ) { - # Construct the full covariance matrix, which is the covariance matrix of the observations plus that of the model. - my $fullCovariance = - $data->{'model' }->{'combined'}->{'correlationFunctionCovariance'}+ - $data->{'observed'}->{'combined'}->{'correlationFunctionCovariance'}; - - # Compute the likelihood. - my $constraint; - my $offsets; - my $jacobian; - my $logLikelihood = - &Galacticus::Constraints::Covariances::ComputeLikelihood( - $data->{'model' }->{'combined'}->{'correlationFunction'}, - $data->{'observed'}->{'combined'}->{'correlationFunction'}, - $fullCovariance , - jacobian => \$jacobian , - offsets => \$offsets , - productMethod => "linearSolver" , - quiet => 0 - ); - $constraint->{'label' } = "sdssClusteringZ0.07"; - $constraint->{'logLikelihood'} = $logLikelihood; - # Compute the variance in the log-likelihood due to errors in the model. - my $logLikelihoodVariance = $jacobian x $data->{'model'}->{'combined'}->{'correlationFunctionCovariance'} x transpose($jacobian); - $constraint->{'logLikelihoodVariance'} = $logLikelihoodVariance->sclr(); - # Output the constraint. - my $xmlOutput = new XML::Simple (NoAttr=>1, RootName=>"constraint"); - open(oHndl,">".$arguments{'outputFile'}); - print oHndl $xmlOutput->XMLout($constraint); - close(oHndl); -} - -exit; diff --git a/source/galacticus.output.analyses.correlation_function.F90 b/source/galacticus.output.analyses.correlation_function.F90 new file mode 100644 index 000000000..f64c2cb74 --- /dev/null +++ b/source/galacticus.output.analyses.correlation_function.F90 @@ -0,0 +1,1348 @@ +!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, +!! 2019 +!! Andrew Benson +!! +!! This file is part of Galacticus. +!! +!! Galacticus is free software: you can redistribute it and/or modify +!! it under the terms of the GNU General Public License as published by +!! the Free Software Foundation, either version 3 of the License, or +!! (at your option) any later version. +!! +!! Galacticus is distributed in the hope that it will be useful, +!! but WITHOUT ANY WARRANTY; without even the implied warranty of +!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +!! GNU General Public License for more details. +!! +!! You should have received a copy of the GNU General Public License +!! along with Galacticus. If not, see . + +!% Contains a module which implements a generic two-point correlation function output analysis class. + + !$ use :: OMP_Lib , only : omp_lock_kind + use , intrinsic :: ISO_C_Binding , only : c_size_t + use :: Geometry_Surveys , only : surveyGeometry , surveyGeometryClass + use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass + use :: Galactic_Filters , only : galacticFilter , galacticFilterClass + use :: Output_Times , only : outputTimes , outputTimesClass + use :: Linear_Growth , only : linearGrowth , linearGrowthClass + use :: Output_Analysis_Distribution_Operators, only : outputAnalysisDistributionOperator, outputAnalysisDistributionOperatorClass + use :: Output_Analysis_Property_Operators , only : outputAnalysisPropertyOperator , outputAnalysisPropertyOperatorClass + use :: Node_Property_Extractors , only : nodePropertyExtractor , nodePropertyExtractorClass + use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMO , darkMatterProfileDMOClass + use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBias , darkMatterHaloBiasClass + use :: Halo_Model_Power_Spectrum_Modifiers , only : haloModelPowerSpectrumModifier , haloModelPowerSpectrumModifierClass + use :: Power_Spectra , only : powerSpectrum , powerSpectrumClass + + !# + !# A generic two-point correlation function output analysis class. + !# + type, extends(outputAnalysisClass) :: outputAnalysisCorrelationFunction + !% A generic two-point correlation function output analysis class. + private + type (varying_string ) :: label , comment , & + & targetLabel + class (galacticFilterClass ), pointer :: galacticFilter_ => null() + class (outputTimesClass ), pointer :: outputTimes_ => null() + class (surveyGeometryClass ), pointer :: surveyGeometry_ => null() + class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null() + class (linearGrowthClass ), pointer :: linearGrowth_ => null() + class (outputAnalysisDistributionOperatorClass), pointer :: massDistributionOperator_ => null() + class (outputAnalysisPropertyOperatorClass ), pointer :: massPropertyOperator_ => null(), separationPropertyOperator_ => null() + class (nodePropertyExtractorClass ), pointer :: massPropertyExtractor_ => null() + class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null() + class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null() + class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_ => null() + class (powerSpectrumClass ), pointer :: powerSpectrum_ => null() + double precision , allocatable, dimension(: ) :: separations , wavenumber , & + & meanDensity , massMinima , & + & massMaxima , linearGrowthFactorSquared , & + & massMinimaLogarithmic , massMaximaLogarithmic + double precision , allocatable, dimension(:,: ) :: outputWeight , meanDensityMainBranch , & + & oneHaloTerm , twoHaloTerm , & + & termCovariance , integralConstraint , & + & binnedProjectedCorrelation , binnedProjectedCorrelationCovariance , & + & binnedProjectedCorrelationTarget , binnedProjectedCorrelationCovarianceTarget + double precision , allocatable, dimension(:,:,:) :: oneHaloTermMainBranch , twoHaloTermMainBranch + integer , allocatable, dimension(: ) :: countMainBranch + double precision :: massHaloLogarithmicMinimum , massHaloIntervalLogarithmicInverse , & + & wavenumberMinimum , wavenumberMaximum , & + & depthLineOfSight + integer (c_size_t ) :: binCount , massCount , & + & countBinsMassHalo , wavenumberCount + logical :: finalized , halfIntegral + !$ integer (omp_lock_kind ) :: accumulateLock + ! Workspace used while accumulating the correlation function. + double precision , allocatable, dimension(: ) :: probabilityCentral + double precision , allocatable, dimension(:,: ) :: probabilitySatellite + integer :: countSatellites + contains + !@ + !@ outputAnalysisCorrelationFunction + !@ + !@ accumulateNode + !@ \doublezero\ mass\argin, \intzero\ massType\argin, \intzero\ indexOutput\argin, \textcolor{red}{\textless type(treeNode)\textgreater} node\arginout + !@ \void + !@ Accumulate a node to the correlation function. + !@ + !@ + !@ accumulateHalo + !@ \intzero\ indexOutput\argin, \textcolor{red}{\textless type(treeNode)\textgreater} node\arginout + !@ \void + !@ Accumulate a halo to the correlation function. + !@ + !@ + final :: correlationFunctionDestructor + procedure :: analyze => correlationFunctionAnalyze + procedure :: finalize => correlationFunctionFinalize + procedure :: reduce => correlationFunctionReduce + procedure :: logLikelihood => correlationFunctionLogLikelihood + procedure :: accumulateNode => correlationFunctionAccumulateNode + procedure :: accumulateHalo => correlationFunctionAccumulateHalo + end type outputAnalysisCorrelationFunction + + interface outputAnalysisCorrelationFunction + !% Constructors for the ``correlationFunction'' output analysis class. + module procedure correlationFunctionConstructorParameters + module procedure correlationFunctionConstructorFile + module procedure correlationFunctionConstructorInternal + end interface outputAnalysisCorrelationFunction + +contains + + function correlationFunctionConstructorParameters(parameters) result(self) + !% Constructor for the ``correlationFunction'' output analysis class which takes a parameter set as input. + use Input_Parameters, only : inputParameter , inputParameters + use Galacticus_Error, only : Galacticus_Error_Report + implicit none + type (outputAnalysisCorrelationFunction ) :: self + type (inputParameters ), intent(inout) :: parameters + class (galacticFilterClass ), pointer :: galacticFilter_ + class (outputTimesClass ), pointer :: outputTimes_ + class (surveyGeometryClass ), pointer :: surveyGeometry_ + class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ + class (linearGrowthClass ), pointer :: linearGrowth_ + class (outputAnalysisDistributionOperatorClass), pointer :: massDistributionOperator_ + class (outputAnalysisPropertyOperatorClass ), pointer :: massPropertyOperator_ , separationPropertyOperator_ + class (nodePropertyExtractorClass ), pointer :: massPropertyExtractor_ + class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ + class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ + class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_ + class (powerSpectrumClass ), pointer :: powerSpectrum_ + double precision , allocatable , dimension(: ) :: separations , massMinima , & + & massMaxima , integralConstraint , & + & binnedProjectedCorrelationTarget1D, binnedProjectedCorrelationCovarianceTarget1D + double precision , allocatable , dimension(:,:) :: binnedProjectedCorrelationTarge t, binnedProjectedCorrelationCovarianceTarget + double precision :: massHaloMinimum , massHaloMaximum , & + & wavenumberMinimum , wavenumberMaximum , & + & depthLineOfSight + logical :: halfIntegral + integer (c_size_t ) :: wavenumberCount + integer :: massHaloBinsPerDecade + type (varying_string ) :: label , comment , & + & targetLabel + + allocate(separations(parameters%count('separations'))) + !# + !# label + !# parameters + !# A label for the mass function. + !# string + !# 0..1 + !# + !# + !# comment + !# parameters + !# A descriptive comment for the mass function. + !# string + !# 0..1 + !# + !# + !# separations + !# parameters + !# The separations corresponding to bin centers. + !# float + !# 0..1 + !# + !# + !# massMinima + !# parameters + !# The minimum mass of each mass sample. + !# float + !# 0..1 + !# + !# + !# massMaxima + !# parameters + !# The maximum mass of each mass sample. + !# float + !# 0..1 + !# + !# + !# massHaloBinsPerDecade + !# 0..1 + !# 10 + !# The number of bins per decade of halo mass to use when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# massHaloMinimum + !# 0..1 + !# 1.0d8 + !# The minimum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# massHaloMaximum + !# 0..1 + !# 1.0d16 + !# The maximum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# wavenumberCount + !# 0..1 + !# 60_c_size_t + !# The number of bins in wavenumber to use in computing the correlation function. + !# output + !# parameters + !# integer + !# + !# + !# wavenumberMinimum + !# 0..1 + !# 1.0d-3 + !# The minimum wavenumber to use when computing the correlation function. + !# output + !# parameters + !# real + !# + !# + !# wavenumberMaximum + !# 0..1 + !# 1.0d4 + !# The maximum wavenumber to use when computing the correlation function. + !# output + !# parameters + !# real + !# + !# + !# integralConstraint + !# 0..1 + !# The integral constraint for these correlation functions. + !# output + !# parameters + !# real + !# + !# + !# depthLineOfSight + !# 0..1 + !# The line-of-sight depth over which the correlation function was projected. + !# output + !# parameters + !# real + !# + !# + !# halfIntegral + !# 0..1 + !# Set to true if the projection integrand should be over line-of-sight depths greater than zero. + !# output + !# parameters + !# real + !# + if (parameters%isPresent('binnedProjectedCorrelationTarget')) then + if (parameters%isPresent('binnedProjectedCorrelationCovarianceTarget')) then + !# + !# binnedProjectedCorrelationTarget + !# parameters + !# The target function for likelihood calculations. + !# binnedProjectedCorrelationTarget1D + !# real + !# 0..1 + !# + !# + !# binnedProjectedCorrelationCovarianceTarget + !# parameters + !# binnedProjectedCorrelationCovarianceTarget1D + !# The target function covariance for likelihood calculations. + !# real + !# 0..1 + !# + if (size(binnedProjectedCorrelationCovarianceTarget1D) == size(binnedProjectedCorrelationTarget1D)**2) then + allocate(binnedProjectedCorrelationTarget (size(separations ),size(massMinima ))) + allocate(binnedProjectedCorrelationCovarianceTarget(size(binnedProjectedCorrelationTarget1D),size(binnedProjectedCorrelationTarget1D))) + binnedProjectedCorrelationTarget =reshape(binnedProjectedCorrelationTarget1D ,shape(binnedProjectedCorrelationTarget )) + binnedProjectedCorrelationCovarianceTarget=reshape(binnedProjectedCorrelationCovarianceTarget1D,shape(binnedProjectedCorrelationCovarianceTarget)) + else + call Galacticus_Error_Report('binnedProjectedCorrelationCovarianceTarget has wrong size'//{introspection:location}) + end if + else + call Galacticus_Error_Report('binnedProjectedCorrelationCovarianceTarget must be specified if binnedProjectedCorrelationTarget is present'//{introspection:location}) + end if + else + if (parameters%isPresent('binnedProjectedCorrelationCovarianceTarget')) call Galacticus_Error_Report('binnedProjectedCorrelationTarget must be specified if binnedProjectedCorrelationCovarianceTarget is present'//{introspection:location}) + end if + !# + !# targetLabel + !# parameters + !# A label for the target dataset in a plot of this analysis. + !# var_str('') + !# string + !# 0..1 + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# self=outputAnalysisCorrelationFunction( & + !# & label , & + !# & comment , & + !# & separations , & + !# & massMinima , & + !# & massMaxima , & + !# & massHaloBinsPerDecade , & + !# & massHaloMinimum , & + !# & massHaloMaximum , & + !# & reshape(integralConstraint,[size(separations),size(massMinima)]), & + !# & wavenumberCount , & + !# & wavenumberMinimum , & + !# & wavenumberMaximum , & + !# & depthLineOfSight , & + !# & halfIntegral , & + !# & linearGrowth_ , & + !# & galacticFilter_ , & + !# & surveyGeometry_ , & + !# & cosmologyFunctions_ , & + !# & outputTimes_ , & + !# & darkMatterProfileDMO_ , & + !# & darkMatterHaloBias_ , & + !# & haloModelPowerSpectrumModifier_ , & + !# & powerSpectrum_ , & + !# & massDistributionOperator_ , & + !# & massPropertyOperator_ , & + !# & separationPropertyOperator_ , & + !# & massPropertyExtractor_ , & + !# & targetLabel & + !# & {conditions} & + !# & ) + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + return + end function correlationFunctionConstructorParameters + + function correlationFunctionConstructorFile(label,comment,fileName,massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,wavenumberCount,wavenumberMinimum,wavenumberMaximum,halfIntegral,linearGrowth_,galacticFilter_,surveyGeometry_,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_,massDistributionOperator_,massPropertyOperator_,separationPropertyOperator_,massPropertyExtractor_) result (self) + !% Constructor for the ``correlation'' output analysis class which reads bin information from a standard format file. + use IO_HDF5 , only : hdf5Access , hdf5Object + use Cosmology_Parameters, only : cosmologyParametersSimple + use Cosmology_Functions , only : cosmologyFunctionsMatterLambda + implicit none + type (outputAnalysisCorrelationFunction ) :: self + type (varying_string ), intent(in ) :: label , comment + character (len=* ), intent(in ) :: fileName + integer (c_size_t ), intent(in ) :: wavenumberCount + double precision , intent(in ) :: massHaloMinimum , massHaloMaximum , & + & wavenumberMinimum , wavenumberMaximum + logical , intent(in ) :: halfIntegral + integer , intent(in ) :: massHaloBinsPerDecade + class (linearGrowthClass ), intent(in ), target :: linearGrowth_ + class (galacticFilterClass ), intent(in ), target :: galacticFilter_ + class (surveyGeometryClass ), intent(in ), target :: surveyGeometry_ + class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_ + class (outputTimesClass ), intent(in ), target :: outputTimes_ + class (outputAnalysisDistributionOperatorClass), intent(in ), target :: massDistributionOperator_ + class (outputAnalysisPropertyOperatorClass ), intent(in ), target :: massPropertyOperator_ , separationPropertyOperator_ + class (nodePropertyExtractorClass ), intent(in ), target :: massPropertyExtractor_ + class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_ + class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_ + class (haloModelPowerSpectrumModifierClass ), intent(in ), target :: haloModelPowerSpectrumModifier_ + class (powerSpectrumClass ), intent(in ), target :: powerSpectrum_ + double precision , allocatable , dimension(: ) :: separations , massMinima , & + & massMaxima + double precision , allocatable , dimension(:,:) :: integralConstraint , binnedProjectedCorrelationTarget, & + & binnedProjectedCorrelationCovarianceTarget + type (varying_string ) :: targetLabel + type (hdf5Object ) :: dataFile , parametersGroup + double precision :: hubbleParameterData , omegaMatterData , & + & omegaDarkEnergyData , depthLineOfSight + + !$ call hdf5Access%set() + call dataFile%openFile(fileName,readOnly=.true.) + ! Extract parameters. + parametersGroup=dataFile%openGroup('Parameters') + call parametersGroup%readAttribute('H_0' ,hubbleParameterData) + call parametersGroup%readAttribute('Omega_Matter' ,omegaMatterData ) + call parametersGroup%readAttribute('Omega_DE' ,omegaDarkEnergyData) + call parametersGroup%readAttribute('projectedCorrelationFunctionLineOfSightDepth',depthLineOfSight ) + call parametersGroup%close() + ! Extract separations. + call dataFile%readDataset('separationObserved',separations ) + ! Extract observed datasets. + call dataFile%readAttribute('label' ,targetLabel ) + call dataFile%readDataset ('projectedCorrelationFunctionObserved',binnedProjectedCorrelationTarget ) + call dataFile%readDataset ('covariance' ,binnedProjectedCorrelationCovarianceTarget) + ! Extract integral constraint. + call dataFile%readDataset('integralConstraint',integralConstraint) + ! Read the minimum and maximum masses. + call dataFile%readDataset("massMinimum" ,massMinima ) + call dataFile%readDataset("massMaximum" ,massMaxima ) + ! Finish reading data file. + call dataFile%close ( ) + !$ call hdf5Access%unset() + self=outputAnalysisCorrelationFunction(label,comment,separations,massMinima,massMaxima,massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,integralConstraint,wavenumberCount,wavenumberMinimum,wavenumberMaximum,depthLineOfSight,halfIntegral,linearGrowth_,galacticFilter_,surveyGeometry_,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_,massDistributionOperator_,massPropertyOperator_,separationPropertyOperator_,massPropertyExtractor_,targetLabel,binnedProjectedCorrelationTarget,binnedProjectedCorrelationCovarianceTarget) + return + end function correlationFunctionConstructorFile + + function correlationFunctionConstructorInternal(label,comment,separations,massMinima,massMaxima,massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,integralConstraint,wavenumberCount,wavenumberMinimum,wavenumberMaximum,depthLineOfSight,halfIntegral,linearGrowth_,galacticFilter_,surveyGeometry_,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_,massDistributionOperator_,massPropertyOperator_,separationPropertyOperator_,massPropertyExtractor_,targetLabel,binnedProjectedCorrelationTarget,binnedProjectedCorrelationCovarianceTarget) result (self) + !% Constructor for the ``correlationFunction'' output analysis class for internal use. + use, intrinsic :: ISO_C_Binding , only : c_size_t + use :: Galacticus_Error , only : Galacticus_Error_Report + use :: Memory_Management , only : allocateArray + use :: Output_Analysis_Utilities, only : Output_Analysis_Output_Weight_Survey_Volume + use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic + implicit none + type (outputAnalysisCorrelationFunction ) :: self + type (varying_string ), intent(in ) :: label , comment + double precision , intent(in ), dimension(: ) :: separations , massMinima , & + & massMaxima + double precision , intent(in ), dimension(:,:) :: integralConstraint + integer (c_size_t ), intent(in ) :: wavenumberCount + double precision , intent(in ) :: massHaloMinimum , massHaloMaximum , & + & wavenumberMinimum , wavenumberMaximum , & + & depthLineOfSight + logical , intent(in ) :: halfIntegral + integer , intent(in ) :: massHaloBinsPerDecade + class (linearGrowthClass ), intent(in ), target :: linearGrowth_ + class (galacticFilterClass ), intent(in ), target :: galacticFilter_ + class (outputTimesClass ), intent(in ), target :: outputTimes_ + class (surveyGeometryClass ), intent(in ), target :: surveyGeometry_ + class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_ + class (outputAnalysisDistributionOperatorClass), intent(in ), target :: massDistributionOperator_ + class (outputAnalysisPropertyOperatorClass ), intent(in ), target :: massPropertyOperator_ , separationPropertyOperator_ + class (nodePropertyExtractorClass ), intent(in ), target :: massPropertyExtractor_ + class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_ + class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_ + class (haloModelPowerSpectrumModifierClass ), intent(in ), target :: haloModelPowerSpectrumModifier_ + class (powerSpectrumClass ), intent(in ), target :: powerSpectrum_ + type (varying_string ), intent(in ) , optional :: targetLabel + double precision , intent(in ), dimension(:,:), optional :: binnedProjectedCorrelationTarget , binnedProjectedCorrelationCovarianceTarget + integer (c_size_t ) :: i + !# + + ! Compute weight that applies to each output redshift. + self%massCount=size(self%massMinima ) + self%binCount =size(self%separations,kind=c_size_t) + call allocateArray(self%outputWeight,[self%massCount,self%outputTimes_%count()]) + do i=1,self%massCount + self%outputWeight(i,:)=Output_Analysis_Output_Weight_Survey_Volume(self%surveyGeometry_,self%cosmologyFunctions_,self%outputTimes_,massMinima(i)) + end do + ! Pre-compute linear growth factors. + allocate(self%linearGrowthFactorSquared(self%outputTimes_%count())) + do i=1,self%outputTimes_%count() + self%linearGrowthFactorSquared=self%linearGrowth_%value(self%outputTimes_%time(i))**2 + end do + ! Construct halo mass bins. + self%massHaloLogarithmicMinimum = log10( massHaloMinimum) + self%countBinsMassHalo = int(log10(massHaloMaximum/massHaloMinimum)*dble(massHaloBinsPerDecade)+0.5d0) + self%massHaloIntervalLogarithmicInverse=dble(self%countBinsMassHalo)/ log10(massHaloMaximum/massHaloMinimum) + ! Allocate wavenumbers. + call allocateArray(self%wavenumber ,[self%wavenumberCount ]) + call allocateArray(self%probabilityCentral ,[ self%massCount ]) + call allocateArray(self%probabilitySatellite ,[ 1_c_size_t ,self%massCount ]) + call allocateArray(self%meanDensity ,[ self%massCount ]) + call allocateArray(self%meanDensityMainBranch,[ self%massCount,self%countBinsMassHalo]) + call allocateArray(self%countMainBranch ,[ self%countBinsMassHalo]) + call allocateArray(self%oneHaloTermMainBranch,[self%wavenumberCount,self%massCount,self%countBinsMassHalo]) + call allocateArray(self%twoHaloTermMainBranch,[self%wavenumberCount,self%massCount,self%countBinsMassHalo]) + call allocateArray(self%oneHaloTerm ,[self%wavenumberCount,self%massCount ]) + call allocateArray(self%twoHaloTerm ,[self%wavenumberCount,self%massCount ]) + call allocateArray(self%termCovariance ,[self%massCount*(2*self%wavenumberCount+1),self%massCount*(2*self%wavenumberCount+1)]) + self%wavenumber=Make_Range(self%wavenumberMinimum,self%wavenumberMaximum,int(self%wavenumberCount),rangeTypeLogarithmic) + ! Compute logarithmic masses. + allocate(self%massMinimaLogarithmic(self%massCount)) + allocate(self%massMaximaLogarithmic(self%massCount)) + self%massMinimaLogarithmic=log10(self%massMinima) + self%massMaximaLogarithmic=log10(self%massMaxima) + ! Initialize population statistics. + self%countMainBranch =0 + self%meanDensity =0.0d0 + self%meanDensityMainBranch=0.0d0 + self%oneHaloTermMainBranch=0.0d0 + self%twoHaloTermMainBranch=0.0d0 + self%oneHaloTerm =0.0d0 + self%twoHaloTerm =0.0d0 + self%termCovariance =0.0d0 + self%probabilityCentral =0.0d0 + self%probabilitySatellite =0.0d0 + self%countSatellites =0 + self%finalized =.false. + ! Initialize accumulation lock. + !$ call OMP_Init_Lock(self%accumulateLock) + return + end function correlationFunctionConstructorInternal + + subroutine correlationFunctionDestructor(self) + !% Destructor for the ``correlationFunction'' output analysis class. + implicit none + type(outputAnalysisCorrelationFunction), intent(inout) :: self + + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !$ call OMP_Destroy_Lock(self%accumulateLock) + return + end subroutine correlationFunctionDestructor + + subroutine correlationFunctionAnalyze(self,node,iOutput) + !% Implement a correlationFunction output analysis. + implicit none + class (outputAnalysisCorrelationFunction), intent(inout) :: self + type (treeNode ), intent(inout) :: node + integer (c_size_t ), intent(in ) :: iOutput + double precision :: mass + integer :: massType + + ! If weights for this output are all zero, we can skip analysis. + if (all(self%outputWeight(:,iOutput) == 0.0d0)) return + ! Filter this node. + ! Extract mass property. + massType=self%massPropertyExtractor_%type() + select type (extractor_ => self%massPropertyExtractor_) + class is (nodePropertyExtractorScalar) + mass=extractor_%extract(node) + class default + mass=0.0d0 + end select + ! Apply operator to mass. + mass=self%massPropertyOperator_%operate(mass,node,massType,iOutput) + ! Accumulate the node. + call self%accumulateNode(mass,massType,iOutput,node) + ! Accumulate the halo. Since nodes are visited depth first we can be sure that all satellite of this isolated halo have + ! already been visited. + if (.not.node%isSatellite()) call self%accumulateHalo(iOutput,node) + return + end subroutine correlationFunctionAnalyze + + subroutine correlationFunctionAccumulateNode(self,mass,massType,indexOutput,node) + !% Accumulate a single galaxy to the population of the current halo. Since galaxy masses + !% have random errors, each galaxy added is assigned an inclusion probability, which will be + !% taken into account when evaluating the one- and two-halo terms from this halo in the halo + !% model. + implicit none + class (outputAnalysisCorrelationFunction), intent(inout) :: self + double precision , intent(in ) :: mass + integer , intent(in ) :: massType + integer (c_size_t ), intent(in ) :: indexOutput + type (treeNode ), intent(inout) :: node + double precision , allocatable , dimension(:,:) :: probabilitySatelliteTmp + double precision , dimension(1 ) :: massDistribution + logical :: satelliteIncluded + integer :: j + + ! Evaluate, for each mass bin, the probability of inclusion of the galaxy in that bin. Store any such non-zero probabilities + ! for central and satellite galaxies separately. + satelliteIncluded=.false. + do j=1,size(self%massMinimaLogarithmic) + ! Find the probability that this galaxy is included in the sample. + massDistribution=self%massDistributionOperator_%operateScalar(mass,massType,self%massMinimaLogarithmic(j:j),self%massMaximaLogarithmic(j:j),indexOutput,node) + if (node%isSatellite()) then + if (massDistribution(1) > 0.0d0) then + if (.not.satelliteIncluded) then + satelliteIncluded =.true. + self%countSatellites=self%countSatellites+1 + if (size(self%probabilitySatellite,dim=1) < self%countSatellites) then + call move_alloc(self%probabilitySatellite,probabilitySatelliteTmp) + allocate(self%probabilitySatellite(2*size(probabilitySatelliteTmp,dim=1),size(self%massMinimaLogarithmic))) + self%probabilitySatellite(1:size(probabilitySatelliteTmp,dim=1),:)=probabilitySatelliteTmp + deallocate(probabilitySatelliteTmp) + end if + self%probabilitySatellite(self%countSatellites,:)=0.0d0 + end if + self%probabilitySatellite(self%countSatellites,j)=massDistribution(1) + end if + else + self%probabilityCentral(j)=massDistribution(1) + end if + end do + return + end subroutine correlationFunctionAccumulateNode + + subroutine correlationFunctionAccumulateHalo(self,indexOutput,node) + !% Assumulate a single halo's contributions to the halo model one- and two-halo terms. For + !% the one-halo term we count contributions from central-satellite pairs, and from + !% satellite-satellite pairs. Contributions differ in the scalings applied to the + !% Fourier-transformed dark matter halo density profile---see + !% \cite[][\S6.1]{cooray_halo_2002} for a discussion of this. The number of satellites in + !% the halo is assumed to follow a Poisson binomial distribution. + use Math_Distributions_Poisson_Binomial, only : Poisson_Binomial_Distribution_Mean, Poisson_Binomial_Distribution_Mean_Pairs, Poisson_Binomial_Distribution_Mean_Pairs_Jacobian + use Vectors , only : Vector_Outer_Product + use Linear_Algebra , only : matrix , operator(*) , assignment(=) + use Galacticus_Nodes , only : nodeComponentBasic + use Halo_Model_Power_Spectrum_Modifiers, only : haloModelTermOneHalo , haloModelTermTwoHalo + implicit none + class (outputAnalysisCorrelationFunction), intent(inout) :: self + integer (c_size_t ), intent(in ) :: indexOutput + type (treeNode ), intent(inout) :: node + class (nodeComponentBasic ), pointer :: basic , basicRoot + double precision , dimension(self%wavenumberCount,self%massCount) :: oneHaloTerm , twoHaloTerm + double precision , dimension( self%massCount) :: galaxyDensity + logical , dimension( self%massCount) :: oneHaloTermActive , twoHaloTermActive + double precision , allocatable , dimension(: ,: ) :: termJacobian , termCovariance , & + & mainBranchTermCovariance, modifierCovariance + double precision , allocatable , dimension(: ) :: satelliteJacobian , modifierCovarianceDiagonal, & + & fourierProfile , wavenumber + double precision :: countSatellitePairsMean , countSatellitesMean , & + & haloWeightOutput , expansionFactor , & + & biasHalo , massHalo + integer (c_size_t ) :: i , j , & + & indexOneHalo , indexTwoHalo , & + & indexDensity + integer :: haloMassBin , scaleType + logical :: mainBranchCounted + type (matrix ) :: jacobianMatrix + + ! Return immediately if no nodes have been accumulated. + if (all(self%probabilityCentral == 0.0d0) .and. self%countSatellites == 0) return + ! Construct the Fourier profile of the host halo. + expansionFactor=self%cosmologyFunctions_%expansionFactor(self%outputTimes_%time(indexOutput)) + allocate(wavenumber (self%wavenumberCount)) + allocate(fourierProfile(self%wavenumberCount)) + do i=1,self%wavenumberCount + ! Note that wavenumbers must be converted from comoving to physical units for the dark matter profile k-space function. + scaleType =outputAnalysisPropertyTypeLinear + wavenumber (i)=+1.0d0 & + & /self%separationPropertyOperator_%operate(1.0d0/self%waveNumber(i),node,scaleType,indexOutput) + fourierProfile(i)=self%darkMatterProfileDMO_%kSpace( & + & node , & + & wavenumber(i)/expansionFactor & + & ) + end do + ! Get the mass of this halo. + basic => node %basic() + massHalo = basic%mass () + ! Get the bias of this halo. + biasHalo=self%darkMatterHaloBias_%bias(node) + ! Accumulate. + oneHaloTermActive=.false. + twoHaloTermActive=.false. + mainBranchCounted=.false. + allocate(termJacobian (self%massCount*(2*self%wavenumberCount+1),self%countSatellites+1)) + allocate(satelliteJacobian ( self%countSatellites )) + allocate(modifierCovariance ( self%wavenumberCount ,self%wavenumberCount )) + allocate(modifierCovarianceDiagonal(self%massCount*(2*self%wavenumberCount+1) )) + termJacobian =0.0d0 + modifierCovariance =0.0d0 + modifierCovarianceDiagonal=0.0d0 + ! Iterate over masses. + do i=1,self%massCount + ! Find mean number of satellites and satellite pairs. + if (self%countSatellites > 0) then + countSatellitesMean =Poisson_Binomial_Distribution_Mean (self%probabilitySatellite(1:self%countSatellites,i)) + countSatellitePairsMean=Poisson_Binomial_Distribution_Mean_Pairs(self%probabilitySatellite(1:self%countSatellites,i)) + else + countSatellitesMean =0.0d0 + countSatellitePairsMean=0.0d0 + end if + ! Skip if this halo contains no galaxies. + if (self%probabilityCentral(i) > 0.0d0 .or. countSatellitesMean > 0.0d0) then + ! Compute output halo weight. + haloWeightOutput=node%hostTree%volumeWeight*self%outputWeight(i,indexOutput) + ! Compute contribution to galaxy density. + galaxyDensity(i)=+haloWeightOutput & + & *( & + & +self%probabilityCentral(i) & + & +countSatellitesMean & + & ) + ! For main branch galaxies, accumulate their contribution to the density as a function of halo mass, so that we can later subtract this from the variance. + if (node%isOnMainBranch()) then + basicRoot => node %hostTree%baseNode%basic() + haloMassBin = floor((log10(basicRoot%mass())-self%massHaloLogarithmicMinimum)*self%massHaloIntervalLogarithmicInverse)+1 + ! Accumulate weights to halo mass arrays. + if (haloMassBin >= 1 .and. haloMassBin <= self%countBinsMassHalo) then + self %meanDensityMainBranch( i,haloMassBin)= & + & +self%meanDensityMainBranch( i,haloMassBin) & + & + haloWeightOutput & + & *self%probabilityCentral ( i ) + self %oneHaloTermMainBranch(:,i,haloMassBin)= & + & +self%oneHaloTermMainBranch(:,i,haloMassBin) & + & + haloWeightOutput & + & *self%probabilityCentral ( i ) & + & * countSatellitesMean & + & * fourierProfile + self %twoHaloTermMainBranch(:,i,haloMassBin)= & + & +self%twoHaloTermMainBranch(:,i,haloMassBin) & + & + haloWeightOutput & + & *self%probabilityCentral ( i ) & + & * biasHalo & + & * fourierProfile + ! If this is the first mass bin in which the central, main branch galaxy is seen, increment the number of main branch galaxies. + if (.not.mainBranchCounted) then + mainBranchCounted =.true. + self%countMainBranch(haloMassBin)=self%countMainBranch(haloMassBin)+1 + end if + end if + end if + ! Accumulate contribution to galaxy density. + self%meanDensity(i)=+self%meanDensity(i) & + & +galaxyDensity (i) + ! Compute and accumulate one-halo term. + if (countSatellitesMean > 0.0d0) then + oneHaloTermActive( i)=.true. + oneHaloTerm (:,i)=+haloWeightOutput & + & *( & + & +self%probabilityCentral (i) & + & * countSatellitesMean & + & * fourierProfile & + & + countSatellitePairsMean & + & * fourierProfile **2 & + & ) + call self%haloModelPowerSpectrumModifier_%modify( & + & wavenumber , & + & haloModelTermOneHalo, & + & oneHaloTerm(:,i) , & + & modifierCovariance , & + & mass=massHalo & + & ) + call correlationFunctionTermIndices(i,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + forall(j=1:self%wavenumberCount) + modifierCovarianceDiagonal(indexOneHalo+j-1)=modifierCovariance(j,j) + end forall + modifierCovarianceDiagonal(indexDensity)=0.0d0 + self%oneHaloTerm(:,i)=+self%oneHaloTerm(:,i) & + & + oneHaloTerm(:,i) + end if + ! Compute and accumulate two-halo term. + twoHaloTermActive( i)=.true. + twoHaloTerm (:,i)=+galaxyDensity (i) & + & *biasHalo & + & *fourierProfile + call self%haloModelPowerSpectrumModifier_%modify( & + & wavenumber , & + & haloModelTermTwoHalo, & + & twoHaloTerm(:,i) , & + & modifierCovariance , & + & mass=massHalo & + & ) + call correlationFunctionTermIndices(i,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + forall(j=1:self%wavenumberCount) + modifierCovarianceDiagonal(indexTwoHalo+j-1)=modifierCovariance(j,j) + end forall + modifierCovarianceDiagonal(indexDensity)=0.0d0 + self%twoHaloTerm(:,i)=+self%twoHaloTerm(:,i) & + & + twoHaloTerm(:,i) + ! Construct Jacobian of the terms being accumulated. The Jacobian here is an MxN matrix, where M=massCount*(2*wavenumberCount+1) + ! (the number of terms in halo model quantities being accumulated {wavenumberCount for 1- and 2-halo terms, plus a density, for + ! each mass bin}), and N is the total number of galaxies in the halo (number of satellites plus 1 central). + ! Compute indices. + call correlationFunctionTermIndices(i,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + ! One halo terms. + if (self%countSatellites > 0) then + satelliteJacobian=Poisson_Binomial_Distribution_Mean_Pairs_Jacobian(self%probabilitySatellite(1:self%countSatellites,i))*self%probabilitySatellite(1:self%countSatellites,i) + do j=1,self%wavenumberCount + termJacobian(indexOneHalo +j -1,1:self%countSatellites )=haloWeightOutput *fourierProfile(j)**2* satelliteJacobian + end do + end if + termJacobian (indexOneHalo:indexOneHalo+self%wavenumberCount-1, self%countSatellites+1)=haloWeightOutput *fourierProfile *self%probabilityCentral ( i)*countSatellitesMean + ! Two halo terms. + do j=1,self%wavenumberCount + termJacobian (indexTwoHalo +j -1,1:self%countSatellites )=haloWeightOutput*biasHalo*fourierProfile(j) *self%probabilitySatellite(1:self%countSatellites,i) + end do + termJacobian (indexTwoHalo:indexTwoHalo+self%wavenumberCount-1, self%countSatellites+1)=haloWeightOutput*biasHalo*fourierProfile *self%probabilityCentral ( i) + ! Compute density terms. + termJacobian (indexDensity ,1:self%countSatellites )=haloWeightOutput *self%probabilitySatellite(1:self%countSatellites,i) + termJacobian (indexDensity , self%countSatellites+1)=haloWeightOutput *self%probabilityCentral ( i) + end if + end do + ! Construct and accumulate term covariance. + allocate(termCovariance(self%massCount*(2*self%wavenumberCount+1),self%massCount*(2*self%wavenumberCount+1))) + jacobianMatrix=termJacobian + termCovariance=jacobianMatrix*jacobianMatrix%transpose() + ! Add modifier covariance. + termCovariance=termCovariance+Vector_Outer_Product(modifierCovarianceDiagonal) + ! For main branch galaxies, zero all off-diagonal contributions. + if (node%isOnMainBranch()) then + termJacobian(:,1:self%countSatellites)=0.0d0 + jacobianMatrix=termJacobian + allocate(mainBranchTermCovariance(self%massCount*(2*self%wavenumberCount+1),self%massCount*(2*self%wavenumberCount+1))) + mainBranchTermCovariance=jacobianMatrix*jacobianMatrix%transpose() + do i=1,self%massCount + mainBranchTermCovariance( & + & (i-1)*(2*self%wavenumberCount+1)+1:i*(2*self%wavenumberCount+1), & + & (i-1)*(2*self%wavenumberCount+1)+1:i*(2*self%wavenumberCount+1) & + & ) & + & =0.0d0 + end do + termCovariance=termCovariance-mainBranchTermCovariance + deallocate(mainBranchTermCovariance) + end if + self%termCovariance=self%termCovariance+termCovariance + deallocate(termJacobian ) + deallocate(satelliteJacobian) + ! Reset counts. + self%probabilityCentral=0.0d0 + self%countSatellites =0 + return + end subroutine correlationFunctionAccumulateHalo + + subroutine correlationFunctionTermIndices(iMass,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + !% Return the indices in the term covariances array at which one-halo, two-halo, and density terms are stored for the given + !% mass. + implicit none + integer(c_size_t), intent(in ) :: iMass , wavenumberCount + integer(c_size_t), intent( out) :: indexOneHalo, indexTwoHalo , & + & indexDensity + + indexOneHalo=(iMass-1)*(2*wavenumberCount+1) +1 + indexTwoHalo=(iMass-1)*(2*wavenumberCount+1)+ wavenumberCount+1 + indexDensity=(iMass-1)*(2*wavenumberCount+1)+2*wavenumberCount+1 + return + end subroutine correlationFunctionTermIndices + + subroutine correlationFunctionReduce(self,reduced) + !% Implement a {\normalfont \ttfamily correlationFunction} output analysis reduction. + use Galacticus_Error, only : Galacticus_Error_Report + implicit none + class(outputAnalysisCorrelationFunction), intent(inout) :: self + class(outputAnalysisClass ), intent(inout) :: reduced + + select type (reduced) + class is (outputAnalysisCorrelationFunction) + !$ call OMP_Set_Lock(reduced%accumulateLock) + reduced%meanDensity =reduced%meanDensity +self%meanDensity + reduced%oneHaloTerm =reduced%oneHaloTerm +self%oneHaloTerm + reduced%twoHaloTerm =reduced%twoHaloTerm +self%twoHaloTerm + reduced%countMainBranch =reduced%countMainBranch +self%countMainBranch + reduced%meanDensityMainBranch=reduced%meanDensityMainBranch+self%meanDensityMainBranch + reduced%oneHaloTermMainBranch=reduced%oneHaloTermMainBranch+self%oneHaloTermMainBranch + reduced%twoHaloTermMainBranch=reduced%twoHaloTermMainBranch+self%twoHaloTermMainBranch + reduced%termCovariance =reduced%termCovariance +self%termCovariance + !$ call OMP_Unset_Lock(reduced%accumulateLock) + class default + call Galacticus_Error_Report('incorrect class'//{introspection:location}) + end select + return + end subroutine correlationFunctionReduce + + subroutine correlationFunctionFinalize(self) + !% Implement a {\normalfont \ttfamily correlationFunction} output analysis finalization. + use IO_HDF5 , only : hdf5Object , hdf5Access + use Galacticus_HDF5 , only : galacticusOutputFile + use Numerical_Constants_Astronomical, only : megaParsec + implicit none + class(outputAnalysisCorrelationFunction), intent(inout) :: self + type (hdf5Object ) :: analysesGroup, analysisGroup, & + & dataset + + ! Finalize analysis. + call correlationFunctionFinalizeAnalysis(self) + ! Output the correlation function. + !$ call hdf5Access%set() + analysesGroup=galacticusOutputFile%openGroup('analyses' ) + analysisGroup=analysesGroup %openGroup('correlationFunction'//char(self%label),char(self%comment)) + ! Write metadata describing this analysis. + call analysisGroup%writeAttribute(char(self%comment) ,'description' ) + call analysisGroup%writeAttribute("function1DSequence" ,'type' ) + call analysisGroup%writeAttribute('$s\, [_\chi\mathrm{Mpc}]$' ,'xAxisLabel' ) + call analysisGroup%writeAttribute('$w(s)\, [_\chi\mathrm{Mpc}]$' ,'yAxisLabel' ) + call analysisGroup%writeAttribute(.true. ,'xAxisIsLog' ) + call analysisGroup%writeAttribute(.true. ,'yAxisIsLog' ) + call analysisGroup%writeAttribute('separation' ,'xDataset' ) + call analysisGroup%writeAttribute('correlationFunction' ,'yDataset' ) + call analysisGroup%writeAttribute('correlationFunctionTarget' ,'yDatasetTarget' ) + call analysisGroup%writeAttribute('correlationFunctionCovariance' ,'yCovariance' ) + call analysisGroup%writeAttribute('correlationFunctionCovarianceTarget' ,'yCovarianceTarget' ) + ! Write computed datasets. + call analysisGroup%writeDataset (self%separations ,'separation' ,'Separation' ,datasetReturned=dataset) + call dataset %writeAttribute('ᵪMpc' ,'units' ) + call dataset %writeAttribute(megaParsec ,'unitsInSI' ) + call dataset %close ( ) + call analysisGroup%writeDataset (self%binnedProjectedCorrelation ,'correlationFunction' ,'Projected correlation' ,datasetReturned=dataset) + call dataset %writeAttribute('ᵪMpc' ,'units' ) + call dataset %writeAttribute(megaParsec ,'unitsInSI' ) + call dataset %close ( ) + call analysisGroup%writeDataset (self%binnedProjectedCorrelationCovariance ,'correlationFunctionCovariance' ,'Projected correlation covariance' ,datasetReturned=dataset) + call dataset %writeAttribute('ᵪMpc²' ,'units' ) + call dataset %writeAttribute(megaParsec**2 ,'unitsInSI' ) + call dataset %close ( ) + ! If available, include the log-likelihood and target dataset. + if (allocated(self%binnedProjectedCorrelationTarget)) then + call analysisGroup%writeAttribute( self%logLikelihood() ,'logLikelihood' ) + call analysisGroup%writeAttribute(char(self%targetLabel) ,'targetLabel' ) + call analysisGroup%writeDataset ( self%binnedProjectedCorrelationTarget ,"correlationFunctionTarget" ,'Projected correlation target' ,datasetReturned=dataset) + call dataset %writeAttribute( "ᵪMpc" ,'units' ) + call dataset %writeAttribute( megaParsec ,'unitsInSI' ) + call dataset %close ( ) + call analysisGroup%writeDataset ( self%binnedProjectedCorrelationCovarianceTarget,"correlationFunctionCovarianceTarget",'Projected correlation covariance target',datasetReturned=dataset) + call dataset %writeAttribute( "ᵪMpc²" ,'units' ) + call dataset %writeAttribute( megaParsec**2 ,'unitsInSI' ) + call dataset %close ( ) + end if + call analysisGroup%close ( ) + call analysesGroup%close ( ) + !$ call hdf5Access%unset() + return + end subroutine correlationFunctionFinalize + + subroutine correlationFunctionFinalizeAnalysis(self) + !% Compute final covariances and normalize. +#ifdef USEMPI + use MPI_Utilities , only : mpiSelf +#endif + use Memory_Management , only : allocateArray , deallocateArray + use FFTLogs , only : FFTLog , fftLogSine , fftLogForward + use Tables , only : table1DLogarithmicLinear , tablesIntegrationWeightFunction + use Table_Labels , only : extrapolationTypeExtrapolate + use Linear_Algebra , only : matrix , operator(*) , assignment(=) + use Vectors , only : Matrix_Copy_Upper_To_Lower_Triangle, Vector_Outer_Product + use Numerical_Constants_Math, only : Pi + implicit none + class (outputAnalysisCorrelationFunction), intent(inout) :: self + double precision , allocatable , dimension(: ) :: separation + double precision , allocatable , dimension(:,:) :: powerSpectrumCovariance , jacobian , & + & correlationCovariance , covarianceTmp , & + & projectedCorrelationCovariance, oneTwoHaloCovariance, & + & powerSpectrumValue , correlation , & + & projectedCorrelation + integer (c_size_t ) :: i , j , & + & m , n , & + & indexOneHalo , indexTwoHalo , & + & indexDensity + type (table1DLogarithmicLinear ) :: correlationTable + double precision :: projectedSeparation , binSeparationMinimum, & + & binSeparationMaximum , binWidthLogarithmic + type (matrix ) :: jacobianMatrix , covarianceMatrix + procedure (tablesIntegrationWeightFunction ), pointer :: integrandWeightFunction + + ! If already finalized, no need to do anything. + if (self%finalized) return + self%finalized=.true. +#ifdef USEMPI + ! If running under MPI, perform a summation reduction across all processes. + self%meanDensity =mpiSelf%sum(meanDensity ) + self%oneHaloTerm =mpiSelf%sum(oneHaloTerm ) + self%twoHaloTerm =mpiSelf%sum(twoHaloTerm ) + self%countMainBranch =mpiSelf%sum(countMainBranch ) + self%meanDensityMainBranch=mpiSelf%sum(meanDensityMainBranch) + self%oneHaloTermMainBranch=mpiSelf%sum(oneHaloTermMainBranch) + self%twoHaloTermMainBranch=mpiSelf%sum(twoHaloTermMainBranch) + self%termCovariance =mpiSelf%sum(termCovariance ) +#endif + ! Copy upper to lower triangle of covariance matrix (we've accumulated only the upper triangle). + self%termCovariance=Matrix_Copy_Upper_To_Lower_Triangle(self%termCovariance) + ! Find average density contribution of main branch galaxies in each halo mass bin. + do n=1,self%massCount + where (self%countMainBranch(:) > 0) + self %meanDensityMainBranch( n,:) & + & =+ self%meanDensityMainBranch( n,:) & + & /dble(self%countMainBranch ( :)) + end where + do i=1,self%wavenumberCount + where (self%countMainBranch(:) > 0) + self %oneHaloTermMainBranch(i,n,:) & + & =+ self%oneHaloTermMainBranch(i,n,:) & + & /dble(self%countMainBranch ( :)) + self %twoHaloTermMainBranch(i,n,:) & + & =+ self%twoHaloTermMainBranch(i,n,:) & + & /dble(self%countMainBranch ( :)) + end where + end do + end do + ! Subtract out Poisson component of main branch galaxy variance (since these galaxies are not Poisson distributed). + do m=1,self%massCount + call correlationFunctionTermIndices(m,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + do i=1,self%countBinsMassHalo + ! Density-density. + self %termCovariance ( indexDensity ,indexDensity )= & + & + self%termCovariance ( indexDensity ,indexDensity ) & + & - self%meanDensityMainBranch( m ,i ) **2 & + & * dble(self%countMainBranch ( i )) + ! One-halo-one-halo. + self %termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexOneHalo:indexOneHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexOneHalo:indexOneHalo+self%wavenumberCount-1) & + & -Vector_Outer_Product( & + & self%oneHaloTermMainBranch(:,m ,i ), & + & self%oneHaloTermMainBranch(:,m ,i ) & + & ) & + & * dble(self%countMainBranch ( i )) + ! Two-halo-two-halo. + self %termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1) & + & -Vector_Outer_Product( & + & self%twoHaloTermMainBranch(:,m ,i ), & + & self%twoHaloTermMainBranch(:,m ,i ) & + & ) & + & * dble(self%countMainBranch ( i )) + ! Density-one-halo. + self %termCovariance ( indexDensity ,indexOneHalo:indexOneHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexDensity ,indexOneHalo:indexOneHalo+self%wavenumberCount-1) & + & - self%meanDensityMainBranch( m ,i ) & + & * self%oneHaloTermMainBranch(:,m ,i ) & + & * dble(self%countMainBranch ( i )) + self %termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexDensity )= & + & + self%termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexDensity ) & + & - self%oneHaloTermMainBranch(:,m ,i ) & + & * self%meanDensityMainBranch( m ,i ) & + & * dble(self%countMainBranch ( i )) + ! Density-two-halo. + self %termCovariance ( indexDensity ,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexDensity ,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1) & + & - self%meanDensityMainBranch( m ,i ) & + & * self%twoHaloTermMainBranch(:,m ,i ) & + & * dble(self%countMainBranch ( i )) + self %termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexDensity )= & + & + self%termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexDensity ) & + & - self%twoHaloTermMainBranch(:,m ,i ) & + & * self%meanDensityMainBranch( m ,i ) & + & * dble(self%countMainBranch ( i )) + ! One-halo-two-halo + self %termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexOneHalo:indexOneHalo+self%wavenumberCount-1,indexTwoHalo:indexTwoHalo+self%wavenumberCount-1) & + & -Vector_Outer_Product( & + & self%oneHaloTermMainBranch(:,m ,i ), & + & self%twoHaloTermMainBranch(:,m ,i ) & + & ) & + & * dble(self%countMainBranch ( i )) + self %termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexOneHalo:indexOneHalo+self%wavenumberCount-1)= & + & + self%termCovariance ( indexTwoHalo:indexTwoHalo+self%wavenumberCount-1,indexOneHalo:indexOneHalo+self%wavenumberCount-1) & + & -Vector_Outer_Product( & + & self%twoHaloTermMainBranch(:,m ,i ), & + & self%oneHaloTermMainBranch(:,m ,i ) & + & ) & + & * dble(self%countMainBranch ( i )) + end do + end do + ! Normalize one- and two-halo terms. + call allocateArray(jacobian ,[self%massCount*(2*self%wavenumberCount),self%massCount*(2*self%wavenumberCount+1)]) + call allocateArray(oneTwoHaloCovariance,[self%massCount*(2*self%wavenumberCount),self%massCount*(2*self%wavenumberCount )]) + ! One-halo term. + jacobian=0.0d0 + do n=1,self%massCount + call correlationFunctionTermIndices(n,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + if (self%meanDensity(n) > 0.0d0) then + do i=1,self%wavenumberCount + jacobian((n-1)*(2*self%wavenumberCount)+i,indexOneHalo+i-1)=1.0d0/self%meanDensity(n)**2 + end do + jacobian((n-1)*(2*self%wavenumberCount)+1:(n-1)*(2*self%wavenumberCount)+self%wavenumberCount,indexDensity)=-2.0d0*self%oneHaloTerm(:,n)/self%meanDensity(n)**3 + end if + end do + ! Two-halo term. + do n=1,self%massCount + call correlationFunctionTermIndices(n,self%wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) + if (self%meanDensity(n) > 0.0d0) then + do i=1,self%wavenumberCount + jacobian((n-1)*(2*self%wavenumberCount)+self%wavenumberCount+i,indexTwoHalo+i-1)=1.0d0/self%meanDensity(n) + end do + jacobian((n-1)*(2*self%wavenumberCount)+self%wavenumberCount+1:(n-1)*(2*self%wavenumberCount)+2*self%wavenumberCount,indexDensity)=-self%twoHaloTerm(:,n)/self%meanDensity(n)**2 + end if + end do + jacobianMatrix =jacobian + covarianceMatrix =self%termCovariance + oneTwoHaloCovariance =jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) + do n=1,self%massCount + if (self%meanDensity(n) > 0.0d0) then + self%oneHaloTerm(:,n)=self%oneHaloTerm(:,n)/self%meanDensity(n)**2 + self%twoHaloTerm(:,n)=self%twoHaloTerm(:,n)/self%meanDensity(n) + end if + end do + call deallocateArray(jacobian) + ! Square the two halo term, and multiply by the linear theory power spectrum. + call allocateArray(jacobian ,[self%massCount*(2*self%wavenumberCount),self%massCount*(2*self%wavenumberCount)]) + jacobian=0.0d0 + do n=1,self%massCount + do i=1,self%wavenumberCount + jacobian((n-1)*(2*self%wavenumberCount) +i,(n-1)*(2*self%wavenumberCount) +i)=+1.0d0 + jacobian((n-1)*(2*self%wavenumberCount)+self%wavenumberCount+i,(n-1)*(2*self%wavenumberCount)+self%wavenumberCount+i)=+2.0d0 & + & *self%twoHaloTerm (i,n) & + & *self%powerSpectrum_%power (self%wavenumber(i )) & + & *self%linearGrowthFactorSquared ( n) + end do + end do + jacobianMatrix =jacobian + covarianceMatrix =oneTwoHaloCovariance + oneTwoHaloCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) + do n=1,self%massCount + do i=1,self%wavenumberCount + self%twoHaloTerm(i,n)=+self%twoHaloTerm (i,n) **2 & + & *self%powerSpectrum_%power (self%wavenumber(i )) & + & *self%linearGrowthFactorSquared ( n) + end do + end do + call deallocateArray(jacobian) + ! Construct the final power spectra. + call allocateArray(powerSpectrumValue ,[ self%wavenumberCount,self%massCount ]) + call allocateArray(powerSpectrumCovariance,[self%massCount*self%wavenumberCount,self%massCount* self%wavenumberCount ]) + call allocateArray(jacobian ,[self%massCount*self%wavenumberCount,self%massCount*(2*self%wavenumberCount)]) + jacobian=0.0d0 + do n=1,self%massCount + do i=1,self%wavenumberCount + jacobian((n-1)*self%wavenumberCount+i,(n-1)*(2*self%wavenumberCount) +i)=1.0d0 + jacobian((n-1)*self%wavenumberCount+i,(n-1)*(2*self%wavenumberCount)+self%wavenumberCount+i)=1.0d0 + end do + end do + jacobianMatrix =jacobian + covarianceMatrix =oneTwoHaloCovariance + powerSpectrumCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) + do n=1,self%massCount + powerSpectrumValue(:,n)=self%oneHaloTerm(:,n)+self%twoHaloTerm(:,n) + end do + call deallocateArray(jacobian ) + call deallocateArray(oneTwoHaloCovariance) + ! Allocate correlation function and separation arrays. + call allocateArray(correlation,shape(powerSpectrumValue)) + call allocateArray(separation ,[self%wavenumberCount]) + ! Fourier transform the power spectrum to get the correlation function. + do n=1,self%massCount + call FFTLog( & + & self%wavenumber , & + & separation , & + & +powerSpectrumValue(:,n) & + & *self%wavenumber & + & * 4.0d0*Pi & + & /(2.0d0*Pi)**3 , & + & correlation(:,n) , & + & fftLogSine , & + & fftLogForward & + & ) + correlation(:,n)=correlation(:,n)/separation + end do + ! Compute the covariance of the correlation function. + call allocateArray(covarianceTmp ,[self%massCount*self%wavenumberCount,self%massCount*self%wavenumberCount]) + call allocateArray(correlationCovariance,[self%massCount*self%wavenumberCount,self%massCount*self%wavenumberCount]) + ! Apply wavenumber weighting to the power spectrum covariance. + do n=1,self%massCount + do m=1,self%massCount + do i=1,self%wavenumberCount + do j=1,self%wavenumberCount + powerSpectrumCovariance ((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+j) & + & =+powerSpectrumCovariance((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+j) & + & *self%wavenumber ( i ) & + & *self%wavenumber ( j) & + & *( & + & + 4.0d0*Pi & + & /(2.0d0*Pi)**3 & + & )**2 + end do + end do + end do + end do + ! Derive the covariance of the correlation function by first Fourier transforming each row of the power spectrum covariance + ! matrix, and then Fourier transforming each column. + do n=1,self%massCount + do m=1,self%massCount + do i=1,self%wavenumberCount + call FFTlog( & + & self%wavenumber , & + & separation , & + & powerSpectrumCovariance((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+1:m*self%wavenumberCount), & + & covarianceTmp ((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+1:m*self%wavenumberCount), & + & fftLogSine , & + & fftLogForward & + ) + end do + end do + end do + do n=1,self%massCount + do m=1,self%massCount + do i=1,self%wavenumberCount + call FFTlog( & + & self%wavenumber , & + & separation , & + & covarianceTmp ((n-1)*self%wavenumberCount+1:n*self%wavenumberCount,(m-1)*self%wavenumberCount+i), & + & correlationCovariance ((n-1)*self%wavenumberCount+1:n*self%wavenumberCount,(m-1)*self%wavenumberCount+i), & + & fftLogSine , & + & fftLogForward & + ) + end do + end do + end do + do n=1,self%massCount + do m=1,self%massCount + do i=1,self%wavenumberCount + do j=1,self%wavenumberCount + correlationCovariance ((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+j) & + & =correlationCovariance((n-1)*self%wavenumberCount+i,(m-1)*self%wavenumberCount+j) & + & /separation ( i ) & + & /separation ( j) + end do + end do + end do + end do + call deallocateArray(covarianceTmp) + ! Construct correlation table. + call correlationTable%create(separation(1),separation(self%wavenumberCount),size(separation),extrapolationTypeExtrapolate) + ! Project the correlation function. + call allocateArray(jacobian ,[self%massCount*self%wavenumberCount,self%massCount*self%wavenumberCount]) + call allocateArray(projectedCorrelationCovariance,[self%massCount*self%wavenumberCount,self%massCount*self%wavenumberCount]) + call allocateArray(projectedCorrelation ,[ self%wavenumberCount,self%massCount ]) + jacobian=0.0d0 + integrandWeightFunction => projectionIntegrandWeight + do i=1,self%wavenumberCount + projectedSeparation=correlationTable%x(int(i)) + jacobian(i,1:self%wavenumberCount)=correlationTable%integrationWeights( & + & projectedSeparation , & + & sqrt( & + & +projectedSeparation **2 & + & +self%depthLineOfSight**2 & + & ) , & + & integrandWeightFunction & + & ) + do n=1,self%massCount + if (n > 1) jacobian((n-1)*self%wavenumberCount+i,(n-1)*self%wavenumberCount+1:n*self%wavenumberCount)=jacobian(i,1:self%wavenumberCount) + projectedCorrelation(i,n)=sum(jacobian(i,1:self%wavenumberCount)*correlation(:,n)) + end do + end do + jacobianMatrix =jacobian + covarianceMatrix =correlationCovariance + projectedCorrelationCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) + call deallocateArray(jacobian) + ! If the integral was taken over the half range, 0 < π < πₘₐₓ, rather than the full range, -πₘₐₓ < π < πₘₐₓ, then divide + ! the projected correlation function by two. + if (self%halfIntegral) then + projectedCorrelation =projectedCorrelation /2.0d0 + projectedCorrelationCovariance=projectedCorrelationCovariance/2.0d0**2 + end if + ! Integrate the projected correlation function over bins. + call allocateArray(self%binnedProjectedCorrelation ,[ size(self%separations,kind=c_size_t),self%massCount ]) + call allocateArray(self%binnedProjectedCorrelationCovariance,[self%massCount*size(self%separations,kind=c_size_t),self%massCount*size(self%separations ,kind=c_size_t)]) + call allocateArray( jacobian ,[self%massCount*size(self%separations,kind=c_size_t),self%massCount* self%wavenumberCount ]) + jacobian=0.0d0 + integrandWeightFunction => binningIntegrandWeight + binWidthLogarithmic=log(self%separations(2)/self%separations(1)) + do i=1,size(self%separations) + binSeparationMinimum =self%separations(i)*exp(-0.5d0*binWidthLogarithmic) + binSeparationMaximum =self%separations(i)*exp(+0.5d0*binWidthLogarithmic) + jacobian(i,1:self%wavenumberCount)=correlationTable%integrationWeights( & + & binSeparationMinimum , & + & binSeparationMaximum , & + & integrandWeightFunction & + & ) & + & /Pi & + & /( & + & +binSeparationMaximum**2 & + & -binSeparationMinimum**2 & + & ) + do n=1,self%massCount + if (n > 1) jacobian((n-1)*size(self%separations)+i,(n-1)*self%wavenumberCount+1:n*self%wavenumberCount)=jacobian(i,1:self%wavenumberCount) + self%binnedProjectedCorrelation(i,n)=sum(jacobian(i,1:self%wavenumberCount)*projectedCorrelation(:,n)) + end do + end do + jacobianMatrix =jacobian + covarianceMatrix =projectedCorrelationCovariance + self%binnedProjectedCorrelationCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) + call deallocateArray(jacobian) + call correlationTable%destroy() + ! Apply the integral constraint. + self%binnedProjectedCorrelation=self%binnedProjectedCorrelation/self%integralConstraint + return + + contains + + double precision function projectionIntegrandWeight(separation) + !% The weight function applied to the correlation function when integrating to get the projected correlation function. + implicit none + double precision, intent(in ) :: separation + + if (separation > projectedSeparation) then + projectionIntegrandWeight=2.0d0*separation/sqrt(separation**2-projectedSeparation**2) + else + projectionIntegrandWeight=0.0d0 + end if + return + end function projectionIntegrandWeight + + double precision function binningIntegrandWeight(separation) + !% The weight function applied to the projected correlation function when integrating into bins. + implicit none + double precision, intent(in ) :: separation + + binningIntegrandWeight=2.0d0*Pi*separation + return + end function binningIntegrandWeight + + end subroutine correlationFunctionFinalizeAnalysis + + double precision function correlationFunctionLogLikelihood(self) + !% Return the log-likelihood of a correlationFunction output analysis. + use Linear_Algebra , only : vector , matrix, assignment(=), operator(*) + use Numerical_Constants_Math, only : Pi + use Galacticus_Error , only : Galacticus_Error_Report + implicit none + class (outputAnalysisCorrelationFunction), intent(inout) :: self + double precision , allocatable , dimension(:,:) :: functionCovarianceCombined + double precision , allocatable , dimension(: ) :: functionValueDifference + type (vector ) :: residual + type (matrix ) :: covariance , covarianceInverse + + ! Check for existance of a target distribution. + if (allocated(self%binnedProjectedCorrelationTarget)) then + ! Finalize analysis. + call correlationFunctionFinalizeAnalysis(self) + ! Allocate workspaces. + allocate(functionCovarianceCombined(self%binCount*self%massCount,self%binCount*self%massCount)) + allocate(functionValueDifference (self%binCount*self%massCount )) + ! Find combined covariance and difference between model and target. + functionValueDifference =reshape( & + & +self%binnedProjectedCorrelation & + & -self%binnedProjectedCorrelationTarget , & + & [ & + & +self%binCount & + & *self%massCount & + & ] & + & ) + functionCovarianceCombined= +self%binnedProjectedCorrelationCovariance & + & +self%binnedProjectedCorrelationCovarianceTarget + residual =functionValueDifference + covariance =functionCovarianceCombined + ! Find the inverse covariance matrix of the combined model and target covariances. + covarianceInverse=covariance%invert() + ! Compute the log-likelihood. + correlationFunctionLogLikelihood=-0.5d0*(residual*(covarianceInverse*residual)) & + & -0.5d0*covariance%determinant() & + & -0.5d0*dble(self%binCount)*log(2.0d0*Pi) + else + correlationFunctionLogLikelihood=0.0d0 + call Galacticus_Error_Report('no target distribution was provided for likelihood calculation'//{introspection:location}) + end if + return + end function correlationFunctionLogLikelihood diff --git a/source/galacticus.output.analyses.correlation_function.Hearin2014_SDSS.F90 b/source/galacticus.output.analyses.correlation_function.Hearin2014_SDSS.F90 new file mode 100644 index 000000000..c0fa856fe --- /dev/null +++ b/source/galacticus.output.analyses.correlation_function.Hearin2014_SDSS.F90 @@ -0,0 +1,288 @@ +!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, +!! 2019 +!! Andrew Benson +!! +!! This file is part of Galacticus. +!! +!! Galacticus is free software: you can redistribute it and/or modify +!! it under the terms of the GNU General Public License as published by +!! the Free Software Foundation, either version 3 of the License, or +!! (at your option) any later version. +!! +!! Galacticus is distributed in the hope that it will be useful, +!! but WITHOUT ANY WARRANTY; without even the implied warranty of +!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +!! GNU General Public License for more details. +!! +!! You should have received a copy of the GNU General Public License +!! along with Galacticus. If not, see . + +!% Contains a module which implements a correlation function output analysis class for the \cite{hearin_dark_2013} analysis. + + !# + !# A correlation function output analysis class for the \cite{hearin_dark_2013} analysis. + !# + type, extends(outputAnalysisCorrelationFunction) :: outputAnalysisCorrelationFunctionHearin2013SDSS + !% A correlation function function output analysis class for the \cite{hearin_dark_2013} analysis. + private + end type outputAnalysisCorrelationFunctionHearin2013SDSS + + interface outputAnalysisCorrelationFunctionHearin2013SDSS + !% Constructors for the ``correlationFunctionHearin2013SDSS'' output analysis class. + module procedure correlationFunctionHearin2013SDSSConstructorParameters + module procedure correlationFunctionHearin2013SDSSConstructorInternal + end interface outputAnalysisCorrelationFunctionHearin2013SDSS + +contains + + function correlationFunctionHearin2013SDSSConstructorParameters(parameters) result (self) + !% Constructor for the ``correlationFunctionHearin2013SDSS'' output analysis class which takes a parameter set as input. + use :: Input_Parameters, only : inputParameter, inputParameters + use, intrinsic :: ISO_C_Binding , only : c_size_t + implicit none + type (outputAnalysisCorrelationFunctionHearin2013SDSS) :: self + type (inputParameters ), intent(inout) :: parameters + class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ + class (linearGrowthClass ), pointer :: linearGrowth_ + class (outputTimesClass ), pointer :: outputTimes_ + class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ + class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ + class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_ + class (powerSpectrumClass ), pointer :: powerSpectrum_ + double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient, systematicErrorPolynomialCoefficient + double precision :: massHaloMinimum , massHaloMaximum , & + & randomErrorMinimum , randomErrorMaximum + integer :: massHaloBinsPerDecade + + ! Check and read parameters. + if (parameters%isPresent( 'randomErrorPolynomialCoefficient')) then + allocate( randomErrorPolynomialCoefficient(parameters%count( 'randomErrorPolynomialCoefficient'))) + else + allocate( randomErrorPolynomialCoefficient(1 )) + end if + if (parameters%isPresent('systematicErrorPolynomialCoefficient')) then + allocate(systematicErrorPolynomialCoefficient(parameters%count('systematicErrorPolynomialCoefficient'))) + else + allocate(systematicErrorPolynomialCoefficient(1 )) + end if + !# + !# randomErrorMinimum + !# parameters + !# randomErrorMinimum + !# 0.07d0 + !# The minimum random error for SDSS stellar masses. + !# float + !# 0..1 + !# + !# + !# randomErrorMaximum + !# parameters + !# randomErrorMaximum + !# 0.07d0 + !# The minimum random error for SDSS stellar masses. + !# float + !# 0..1 + !# + !# + !# randomErrorPolynomialCoefficient + !# parameters + !# randomErrorPolynomialCoefficient + !# [0.07d0] + !# The coefficients of the random error polynomial for SDSS stellar masses. + !# float + !# 0..1 + !# + !# + !# systematicErrorPolynomialCoefficient + !# parameters + !# systematicErrorPolynomialCoefficient + !# [0.0d0] + !# The coefficients of the systematic error polynomial for SDSS stellar masses. + !# float + !# 0..1 + !# + !# + !# massHaloBinsPerDecade + !# 0..1 + !# 10 + !# The number of bins per decade of halo mass to use when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# massHaloMinimum + !# 0..1 + !# 1.0d8 + !# The minimum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# massHaloMaximum + !# 0..1 + !# 1.0d16 + !# The maximum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. + !# output + !# parameters + !# real + !# + !# + !# + !# + !# + !# + !# + !# + self=outputAnalysisCorrelationFunctionHearin2013SDSS(massHaloBinsPerDecade,massHaloMinimum, massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,linearGrowth_,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_) + !# + !# + !# + !# + !# + !# + !# + !# + return + end function correlationFunctionHearin2013SDSSConstructorParameters + + function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,linearGrowth_,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_) result (self) + !% Constructor for the ``correlationFunctionHearin2013SDSS'' output analysis class for internal use. + use, intrinsic :: ISO_C_Binding , only : c_size_t + use :: Galacticus_Paths , only : galacticusPath , pathTypeDataStatic + use :: Cosmology_Parameters , only : cosmologyParametersSimple + use :: Output_Analysis_Property_Operators, only : outputAnalysisPropertyOperatorCsmlgyLmnstyDstnc + implicit none + type (outputAnalysisCorrelationFunctionHearin2013SDSS ) :: self + double precision , intent(in ) , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient + double precision , intent(in ) :: massHaloMinimum , massHaloMaximum , & + & randomErrorMinimum , randomErrorMaximum + integer , intent(in ) :: massHaloBinsPerDecade + class (linearGrowthClass ), intent(in ), target :: linearGrowth_ + class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_ + class (outputTimesClass ), intent(in ), target :: outputTimes_ + class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_ + class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_ + class (haloModelPowerSpectrumModifierClass ), intent(in ), target :: haloModelPowerSpectrumModifier_ + class (powerSpectrumClass ), intent(in ), target :: powerSpectrum_ + type (cosmologyParametersSimple ), pointer :: cosmologyParametersData_ + type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctionsData_ + type (galacticFilterStellarMass ), pointer :: galacticFilter_ + type (surveyGeometryHearin2014SDSS ), pointer :: surveyGeometry_ + type (outputAnalysisPropertyOperatorLog10 ), pointer :: massPropertyOperatorLog10_ + type (outputAnalysisPropertyOperatorSystmtcPolynomial ) , pointer :: massPropertyOperatorSystmtcPolynomial_ + type (outputAnalysisDistributionOperatorRandomErrorPlynml) , pointer :: massDistributionOperator_ + type (outputAnalysisPropertyOperatorCsmlgyLmnstyDstnc ), pointer :: massPropertyOperatorCsmlgyLmnstyDstnc_ + type (outputAnalysisPropertyOperatorCsmlgyAnglrDstnc ), pointer :: separationPropertyOperator_ + type (nodePropertyExtractorMassStellar ), pointer :: massPropertyExtractor_ + type (outputAnalysisPropertyOperatorSequence ), pointer :: massPropertyOperator_ + type (propertyOperatorList ), pointer :: propertyOperators_ + double precision , parameter :: errorPolynomialZeroPoint =11.3d+0 + integer (c_size_t ), parameter :: wavenumberCount =60 + double precision , parameter :: wavenumberMinimum = 1.0d-3, wavenumberMaximum=1.0d+4 + logical , parameter :: halfIntegral =.false. + + ! Build a filter which selects galaxies above some minimum stellar mass. + allocate(galacticFilter_ ) + !# + ! Build the SDSS survey geometry of Hearin et al. (2013) with their imposed redshift limits. + allocate(surveyGeometry_ ) + !# + ! Create the data cosmology. + allocate(cosmologyParametersData_) + allocate(cosmologyFunctionsData_ ) + !# + !# + !# cosmologyParametersSimple ( & + !# & OmegaMatter = 0.27d0, & + !# & OmegaDarkEnergy= 0.73d0, & + !# & HubbleConstant =70.00d0, & + !# & temperatureCMB = 0.00d0, & + !# & OmegaBaryon = 0.00d0 & + !# & ) + !# + !# + !# + !# + !# cosmologyFunctionsMatterLambda( & + !# & cosmologyParametersData_ & + !# & ) + !# + !# + ! Stellar mass property extractor. + allocate(massPropertyExtractor_ ) + !# + ! Sequence of property operators to correct for cosmological model, convert to logarithm, and apply systematic errors. + allocate(massPropertyOperatorCsmlgyLmnstyDstnc_) + !# + allocate(massPropertyOperatorLog10_ ) + !# + ! Systematic error model. + allocate(massPropertyOperatorSystmtcPolynomial_) + !# + allocate(propertyOperators_ ) + allocate(propertyOperators_%next ) + allocate(propertyOperators_%next%next ) + propertyOperators_ %operator_ => massPropertyOperatorCsmlgyLmnstyDstnc_ + propertyOperators_%next %operator_ => massPropertyOperatorLog10_ + propertyOperators_%next%next%operator_ => massPropertyOperatorSystmtcPolynomial_ + allocate(massPropertyOperator_ ) + !# + ! Build a random error distribution operator. + allocate(massDistributionOperator_) + !# + !# + !# outputAnalysisDistributionOperatorRandomErrorPlynml( & + !# & randomErrorMinimum , & + !# & randomErrorMaximum , & + !# & errorPolynomialZeroPoint , & + !# & randomErrorPolynomialCoefficient & + !# & ) + !# + !# + ! Build an operator for separations which corrects for cosmological model. + allocate(separationPropertyOperator_) + !# + ! Build the object. + self%outputAnalysisCorrelationFunction= & + & outputAnalysisCorrelationFunction( & + & var_str('Hearin2013SDSS' ) , & + & var_str('Correlation function for the Hearin et al. (2013) SDSS analysis') , & + & char(galacticusPath(pathTypeDataStatic)//'/observations/correlationFunctions/Projected_Correlation_Functions_Hearin_2013.hdf5'), & + & massHaloBinsPerDecade , & + & massHaloMinimum , & + & massHaloMaximum , & + & wavenumberCount , & + & wavenumberMinimum , & + & wavenumberMaximum , & + & halfIntegral , & + & linearGrowth_ , & + & galacticFilter_ , & + & surveyGeometry_ , & + & cosmologyFunctions_ , & + & outputTimes_ , & + & darkMatterProfileDMO_ , & + & darkMatterHaloBias_ , & + & haloModelPowerSpectrumModifier_ , & + & powerSpectrum_ , & + & massDistributionOperator_ , & + & massPropertyOperator_ , & + & separationPropertyOperator_ , & + & massPropertyExtractor_ & + & ) + ! Clean up. + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + !# + nullify(propertyOperators_) + return + end function correlationFunctionHearin2013SDSSConstructorInternal diff --git a/source/galacticus.output.analysis.correlation_function.F90 b/source/galacticus.output.analysis.correlation_function.F90 deleted file mode 100644 index 59c2f613d..000000000 --- a/source/galacticus.output.analysis.correlation_function.F90 +++ /dev/null @@ -1,1323 +0,0 @@ -!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, -!! 2019 -!! Andrew Benson -!! -!! This file is part of Galacticus. -!! -!! Galacticus is free software: you can redistribute it and/or modify -!! it under the terms of the GNU General Public License as published by -!! the Free Software Foundation, either version 3 of the License, or -!! (at your option) any later version. -!! -!! Galacticus is distributed in the hope that it will be useful, -!! but WITHOUT ANY WARRANTY; without even the implied warranty of -!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -!! GNU General Public License for more details. -!! -!! You should have received a copy of the GNU General Public License -!! along with Galacticus. If not, see . - -!% Contains a module which performs analysis to compute a variety of correlation functions. - -module Galacticus_Output_Analyses_Correlation_Functions - !% Performs analysis to compute a variety of correlation functions. - use, intrinsic :: ISO_C_Binding - use :: Galacticus_Nodes, only : treeNode - use Galactic_Structure_Options - use Geometry_Surveys - use Numerical_Constants_Astronomical - use Numerical_Constants_Prefixes - implicit none - private - public :: Galacticus_Output_Analysis_Correlation_Functions, Galacticus_Output_Analysis_Correlation_Functions_Output - - ! Record of module initialization. - logical :: moduleInitialized =.false. - - ! Record of whether this analysis is active. - logical :: analysisActive - - ! Number of supported mass functions. - integer , parameter :: correlationFunctionsSupportedCount=1 - - ! Labels for supported mass functions. - character(len=25), dimension(correlationFunctionsSupportedCount) :: correlationFunctionLabels= & - & [ & - & 'sdssClusteringZ0.07' & - & ] - - ! Interface for mass mapping functions. - abstract interface - double precision function Map_Mass(mass,node) - import treeNode - double precision , intent(in ) :: mass - type (treeNode), intent(inout), pointer :: node - end function Map_Mass - end interface - - ! Interface for mass error functions. - abstract interface - double precision function Mass_Error(mass,node) - import treeNode - double precision , intent(in ) :: mass - type (treeNode), intent(inout), pointer :: node - end function Mass_Error - end interface - - ! Type for descriptors of correlation functions. - type :: correlationFunctionDescriptor - double precision :: massSystematicLogM0 - procedure (Mass_Error ), pointer , nopass :: massRandomErrorFunction - double precision :: massLogarithmicMinimum - integer :: massSystematicCoefficientCount, massRandomCoefficientCount - integer :: massType - double precision :: massUnitsInSI - logical :: halfIntegral - character (len= 32 ) :: label - character (len=128 ) :: comment - procedure (Map_Mass ), pointer , nopass :: mapMass - class (surveyGeometryClass), allocatable :: geometry - end type correlationFunctionDescriptor - - ! Correlation function descriptors. - type(correlationFunctionDescriptor), dimension(correlationFunctionsSupportedCount), target :: correlationFunctionDescriptors= & - & [ & - ! Hearin et al. (2013) SDSS. - & correlationFunctionDescriptor( & - & 11.300d0 , & - & null() , & - & 8.000d0 , & - & 2 , & - & 2 , & - & massTypeStellar , & - & massSolar , & - & .false. , & - & 'sdssClusteringZ0.07' , & - & 'SDSS galaxy clustering at z=0.07' , & - & null() , & - & null() & - & ) & - & ] - - ! Type to store size functions. - type :: correlationFunction - ! Copy of the mass function descriptor for this mass function. - type (correlationFunctionDescriptor), pointer :: descriptor - ! Parameters for the systematic error model. - double precision , allocatable, dimension(: ) :: massSystematicCoefficients, massRandomCoefficients - double precision :: massRandomMinimum , massRandomMaximum - ! Weights to apply to each output. - double precision , allocatable, dimension(:,: ) :: outputWeight - ! Mass range. - double precision , allocatable, dimension(: ) :: massMinimum - double precision , allocatable, dimension(: ) :: massMinimumLogarithmic - ! Separations. - double precision , allocatable, dimension(: ) :: separation - ! Integral constraint. - double precision , allocatable, dimension(:,: ) :: integralConstraint - ! Line-of-sight integration depth. - double precision :: lineOfSightDepth - ! Population statistics. - double precision , allocatable, dimension(: ) :: meanDensity - ! Density and count of galaxies on the main branches of trees. - double precision , allocatable, dimension(:,: ) :: meanDensityMainBranch - double precision , allocatable, dimension(:,:,:) :: oneHaloTermMainBranch, twoHaloTermMainBranch - integer , allocatable, dimension(: ) :: countMainBranch - ! Power spectrum wavenumbers. - double precision , allocatable, dimension(: ) :: wavenumber - ! Power spectra. - double precision , allocatable, dimension(:,: ) :: oneHaloTerm , twoHaloTerm - ! Covariances. - double precision , allocatable, dimension(:,: ) :: termCovariance - ! Cosmology conversion factors. - double precision , allocatable, dimension(: ) :: cosmologyConversionMass , cosmologyConversionSize - ! Weighted linear growth factor. - double precision , allocatable, dimension(: ) :: linearGrowthFactor - end type correlationFunction - - ! Correlation functions. - type(correlationFunction), allocatable, dimension(:) :: correlationFunctions - - ! Type for storing temporary size functions during cumulation. - type :: correlationFunctionWork - double precision , allocatable, dimension(:,:) :: satelliteProbability - double precision , allocatable, dimension( :) :: centralProbability , fourierProfile - integer :: satelliteCount - integer (c_size_t ) :: outputNumber - double precision :: haloBias , haloWeight , & - & haloTime , haloMass , & - & hostMass - logical :: propertiesSet , isMainBranch , & - & initialized - ! Indices of current halo. - integer (kind=kind_int8) :: treeIndex , haloIndex - end type correlationFunctionWork - - ! Work array. - type(correlationFunctionWork), allocatable, dimension(:) :: haloWork - !$omp threadprivate(haloWork) - - ! Halo mass binning. - double precision :: analysisProjectedCorrelationFunctionsHaloMassMinimum , analysisProjectedCorrelationFunctionsHaloMassMaximum , & - & analysisProjectedCorrelationFunctionsHaloMassMinimumLogarithmic, haloMassIntervalLogarithmicInverse - integer :: analysisProjectedCorrelationFunctionsHaloMassBinsCount , analysisProjectedCorrelationFunctionsHaloMassBinsPerDecade - - abstract interface - double precision function integrandTemplate(x) - double precision, intent(in ) :: x - end function integrandTemplate - end interface - -contains - - !# - !# Galacticus_Output_Analysis_Correlation_Functions - !# - subroutine Galacticus_Output_Analysis_Correlation_Functions(tree,node,nodeStatus,iOutput,mergerTreeAnalyses) - !% Construct correlation functions to compare to various observational determinations. - use, intrinsic :: ISO_C_Binding - use :: Galacticus_Nodes, only : mergerTree - use Galacticus_Paths - use ISO_Varying_String - use Memory_Management - use Cosmology_Parameters - use Galactic_Structure_Enclosed_Masses - use Input_Parameters - use Output_Times - use Galacticus_Error - use Cosmology_Functions - use String_Handling - use Galacticus_Output_Analyses_Cosmology_Scalings - use Numerical_Comparison - use Numerical_Ranges - use IO_HDF5 - use Linear_Growth - use Galacticus_Output_Merger_Tree_Data - implicit none - type (mergerTree ), intent(inout) :: tree - type (treeNode ), intent(inout), pointer :: node - integer , intent(in ) :: nodeStatus - integer (c_size_t ), intent(in ) :: iOutput - type (varying_string ), intent(in ), dimension(: ) :: mergerTreeAnalyses - class (cosmologyFunctionsClass ) , pointer :: cosmologyFunctionsModel - class (linearGrowthClass ) , pointer :: linearGrowth_ - integer , parameter :: wavenumberCount =60 - double precision , parameter :: wavenumberMinimum=0.001d0, wavenumberMaximum=10000.0d0 - type (cosmologyFunctionsMatterLambda) :: cosmologyFunctionsObserved - type (cosmologyParametersSimple ), pointer :: cosmologyParametersObserved => null() - class (outputTimesClass ), pointer :: outputTimes_ - double precision :: mass, massLogarithmic, dataHubbleParameter, dataOmegaMatter, dataOmegaDarkEnergy, redshift, timeMinimum, timeMaximum, distanceMinimum, distanceMaximum, weight - integer :: i,j,k,m, currentAnalysis, activeAnalysisCount,massCount - integer (c_size_t ) :: jOutput - type (varying_string ) :: parameterName, cosmologyScalingMass, cosmologyScalingSize, message - type (hdf5Object ) :: dataFile , parameters, dataset - - ! Initialize the module if necessary. - if (.not.moduleInitialized) then - !$omp critical(Galacticus_Output_Analysis_Correlation_Functions_Initialize) - if (.not.moduleInitialized) then - !# - !# analysisProjectedCorrelationFunctionsHaloMassBinsPerDecade - !# 0..1 - !# 10 - !# The number of bins per decade of halo mass to use when constructing the mass function covariance matrix for main branch galaxies. - !# output - !# globalParameters - !# real - !# - !# - !# analysisProjectedCorrelationFunctionsHaloMassMinimum - !# 0..1 - !# 1.0d8 - !# The minimum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. - !# output - !# globalParameters - !# real - !# - !# - !# analysisProjectedCorrelationFunctionsHaloMassMaximum - !# 0..1 - !# 1.0d16 - !# The maximum halo mass to consider when constructing the mass function covariance matrix for main branch galaxies. - !# output - !# globalParameters - !# real - !# - analysisProjectedCorrelationFunctionsHaloMassMinimumLogarithmic=log10(analysisProjectedCorrelationFunctionsHaloMassMinimum) - analysisProjectedCorrelationFunctionsHaloMassBinsCount=int(log10(analysisProjectedCorrelationFunctionsHaloMassMaximum/analysisProjectedCorrelationFunctionsHaloMassMinimum)*dble(analysisProjectedCorrelationFunctionsHaloMassBinsPerDecade)+0.5d0) - haloMassIntervalLogarithmicInverse=dble(analysisProjectedCorrelationFunctionsHaloMassBinsCount)/log10(analysisProjectedCorrelationFunctionsHaloMassMaximum/analysisProjectedCorrelationFunctionsHaloMassMinimum) - ! Validate correlation function descriptors. - call validateDescriptors(correlationFunctionDescriptors) - ! Establish mapping functions for correlation function descriptors. - correlationFunctionDescriptors(1)%mapMass => null() - ! Determine how many supported mass functions are requested. - activeAnalysisCount=0 - do i=1,correlationFunctionsSupportedCount - if (any(trim(mergerTreeAnalyses) == trim(correlationFunctionLabels(i)))) activeAnalysisCount=activeAnalysisCount+1 - end do - ! Allocate mass function arrays and populate with required data. - if (activeAnalysisCount <= 0) then - analysisActive=.false. - else - analysisActive=.true. - cosmologyFunctionsModel => cosmologyFunctions() - outputTimes_ => outputTimes () - ! Establish survey geometries. - allocate(surveyGeometryHearin2014SDSS :: correlationFunctionDescriptors(1)%geometry) - select type (g => correlationFunctionDescriptors(1)%geometry) - type is (surveyGeometryHearin2014SDSS) - g=surveyGeometryHearin2014SDSS(cosmologyFunctionsModel) - end select - ! Initialize correlation functions. - currentAnalysis=0 - allocate(correlationFunctions(activeAnalysisCount)) - linearGrowth_ => linearGrowth() - do i=1,size(mergerTreeAnalyses) - do j=1,correlationFunctionsSupportedCount - if (mergerTreeAnalyses(i) == trim(correlationFunctionLabels(j))) then - currentAnalysis=currentAnalysis+1 - ! Set a pointer to the descriptor for this size function. - correlationFunctions(currentAnalysis)%descriptor => correlationFunctionDescriptors(j) - ! Read parameters of the systematic error model. - if (correlationFunctionDescriptors(j)%massSystematicCoefficientCount > 0) then - allocate(correlationFunctions(currentAnalysis)%massSystematicCoefficients(correlationFunctionDescriptors(j)%massSystematicCoefficientCount)) - do k=1,correlationFunctionDescriptors(j)%massSystematicCoefficientCount - parameterName=trim(correlationFunctionLabels(j))//'MassSystematic' - parameterName=parameterName//(k-1) - !# - !# char(parameterName) - !# correlationFunctions(currentAnalysis)%massSystematicCoefficients(k) - !# globalParameters - !# (sdssClustering)Z[0-9\.]+MassSystematic[0-9]+ - !# 0.0d0 - !# Correlation function systematic error model parameters. - !# real - !# 1 - !# - end do - end if - ! Read parameters of the random error model. - if (correlationFunctionDescriptors(j)%massRandomCoefficientCount > 0) then - allocate(correlationFunctions(currentAnalysis)%massRandomCoefficients(correlationFunctionDescriptors(j)%massRandomCoefficientCount)) - do k=1,correlationFunctionDescriptors(j)%massRandomCoefficientCount - parameterName=trim(correlationFunctionLabels(j))//'MassRandom' - parameterName=parameterName//(k-1) - !# - !# char(parameterName) - !# correlationFunctions(currentAnalysis)%massRandomCoefficients(k) - !# (sdssClustering)Z[0-9\.]+MassRandom[0-9]+ - !# 0.0d0 - !# globalParameters - !# Correlation function mass random parameters. - !# real - !# 1 - !# - end do - end if - parameterName=trim(correlationFunctionLabels(j))//'MassRandomMinimum' - !# - !# char(parameterName) - !# correlationFunctions(currentAnalysis)%massRandomMinimum - !# (sdssClustering)Z[0-9\.]+MassRandomMinimum - !# 1.0d-3 - !# globalParameters - !# Correlation function mass random minimum. - !# real - !# 1 - !# - parameterName=trim(correlationFunctionLabels(j))//'MassRandomMaximum' - !# - !# char(parameterName) - !# correlationFunctions(currentAnalysis)%massRandomMaximum - !# (sdssClustering)Z[0-9\.]+MassRandomMaximum - !# 1.0d-3 - !# globalParameters - !# Correlation function mass random maximum. - !# real - !# 1 - !# - ! Read the appropriate observational data definition. - select case (trim(correlationFunctionLabels(j))) - case ('sdssClusteringZ0.07') - ! Read data for the Hearin et al. (2013) projected correlation function. - !$ call hdf5Access%set() - call dataFile%openFile(char(galacticusPath(pathTypeDataStatic)//'/observations/correlationFunctions/Projected_Correlation_Functions_Hearin_2013.hdf5'),readOnly=.true.) - ! Extract parameters. - parameters =dataFile%openGroup('Parameters') - call parameters %readAttribute('H_0' ,dataHubbleParameter ) - call parameters %readAttribute('Omega_Matter' ,dataOmegaMatter ) - call parameters %readAttribute('Omega_DE' ,dataOmegaDarkEnergy ) - call parameters %readAttribute('projectedCorrelationFunctionLineOfSightDepth',correlationFunctions(currentAnalysis)%lineOfSightDepth) - call parameters %close() - ! Read cosmology scalings. - dataset=dataFile%openDataset('massMinimum') - call dataset%readAttribute('cosmologyScaling',cosmologyScalingMass,allowPseudoScalar=.true.) - call dataset%close() - dataset=dataFile%openDataset('separation') - call dataset%readAttribute('cosmologyScaling',cosmologyScalingSize,allowPseudoScalar=.true.) - call dataset%close() - ! Extract minimum mass. - call dataFile%readDataset('massMinimum' ,correlationFunctions(currentAnalysis)%massMinimum ) - correlationFunctions(currentAnalysis)%massMinimumLogarithmic=log10(correlationFunctions(currentAnalysis)%massMinimum) - ! Extract separations. - call dataFile%readDataset('separationObserved',correlationFunctions(currentAnalysis)%separation ) - ! Extract integral constraint. - call dataFile%readDataset('integralConstraint',correlationFunctions(currentAnalysis)%integralConstraint) - ! Done. - call dataFile%close() - !$ call hdf5Access%unset() - ! Create the observed cosmology. - allocate(cosmologyParametersObserved) - cosmologyParametersObserved=cosmologyParametersSimple ( & - & OmegaMatter =dataOmegaMatter , & - & OmegaDarkEnergy=dataOmegaDarkEnergy, & - & HubbleConstant =dataHubbleParameter, & - & temperatureCMB =0.0d0 , & - & OmegaBaryon =0.0d0 & - & ) - cosmologyFunctionsObserved =cosmologyFunctionsMatterLambda( & - & cosmologyParametersObserved & - & ) - ! Allocate wavenumbers. - massCount=size(correlationFunctions(currentAnalysis)%massMinimum) - call allocateArray(correlationFunctions(currentAnalysis)%wavenumber ,[wavenumberCount ]) - call allocateArray(correlationFunctions(currentAnalysis)%meanDensity ,[ massCount ]) - call allocateArray(correlationFunctions(currentAnalysis)%meanDensityMainBranch,[ massCount,analysisProjectedCorrelationFunctionsHaloMassBinsCount]) - call allocateArray(correlationFunctions(currentAnalysis)%countMainBranch ,[ analysisProjectedCorrelationFunctionsHaloMassBinsCount]) - call allocateArray(correlationFunctions(currentAnalysis)%oneHaloTermMainBranch,[wavenumberCount,massCount,analysisProjectedCorrelationFunctionsHaloMassBinsCount]) - call allocateArray(correlationFunctions(currentAnalysis)%twoHaloTermMainBranch,[wavenumberCount,massCount,analysisProjectedCorrelationFunctionsHaloMassBinsCount]) - call allocateArray(correlationFunctions(currentAnalysis)%oneHaloTerm ,[wavenumberCount,massCount ]) - call allocateArray(correlationFunctions(currentAnalysis)%twoHaloTerm ,[wavenumberCount,massCount ]) - call allocateArray(correlationFunctions(currentAnalysis)%termCovariance ,[massCount*(2*wavenumberCount+1),massCount*(2*wavenumberCount+1) ]) - correlationFunctions(currentAnalysis)%wavenumber=Make_Range(wavenumberMinimum,wavenumberMaximum,wavenumberCount,rangeTypeLogarithmic) - case default - massCount=0 - call Galacticus_Error_Report('unknown size function'//{introspection:location}) - end select - ! Get cosmological conversion factors. - call allocateArray(correlationFunctions(currentAnalysis)%cosmologyConversionMass,[outputTimes_%count()]) - call allocateArray(correlationFunctions(currentAnalysis)%cosmologyConversionSize,[outputTimes_%count()]) - do jOutput=1,outputTimes_%count() - redshift= & - & cosmologyFunctionsModel %redshiftFromExpansionFactor( & - & cosmologyFunctionsModel%expansionFactor ( & - & outputTimes_%time(jOutput) & - & ) & - & ) - call Cosmology_Conversion_Factors( & - & redshift , & - & cosmologyFunctionsModel , & - & cosmologyFunctionsObserved , & - & cosmologyScalingMass =cosmologyScalingMass , & - & cosmologyScalingSize =cosmologyScalingSize , & - & cosmologyConversionMass =correlationFunctions(currentAnalysis)%cosmologyConversionMass(jOutput), & - & cosmologyConversionSize =correlationFunctions(currentAnalysis)%cosmologyConversionSize(jOutput) & - & ) - end do - nullify(cosmologyParametersObserved) - ! Compute output weights for correlation function. We assume a volume limited survey at the minimum mass. - call allocateArray(correlationFunctions(currentAnalysis)%linearGrowthFactor,[ massCount ]) - call allocateArray(correlationFunctions(currentAnalysis)%outputWeight ,[int(massCount,kind=c_size_t),outputTimes_%count()]) - correlationFunctions(currentAnalysis)%outputWeight =0.0d0 - correlationFunctions(currentAnalysis)%linearGrowthFactor=0.0d0 - do k=1,massCount - do jOutput=1,outputTimes_%count() - do m=1,correlationFunctions(currentAnalysis)%descriptor%geometry%fieldCount() - if (jOutput == outputTimes_%count()) then - timeMaximum= outputTimes_%time(jOutput) - else - timeMaximum=sqrt(outputTimes_%time(jOutput)*outputTimes_%time(jOutput+1)) - end if - if (jOutput == 1) then - timeMinimum= outputTimes_%time(jOutput) - else - timeMinimum=sqrt(outputTimes_%time(jOutput)*outputTimes_%time(jOutput-1)) - end if - distanceMinimum=max( & - & cosmologyFunctionsModel%distanceComoving(timeMaximum) , & - & correlationFunctions(currentAnalysis)%descriptor%geometry%distanceMinimum(correlationFunctions(currentAnalysis)%massMinimum(k),field=m) & - & ) - distanceMaximum=min( & - & cosmologyFunctionsModel%distanceComoving(timeMinimum) , & - & correlationFunctions(currentAnalysis)%descriptor%geometry%distanceMaximum(correlationFunctions(currentAnalysis)%massMinimum(k),field=m) & - & ) - weight=+correlationFunctions(currentAnalysis)%descriptor%geometry%solidAngle(m) & - & /3.0d0 & - & * & - & max( & - & +0.0d0 , & - & +distanceMaximum**3 & - & -distanceMinimum**3 & - & ) - correlationFunctions (currentAnalysis)%outputWeight(k,jOutput) & - & =correlationFunctions(currentAnalysis)%outputWeight(k,jOutput) & - & +weight - correlationFunctions (currentAnalysis)%linearGrowthFactor(k)= & - & +correlationFunctions(currentAnalysis)%linearGrowthFactor(k) & - & +weight & - & *linearGrowth_%value(outputTimes_%time(jOutput))**2 - end do - end do - where(correlationFunctions(currentAnalysis)%outputWeight(k,:) < 0.0d0) - correlationFunctions(currentAnalysis)%outputWeight(k,:)=0.0d0 - end where - if (any(correlationFunctions(currentAnalysis)%outputWeight(k,:) > 0.0d0)) then - correlationFunctions (currentAnalysis)%linearGrowthFactor(k ) & - & =sqrt( & - & + correlationFunctions(currentAnalysis)%linearGrowthFactor(k ) & - & /sum(correlationFunctions(currentAnalysis)%outputWeight (k,:)) & - & ) - correlationFunctions (currentAnalysis)%outputWeight (k,:) & - & = + correlationFunctions(currentAnalysis)%outputWeight (k,:) & - & /sum(correlationFunctions(currentAnalysis)%outputWeight (k,:)) - else - message="correlation function '"//trim(correlationFunctions(currentAnalysis)%descriptor%label)//"' mass bin " - message=message//k//" has zero weights" - call Galacticus_Error_Report(message//{introspection:location}) - end if - end do - ! Initialize population statistics. - correlationFunctions(currentAnalysis)%countMainBranch =0 - correlationFunctions(currentAnalysis)%meanDensity =0.0d0 - correlationFunctions(currentAnalysis)%meanDensityMainBranch=0.0d0 - correlationFunctions(currentAnalysis)%oneHaloTermMainBranch=0.0d0 - correlationFunctions(currentAnalysis)%twoHaloTermMainBranch=0.0d0 - correlationFunctions(currentAnalysis)%oneHaloTerm =0.0d0 - correlationFunctions(currentAnalysis)%twoHaloTerm =0.0d0 - correlationFunctions(currentAnalysis)%termCovariance =0.0d0 - exit - end if - end do - end do - end if - ! Record that module is initialized. - moduleInitialized=.true. - end if - !$omp end critical(Galacticus_Output_Analysis_Correlation_Functions_Initialize) - end if - ! Return if this analysis is not active. - if (.not.analysisActive) return - ! Accumulate halo and return on tree finalization. - if (nodeStatus == nodeStatusFinal) then - if (allocated(haloWork)) then - do i=1,size(correlationFunctions) - if (haloWork(i)%initialized) call Accumulate_Halo(correlationFunctions(i),haloWork(i)) - end do - end if - return - end if - ! Allocate work arrays. - if (.not.allocated(haloWork)) then - allocate(haloWork(size(correlationFunctions))) - haloWork%initialized=.false. - end if - ! Iterate over active analyses. - do i=1,size(correlationFunctions) - ! Return if this correlation function receives no contribution from this output number. - if (all(correlationFunctions(i)%outputWeight(:,iOutput) <= 0.0d0)) cycle - ! Get the galactic mass. - mass= & - & Galactic_Structure_Enclosed_Mass(node,radiusLarge,componentType=componentTypeDisk ,massType=correlationFunctions(i)%descriptor%massType) & - & +Galactic_Structure_Enclosed_Mass(node,radiusLarge,componentType=componentTypeSpheroid,massType=correlationFunctions(i)%descriptor%massType) - if (mass <= 0.0d0) cycle - if (associated(correlationFunctions(i)%descriptor%mapMass)) mass=correlationFunctions(i)%descriptor%mapMass(mass,node) - mass=mass*correlationFunctions(i)%cosmologyConversionMass(iOutput) ! Convert for cosmology. - massLogarithmic=log10(mass) - do j=1,correlationFunctions(i)%descriptor%massSystematicCoefficientCount - massLogarithmic=+massLogarithmic & - & +correlationFunctions(i)%massSystematicCoefficients(j) & - & *( & - & +log10(mass) & - & -correlationFunctions(i)%descriptor%massSystematicLogM0 & - & )**(j-1) - end do - if (massLogarithmic < correlationFunctions(i)%descriptor%massLogarithmicMinimum) cycle - ! Accumulate the node. - call Accumulate_Node(correlationFunctions(i),haloWork(i),tree,node,mass,massLogarithmic,iOutput) - ! Accumulate halo if this is the last node in the tree. - if (nodeStatus == nodeStatusLast) call Accumulate_Halo(correlationFunctions(i),haloWork(i)) - end do - return - end subroutine Galacticus_Output_Analysis_Correlation_Functions - - subroutine Accumulate_Node(correlationFunction_,haloWork,tree,node,mass,massLogarithmic,iOutput) - !% Accumulate a single galaxy to the population of the current halo. Since galaxy masses - !% have random errors, each galaxy added is assigned an inclusion probability, which will be - !% taken into account when evaluating the one- and two-halo terms from this halo in the halo - !% model. - use, intrinsic :: ISO_C_Binding - use :: Galacticus_Nodes, only : mergerTree, nodeComponentBasic - use Dark_Matter_Halo_Biases - use Cosmology_Functions - use Dark_Matter_Profiles_DMO - use Memory_Management - use Galacticus_Output_Merger_Tree_Data - implicit none - type (correlationFunction ), intent(inout) :: correlationFunction_ - type (correlationFunctionWork), intent(inout) :: haloWork - type (mergerTree ), intent(in ) :: tree - type (treeNode ), intent(inout), pointer :: node - double precision , intent(in ) :: mass , massLogarithmic - integer (c_size_t ), intent(in ) :: iOutput - type (treeNode ) , pointer :: hostNode - class (cosmologyFunctionsClass) , pointer :: cosmologyFunctions_ - class (nodeComponentBasic ) , pointer :: basic , basicRoot - class (darkMatterProfileDMOClass ) , pointer :: darkMatterProfileDMO_ - class (darkMatterHaloBiasClass) , pointer :: darkMatterHaloBias_ - double precision , allocatable , dimension(:,:) :: satelliteProbabilityTmp - integer , parameter :: satelliteCountMinimum=100 - integer (kind=kind_int8 ) :: hostIndex - integer :: i, j - double precision :: expansionFactor , galaxyInclusionProbability, & - & randomError - logical :: satelliteIncluded - - ! Get the index of the host halo. - if (node%isSatellite()) then - hostNode => node%parent - else - hostNode => node - end if - hostIndex=hostNode%uniqueID() - ! Allocate arrays. - if (.not.haloWork%initialized) then - call allocateArray(haloWork%centralProbability,[size(correlationFunction_%massMinimumLogarithmic)]) - call allocateArray(haloWork%fourierProfile ,[size(correlationFunction_%wavenumber )]) - allocate (haloWork%satelliteProbability(0,0)) - deallocate(haloWork%satelliteProbability ) - haloWork%propertiesSet =.false. - haloWork%satelliteCount =0 - haloWork%centralProbability=0.0d0 - haloWork%isMainBranch =.false. - haloWork%initialized =.true. - haloWork%treeIndex =-1_kind_int8 - haloWork%haloIndex =-1_kind_int8 - end if - ! Check if the host has changed. - if ( & - & tree %index /= haloWork%treeIndex & - & .or. & - & hostIndex /= haloWork%haloIndex & - & ) & - & call Accumulate_Halo(correlationFunction_,haloWork) - ! Accumulate properties to the current halo. - haloWork%treeIndex=tree%index - haloWork%haloIndex=hostIndex - ! Find the random error on the galaxy mass. - if (associated(correlationFunction_%descriptor%massRandomErrorFunction)) then - randomError=correlationFunction_%descriptor%massRandomErrorFunction(mass,node) - else - randomError=0.0d0 - do j=1,correlationFunction_%descriptor%massRandomCoefficientCount - randomError=+randomError & - & +correlationFunction_%massRandomCoefficients(j) & - & *( & - & +log10(mass) & - & -correlationFunction_%descriptor%massSystematicLogM0 & - & )**(j-1) - end do - randomError=max(min(randomError,correlationFunction_%massRandomMaximum),correlationFunction_%massRandomMinimum) - end if - ! Iterate over mass ranges. - satelliteIncluded=.false. - do j=1,size(correlationFunction_%massMinimumLogarithmic) - ! Find the probability that this galaxy is included in the sample. - galaxyInclusionProbability=0.5d0*(1.0d0-erf((correlationFunction_%massMinimumLogarithmic(j)-massLogarithmic)/randomError/sqrt(2.0d0))) - if (node%isSatellite()) then - if (galaxyInclusionProbability > 0.0d0) then - if (.not.satelliteIncluded) then - satelliteIncluded=.true. - haloWork%satelliteCount=haloWork%satelliteCount+1 - if (.not.allocated(haloWork%satelliteProbability)) then - call allocateArray(haloWork%satelliteProbability,[satelliteCountMinimum,size(correlationFunction_%massMinimumLogarithmic)]) - else if (size(haloWork%satelliteProbability,dim=1) < haloWork%satelliteCount) then - call Move_Alloc(haloWork%satelliteProbability,satelliteProbabilityTmp) - call allocateArray(haloWork%satelliteProbability,[2*size(satelliteProbabilityTmp,dim=1),size(correlationFunction_%massMinimumLogarithmic)]) - haloWork%satelliteProbability(1:size(satelliteProbabilityTmp,dim=1),:)=satelliteProbabilityTmp - call deallocateArray(satelliteProbabilityTmp) - end if - haloWork%satelliteProbability(haloWork%satelliteCount,:)=0.0d0 - end if - haloWork%satelliteProbability(haloWork%satelliteCount,j)=galaxyInclusionProbability - end if - else - haloWork%centralProbability(j)= galaxyInclusionProbability - end if - if (galaxyInclusionProbability > 0.0d0 .and. .not.haloWork%propertiesSet) then - cosmologyFunctions_ => cosmologyFunctions ( ) - darkMatterProfileDMO_ => darkMatterProfileDMO ( ) - darkMatterHaloBias_ => darkMatterHaloBias ( ) - haloWork%propertiesSet = .true. - haloWork%isMainBranch = hostNode %isOnMainBranch( ) - basic => hostNode %basic( ) - basicRoot => tree %baseNode%basic( ) - haloWork%haloMass = basicRoot%mass ( ) - haloWork%hostMass = basic%mass ( ) - haloWork%haloWeight = tree %volumeWeight - haloWork%outputNumber = iOutput - haloWork%haloTime = basic%time ( ) - haloWork%haloBias = darkMatterHaloBias_%bias(hostNode) - expansionFactor = cosmologyFunctions_%expansionFactor(basic%time()) - do i=1,size(correlationFunction_%wavenumber) - ! Note that wavenumbers must be converted from comoving to physical units for the dark matter profile k-space function. - haloWork%fourierProfile(i)=darkMatterProfileDMO_%kSpace( & - & hostNode , & - & correlationFunction_%waveNumber (i ) & - & *correlationFunction_%cosmologyConversionSize(iOutput) & - & /expansionFactor & - & ) - end do - end if - end do - return - end subroutine Accumulate_Node - - subroutine Accumulate_Halo(correlationFunction_,haloWork) - !% Assumulate a single halo's contributions to the halo model one- and two-halo terms. For - !% the one-halo term we count contributions from central-satellite pairs, and from - !% satellite-satellite pairs. Contributions differ in the scalings applied to the - !% Fourier-transformed dark matter halo density profile---see - !% \cite[][\S6.1]{cooray_halo_2002} for a discussion of this. The number of satellites in - !% the halo is assumed to follow a Poisson binomial distribution. - use Math_Distributions_Poisson_Binomial - use Vectors - use Halo_Model_Power_Spectrum_Modifiers - use Linear_Algebra - implicit none - type (correlationFunction ), intent(inout) :: correlationFunction_ - type (correlationFunctionWork ), intent(inout) :: haloWork - double precision , dimension( & - & size(correlationFunction_%wavenumber ), & - & size(correlationFunction_%massMinimumLogarithmic) & - & ) :: oneHaloTerm , twoHaloTerm - double precision , dimension(size(correlationFunction_%massMinimumLogarithmic)) :: galaxyDensity - logical , dimension(size(correlationFunction_%massMinimumLogarithmic)) :: oneHaloTermActive , twoHaloTermActive - double precision , allocatable , dimension(:,: ) :: termJacobian , termCovariance , & - & mainBranchTermCovariance , modifierCovariance - double precision , allocatable , dimension(: ) :: satelliteJacobian , modifierCovarianceDiagonal - class (haloModelPowerSpectrumModifierClass), pointer :: haloModelPowerSpectrumModifier_ - double precision :: satellitePairsCountMean , satelliteCountMean , & - & haloWeightOutput - integer :: wavenumberCount , haloMassBin , & - & i , j , & - & indexOneHalo , indexTwoHalo , & - & indexDensity , massCount - logical :: mainBranchCounted - type (matrix ) :: jacobianMatrix - - ! Return immediately if no nodes have been accumulated. - if (haloWork%treeIndex /= -1_kind_int8) then - oneHaloTermActive=.false. - twoHaloTermActive=.false. - mainBranchCounted=.false. - massCount =size(correlationFunction_%massMinimumLogarithmic) - wavenumberCount =size(correlationFunction_%wavenumber ) - allocate(termJacobian (massCount*(2*wavenumberCount+1),haloWork%satelliteCount+1)) - allocate(satelliteJacobian ( haloWork%satelliteCount )) - allocate(modifierCovariance ( wavenumberCount ,wavenumberCount )) - allocate(modifierCovarianceDiagonal(massCount*(2*wavenumberCount+1) )) - haloModelPowerSpectrumModifier_ => haloModelPowerSpectrumModifier() - termJacobian =0.0d0 - modifierCovariance =0.0d0 - modifierCovarianceDiagonal=0.0d0 - ! Iterate over masses. - do i=1,massCount - ! Find mean number of satellites and satellite pairs. - if (haloWork%satelliteCount > 0) then - satelliteCountMean =Poisson_Binomial_Distribution_Mean (haloWork%satelliteProbability(1:haloWork%satelliteCount,i)) - satellitePairsCountMean=Poisson_Binomial_Distribution_Mean_Pairs(haloWork%satelliteProbability(1:haloWork%satelliteCount,i)) - else - satelliteCountMean =0.0d0 - satellitePairsCountMean=0.0d0 - end if - ! Skip if this halo contains no galaxies. - if (haloWork%centralProbability(i) > 0.0d0 .or. satelliteCountMean > 0.0d0) then - ! Compute output halo weight. - haloWeightOutput=haloWork%haloWeight*correlationFunction_%outputWeight(i,haloWork%outputNumber) - ! Compute contribution to galaxy density. - galaxyDensity(i)= haloWeightOutput & - & *( & - & +haloWork%centralProbability(i) & - & +satelliteCountMean & - & ) - ! For main branch galaxies, accumulate their contribution to the density as a function of halo mass, so that we can later subtract this from the variance. - if (haloWork%isMainBranch) then - haloMassBin=floor((log10(haloWork%haloMass)-analysisProjectedCorrelationFunctionsHaloMassMinimumLogarithmic)*haloMassIntervalLogarithmicInverse)+1 - ! Accumulate weights to halo mass arrays. - if (haloMassBin >= 1 .and. haloMassBin <= analysisProjectedCorrelationFunctionsHaloMassBinsCount) then - !$omp critical (Analyses_Correlation_Functions_Main_Branch) - correlationFunction_ %meanDensityMainBranch( i,haloMassBin)= & - & +correlationFunction_%meanDensityMainBranch( i,haloMassBin) & - & +haloWeightOutput & - & *haloWork %centralProbability ( i ) - correlationFunction_ %oneHaloTermMainBranch(:,i,haloMassBin)= & - & +correlationFunction_%oneHaloTermMainBranch(:,i,haloMassBin) & - & +haloWeightOutput & - & *haloWork %centralProbability ( i ) & - & *satelliteCountMean & - & *haloWork%fourierProfile - correlationFunction_ %twoHaloTermMainBranch(:,i,haloMassBin)= & - & +correlationFunction_%twoHaloTermMainBranch(:,i,haloMassBin) & - & +haloWeightOutput & - & *haloWork %centralProbability ( i ) & - & *haloWork%haloBias & - & *haloWork%fourierProfile - !$omp end critical (Analyses_Correlation_Functions_Main_Branch) - ! If this is the first mass bin in which the central, main branch galaxy is seen, increment the number of main branch galaxies. - if (.not.mainBranchCounted) then - mainBranchCounted=.true. - !$omp atomic - correlationFunction_%countMainBranch(haloMassBin)=correlationFunction_%countMainBranch(haloMassBin)+1 - end if - end if - end if - ! Accumulate contribution to galaxy density. - !$omp atomic - correlationFunction_%meanDensity(i)= correlationFunction_%meanDensity(i) & - & +galaxyDensity (i) - ! Compute and accumulate one-halo term. - if (satelliteCountMean > 0.0d0) then - oneHaloTermActive( i)=.true. - oneHaloTerm (:,i)= haloWeightOutput & - & *( & - & +haloWork%centralProbability(i) & - & *satelliteCountMean & - & *haloWork%fourierProfile & - & +satellitePairsCountMean & - & *haloWork%fourierProfile **2 & - & ) - call haloModelPowerSpectrumModifier_%modify( & - & correlationFunction_%wavenumber & - & *correlationFunction_%cosmologyConversionSize(haloWork%outputNumber), & - & haloModelTermOneHalo , & - & oneHaloTerm(:,i) , & - & modifierCovariance , & - & mass=haloWork%hostMass & - & ) - call Term_Indices(i,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - forall(j=1:wavenumberCount) - modifierCovarianceDiagonal(indexOneHalo+j-1)=modifierCovariance(j,j) - end forall - modifierCovarianceDiagonal(indexDensity)=0.0d0 - !$omp critical(Analyses_Correlation_Functions_Accumulate1) - correlationFunction_%oneHaloTerm(:,i)= correlationFunction_%oneHaloTerm(:,i) & - & +oneHaloTerm (:,i) - !$omp end critical(Analyses_Correlation_Functions_Accumulate1) - end if - ! Compute and accumulate two-halo term. - twoHaloTermActive( i)=.true. - twoHaloTerm (:,i)= galaxyDensity(i) & - & *haloWork%haloBias & - & *haloWork%fourierProfile - call haloModelPowerSpectrumModifier_%modify( & - & correlationFunction_%wavenumber & - & *correlationFunction_%cosmologyConversionSize(haloWork%outputNumber), & - & haloModelTermTwoHalo , & - & twoHaloTerm(:,i) , & - & modifierCovariance , & - & mass=haloWork%hostMass & - & ) - call Term_Indices(i,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - forall(j=1:wavenumberCount) - modifierCovarianceDiagonal(indexTwoHalo+j-1)=modifierCovariance(j,j) - end forall - modifierCovarianceDiagonal(indexDensity)=0.0d0 - !$omp critical(Analyses_Correlation_Functions_Accumulate2) - correlationFunction_%twoHaloTerm(:,i)= correlationFunction_%twoHaloTerm(:,i) & - & +twoHaloTerm (:,i) - !$omp end critical(Analyses_Correlation_Functions_Accumulate2) - ! Construct Jacobian of the terms being accumulated. The Jacobian here is an MxN matrix, where M=massCount*(2*wavenumberCount+1) - ! (the number of terms in halo model quantities being accumulated {wavenumberCount for 1- and 2-halo terms, plus a density, for - ! each mass bin}), and N is the total number of galaxies in the halo (number of satellites plus 1 central). - ! Compute indices. - call Term_Indices(i,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - ! One halo terms. - if (haloWork%satelliteCount > 0) then - satelliteJacobian=Poisson_Binomial_Distribution_Mean_Pairs_Jacobian(haloWork%satelliteProbability(1:haloWork%satelliteCount,i))*haloWork%satelliteProbability(1:haloWork%satelliteCount,i) - do j=1,wavenumberCount - termJacobian(indexOneHalo +j -1,1:haloWork%satelliteCount )=haloWeightOutput *haloWork%fourierProfile(j)**2*satelliteJacobian - end do - end if - termJacobian (indexOneHalo:indexOneHalo+wavenumberCount-1, haloWork%satelliteCount+1)=haloWeightOutput *haloWork%fourierProfile *haloWork%centralProbability( i)*satelliteCountMean - ! Two halo terms. - do j=1,wavenumberCount - termJacobian (indexTwoHalo +j -1,1:haloWork%satelliteCount )=haloWeightOutput*haloWork%haloBias*haloWork%fourierProfile(j) *haloWork%satelliteProbability(1:haloWork%satelliteCount,i) - end do - termJacobian (indexTwoHalo:indexTwoHalo+wavenumberCount-1, haloWork%satelliteCount+1)=haloWeightOutput*haloWork%haloBias*haloWork%fourierProfile *haloWork% centralProbability( i) - ! Compute density terms. - termJacobian (indexDensity ,1:haloWork%satelliteCount )=haloWeightOutput *haloWork%satelliteProbability(1:haloWork%satelliteCount,i) - termJacobian (indexDensity , haloWork%satelliteCount+1)=haloWeightOutput *haloWork% centralProbability( i) - end if - end do - ! Construct and accumulate term covariance. - allocate(termCovariance(massCount*(2*wavenumberCount+1),massCount*(2*wavenumberCount+1))) - jacobianMatrix=termJacobian - termCovariance=jacobianMatrix*jacobianMatrix%transpose() - ! Add modifier covariance. - termCovariance=termCovariance+Vector_Outer_Product(modifierCovarianceDiagonal) - ! For main branch galaxies, zero all off-diagonal contributions. - if (haloWork%isMainBranch) then - termJacobian(:,1:haloWork%satelliteCount)=0.0d0 - jacobianMatrix=termJacobian - allocate(mainBranchTermCovariance(massCount*(2*wavenumberCount+1),massCount*(2*wavenumberCount+1))) - mainBranchTermCovariance=jacobianMatrix*jacobianMatrix%transpose() - do i=1,massCount - mainBranchTermCovariance( & - & (i-1)*(2*wavenumberCount+1)+1:i*(2*wavenumberCount+1), & - & (i-1)*(2*wavenumberCount+1)+1:i*(2*wavenumberCount+1) & - & ) & - & =0.0d0 - end do - termCovariance=termCovariance-mainBranchTermCovariance - deallocate(mainBranchTermCovariance) - end if - !$omp critical(Analyses_Correlation_Functions_Accumulate3) - correlationFunction_%termCovariance=correlationFunction_%termCovariance+termCovariance - !$omp end critical(Analyses_Correlation_Functions_Accumulate3) - deallocate(termJacobian ) - deallocate(satelliteJacobian) - end if - ! Reset counts. - if (allocated(haloWork%centralProbability)) haloWork%centralProbability=0.0d0 - haloWork%satelliteCount=0 - haloWork%propertiesSet =.false. - ! Reset indices. - haloWork%treeIndex=-1_kind_int8 - haloWork%haloIndex=-1_kind_int8 - return - end subroutine Accumulate_Halo - - !# - !# Galacticus_Output_Analysis_Correlation_Functions_Output - !# - subroutine Galacticus_Output_Analysis_Correlation_Functions_Output - !% Outputs correlation functions to file. - use, intrinsic :: ISO_C_Binding - use Galacticus_HDF5 - use Power_Spectra - use Array_Utilities - use FFTLogs - use Memory_Management - use Tables - use Table_Labels - use Linear_Algebra - use Vectors - implicit none - double precision , allocatable, dimension(: ) :: separation - double precision , allocatable, dimension(:,:) :: powerSpectrumCovariance , jacobian , & - & correlationCovariance , covarianceTmp , & - & projectedCorrelationCovariance, binnedProjectedCorrelationCovariance, & - & powerSpectrumValue , correlation , & - & projectedCorrelation , binnedProjectedCorrelation , & - & oneTwoHaloCovariance - class (powerSpectrumClass ), pointer :: powerSpectrum_ - integer :: i , k , & - & j , wavenumberCount, m, n, massCount, indexDensity, indexOneHalo, indexTwoHalo - type (hdf5Object ) :: analysisGroup , correlationFunctionGroup , & - & dataset - type (table1DLogarithmicLinear ) :: correlationTable - double precision :: projectedSeparation , binSeparationMinimum , & - & binSeparationMaximum , binWidthLogarithmic - type (matrix ) :: jacobianMatrix , covarianceMatrix - procedure (integrandTemplate ), pointer :: integrandWeightFunction - - ! Return immediately if this analysis is not active. - if (.not.analysisActive) return - ! Get required objects. - powerSpectrum_ => powerSpectrum() - ! Iterate over mass functions. - do k=1,size(correlationFunctions) - ! Copy upper to lower triangle of covariance matrix (we've accumulated only the upper triangle). - correlationFunctions(k)%termCovariance=Matrix_Copy_Upper_To_Lower_Triangle(correlationFunctions(k)%termCovariance) - ! Get count of mass bins and wavenumbers. - massCount =size(correlationFunctions(k)%massMinimumLogarithmic) - wavenumberCount=size(correlationFunctions(k)%wavenumber ) - ! Find average density contribution of main branch galaxies in each halo mass bin. - do n=1,massCount - where (correlationFunctions(k)%countMainBranch(:) > 0) - correlationFunctions (k)%meanDensityMainBranch(n,:) & - & = correlationFunctions(k)%meanDensityMainBranch(n,:) & - & /dble(correlationFunctions(k)%countMainBranch ( :)) - end where - do i=1,wavenumberCount - where (correlationFunctions(k)%countMainBranch(:) > 0) - correlationFunctions (k)%oneHaloTermMainBranch(i,n,:) & - & = correlationFunctions(k)%oneHaloTermMainBranch(i,n,:) & - & /dble(correlationFunctions(k)%countMainBranch ( :)) - correlationFunctions (k)%twoHaloTermMainBranch(i,n,:) & - & = correlationFunctions(k)%twoHaloTermMainBranch(i,n,:) & - & /dble(correlationFunctions(k)%countMainBranch ( :)) - end where - end do - end do - ! Subtract out Poisson component of main branch galaxy variance (since these galaxies are not Poisson distributed). - do m=1,massCount - call Term_Indices(m,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - do i=1,analysisProjectedCorrelationFunctionsHaloMassBinsCount - ! Density-density. - correlationFunctions (k)%termCovariance ( indexDensity ,indexDensity )= & - & + correlationFunctions(k)%termCovariance ( indexDensity ,indexDensity ) & - & - correlationFunctions(k)%meanDensityMainBranch( m ,i ) **2 & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - ! One-halo-one-halo. - correlationFunctions (k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexOneHalo:indexOneHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexOneHalo:indexOneHalo+wavenumberCount-1) & - & - Vector_Outer_Product( & - & correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ), & - & correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ) & - & ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - ! Two-halo-two-halo. - correlationFunctions (k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexTwoHalo:indexTwoHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexTwoHalo:indexTwoHalo+wavenumberCount-1) & - & - Vector_Outer_Product( & - & correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ), & - & correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ) & - & ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - ! Density-one-halo. - correlationFunctions (k)%termCovariance ( indexDensity ,indexOneHalo:indexOneHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexDensity ,indexOneHalo:indexOneHalo+wavenumberCount-1) & - & - correlationFunctions(k)%meanDensityMainBranch( m ,i ) & - & * correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - correlationFunctions (k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexDensity )= & - & + correlationFunctions(k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexDensity ) & - & - correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ) & - & * correlationFunctions(k)%meanDensityMainBranch( m ,i ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - ! Density-two-halo. - correlationFunctions (k)%termCovariance ( indexDensity ,indexTwoHalo:indexTwoHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexDensity ,indexTwoHalo:indexTwoHalo+wavenumberCount-1) & - & - correlationFunctions(k)%meanDensityMainBranch( m ,i ) & - & * correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - correlationFunctions (k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexDensity )= & - & + correlationFunctions(k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexDensity ) & - & - correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ) & - & * correlationFunctions(k)%meanDensityMainBranch( m ,i ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - ! One-halo-two-halo - correlationFunctions (k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexTwoHalo:indexTwoHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexOneHalo:indexOneHalo+wavenumberCount-1,indexTwoHalo:indexTwoHalo+wavenumberCount-1) & - & - Vector_Outer_Product( & - & correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ), & - & correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ) & - & ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - correlationFunctions (k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexOneHalo:indexOneHalo+wavenumberCount-1)= & - & + correlationFunctions(k)%termCovariance ( indexTwoHalo:indexTwoHalo+wavenumberCount-1,indexOneHalo:indexOneHalo+wavenumberCount-1) & - & - Vector_Outer_Product( & - & correlationFunctions(k)%twoHaloTermMainBranch(:,m ,i ), & - & correlationFunctions(k)%oneHaloTermMainBranch(:,m ,i ) & - & ) & - & * dble(correlationFunctions(k)%countMainBranch ( i )) - end do - end do - ! Normalize one- and two-halo terms. - call allocateArray(jacobian ,[massCount*(2*wavenumberCount),massCount*(2*wavenumberCount+1)]) - call allocateArray(oneTwoHaloCovariance,[massCount*(2*wavenumberCount),massCount*(2*wavenumberCount )]) - ! One-halo term. - jacobian=0.0d0 - do n=1,massCount - call Term_Indices(n,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - if (correlationFunctions(k)%meanDensity(n) > 0.0d0) then - do i=1,wavenumberCount - jacobian((n-1)*(2*wavenumberCount)+i,indexOneHalo+i-1)=1.0d0/correlationFunctions(k)%meanDensity(n)**2 - end do - jacobian((n-1)*(2*wavenumberCount)+1:(n-1)*(2*wavenumberCount)+wavenumberCount,indexDensity)=-2.0d0*correlationFunctions(k)%oneHaloTerm(:,n)/correlationFunctions(k)%meanDensity(n)**3 - end if - end do - ! Two-halo term. - do n=1,massCount - call Term_Indices(n,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - if (correlationFunctions(k)%meanDensity(n) > 0.0d0) then - do i=1,wavenumberCount - jacobian((n-1)*(2*wavenumberCount)+wavenumberCount+i,indexTwoHalo+i-1)=1.0d0/correlationFunctions(k)%meanDensity(n) - end do - jacobian((n-1)*(2*wavenumberCount)+wavenumberCount+1:(n-1)*(2*wavenumberCount)+2*wavenumberCount,indexDensity)=-correlationFunctions(k)%twoHaloTerm(:,n)/correlationFunctions(k)%meanDensity(n)**2 - end if - end do - jacobianMatrix =jacobian - covarianceMatrix =correlationFunctions(k)%termCovariance - oneTwoHaloCovariance =jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) - do n=1,massCount - if (correlationFunctions(k)%meanDensity(n) > 0.0d0) then - correlationFunctions(k)%oneHaloTerm(:,n)=correlationFunctions(k)%oneHaloTerm(:,n)/correlationFunctions(k)%meanDensity(n)**2 - correlationFunctions(k)%twoHaloTerm(:,n)=correlationFunctions(k)%twoHaloTerm(:,n)/correlationFunctions(k)%meanDensity(n) - end if - end do - call deallocateArray(jacobian) - ! Square the two halo term, and multiply by the linear theory power spectrum. - call allocateArray(jacobian ,[massCount*(2*wavenumberCount),massCount*(2*wavenumberCount)]) - jacobian=0.0d0 - do n=1,massCount - do i=1,wavenumberCount - jacobian((n-1)*(2*wavenumberCount) +i,(n-1)*(2*wavenumberCount) +i)=+1.0d0 - jacobian((n-1)*(2*wavenumberCount)+wavenumberCount+i,(n-1)*(2*wavenumberCount)+wavenumberCount+i)=+2.0d0& - & * correlationFunctions(k)%twoHaloTerm (i,n) & - & *powerSpectrum_%power(correlationFunctions(k)%wavenumber (i )) & - & * correlationFunctions(k)%linearGrowthFactor( n) **2 - end do - end do - jacobianMatrix =jacobian - covarianceMatrix =oneTwoHaloCovariance - oneTwoHaloCovariance =jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) - do n=1,massCount - do i=1,wavenumberCount - correlationFunctions(k)%twoHaloTerm(i,n)= correlationFunctions(k)%twoHaloTerm (i,n) **2 & - & *powerSpectrum_%power(correlationFunctions(k)%wavenumber (i )) & - & * correlationFunctions(k)%linearGrowthFactor( n) **2 - end do - end do - call deallocateArray(jacobian) - ! Construct the final power spectra. - call allocateArray(powerSpectrumValue ,[ wavenumberCount,massCount ]) - call allocateArray(powerSpectrumCovariance,[massCount*wavenumberCount,massCount* wavenumberCount ]) - call allocateArray(jacobian ,[massCount*wavenumberCount,massCount*(2*wavenumberCount)]) - jacobian=0.0d0 - do n=1,massCount - do i=1,wavenumberCount - jacobian((n-1)*wavenumberCount+i,(n-1)*(2*wavenumberCount) +i)=1.0d0 - jacobian((n-1)*wavenumberCount+i,(n-1)*(2*wavenumberCount)+wavenumberCount+i)=1.0d0 - end do - end do - jacobianMatrix =jacobian - covarianceMatrix =oneTwoHaloCovariance - powerSpectrumCovariance =jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) - do n=1,massCount - powerSpectrumValue(:,n)=correlationFunctions(k)%oneHaloTerm(:,n)+correlationFunctions(k)%twoHaloTerm(:,n) - end do - call deallocateArray(jacobian ) - call deallocateArray(oneTwoHaloCovariance) - ! Allocate correlation function and separation arrays. - call allocateArray(correlation,shape(powerSpectrumValue)) - call allocateArray(separation ,[wavenumberCount]) - ! Fourier transform the power spectrum to get the correlation function. - do n=1,massCount - call FFTLog( & - & correlationFunctions(k)%wavenumber, & - & separation , & - & +powerSpectrumValue(:,n) & - & *correlationFunctions(k)%wavenumber & - & * 4.0d0*Pi & - & /(2.0d0*Pi)**3 , & - & correlation(:,n) , & - & fftLogSine , & - & fftLogForward & - & ) - correlation(:,n)=correlation(:,n)/separation - end do - ! Compute the covariance of the correlation function. - call allocateArray(covarianceTmp ,[massCount*wavenumberCount,massCount*wavenumberCount]) - call allocateArray(correlationCovariance,[massCount*wavenumberCount,massCount*wavenumberCount]) - ! Apply wavenumber weighting to the power spectrum covariance. - do n=1,massCount - do m=1,massCount - do i=1,wavenumberCount - do j=1,wavenumberCount - powerSpectrumCovariance ((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+j) & - & =powerSpectrumCovariance ((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+j) & - & *correlationFunctions(k)%wavenumber( i ) & - & *correlationFunctions(k)%wavenumber( j) & - & *( & - & + 4.0d0*Pi & - & /(2.0d0*Pi)**3 & - & )**2 - end do - end do - end do - end do - ! Derive the covariance of the correlation function by first Fourier transforming each row of the power spectrum covariance - ! matrix, and then Fourier transforming each column. - do n=1,massCount - do m=1,massCount - do i=1,wavenumberCount - call FFTlog( & - & correlationFunctions(k)%wavenumber , & - & separation , & - & powerSpectrumCovariance((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+1:m*wavenumberCount), & - & covarianceTmp ((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+1:m*wavenumberCount), & - & fftLogSine , & - & fftLogForward & - ) - end do - end do - end do - do n=1,massCount - do m=1,massCount - do i=1,wavenumberCount - call FFTlog( & - & correlationFunctions(k)%wavenumber , & - & separation , & - & covarianceTmp ((n-1)*wavenumberCount+1:n*wavenumberCount,(m-1)*wavenumberCount+i), & - & correlationCovariance ((n-1)*wavenumberCount+1:n*wavenumberCount,(m-1)*wavenumberCount+i), & - & fftLogSine , & - & fftLogForward & - ) - end do - end do - end do - do n=1,massCount - do m=1,massCount - do i=1,wavenumberCount - do j=1,wavenumberCount - correlationCovariance ((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+j) & - & =correlationCovariance((n-1)*wavenumberCount+i,(m-1)*wavenumberCount+j) & - & /separation ( i ) & - & /separation ( j) - end do - end do - end do - end do - call deallocateArray(covarianceTmp) - ! Construct correlation table. - call correlationTable%create(separation(1),separation(wavenumberCount),size(separation),extrapolationTypeExtrapolate) - ! Project the correlation function. - call allocateArray(jacobian ,[massCount*wavenumberCount,massCount*wavenumberCount]) - call allocateArray(projectedCorrelationCovariance,[massCount*wavenumberCount,massCount*wavenumberCount]) - call allocateArray(projectedCorrelation ,[wavenumberCount,massCount ]) - jacobian=0.0d0 - integrandWeightFunction => projectionIntegrandWeight - do i=1,wavenumberCount - projectedSeparation=correlationTable%x(i) - jacobian(i,1:wavenumberCount)=correlationTable%integrationWeights( & - & projectedSeparation , & - & sqrt( & - & +projectedSeparation **2 & - & +correlationFunctions(k)%lineOfSightDepth**2 & - & ) , & - & integrandWeightFunction & - & ) - do n=1,massCount - if (n > 1) jacobian((n-1)*wavenumberCount+i,(n-1)*wavenumberCount+1:n*wavenumberCount)=jacobian(i,1:wavenumberCount) - projectedCorrelation(i,n)=sum(jacobian(i,1:wavenumberCount)*correlation(:,n)) - end do - end do - jacobianMatrix =jacobian - covarianceMatrix =correlationCovariance - projectedCorrelationCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) - call deallocateArray(jacobian) - ! If the integral was taken over the half range, 0 binningIntegrandWeight - binWidthLogarithmic=log(correlationFunctions(k)%separation(2)/correlationFunctions(k)%separation(1)) - do i=1,size(correlationFunctions(k)%separation) - binSeparationMinimum =correlationFunctions(k)%separation(i)*exp(-0.5d0*binWidthLogarithmic) - binSeparationMaximum =correlationFunctions(k)%separation(i)*exp(+0.5d0*binWidthLogarithmic) - jacobian(i,1:wavenumberCount)=correlationTable%integrationWeights( & - & binSeparationMinimum , & - & binSeparationMaximum , & - & integrandWeightFunction & - & ) & - & /Pi & - & /( & - & +binSeparationMaximum**2 & - & -binSeparationMinimum**2 & - & ) - do n=1,massCount - if (n > 1) jacobian((n-1)*size(correlationFunctions(k)%separation)+i,(n-1)*wavenumberCount+1:n*wavenumberCount)=jacobian(i,1:wavenumberCount) - binnedProjectedCorrelation(i,n)=sum(jacobian(i,1:wavenumberCount)*projectedCorrelation(:,n)) - end do - end do - jacobianMatrix =jacobian - covarianceMatrix =projectedCorrelationCovariance - binnedProjectedCorrelationCovariance=jacobianMatrix*(covarianceMatrix*jacobianMatrix%transpose()) - call deallocateArray(jacobian) - call correlationTable%destroy() - ! Apply the integral constraint. - binnedProjectedCorrelation=binnedProjectedCorrelation/correlationFunctions(k)%integralConstraint - ! Output the correlation function. - !$ call hdf5Access%set() - analysisGroup =galacticusOutputFile%openGroup('analysis','Model analysis') - correlationFunctionGroup=analysisGroup %openGroup(trim(correlationFunctions(k)%descriptor%label),trim(correlationFunctions(k)%descriptor%comment)) - call correlationFunctionGroup%writeDataset (correlationFunctions(k)%separation ,'separation' ,'Separation' ,datasetReturned=dataset) - call dataset %writeAttribute(megaParsec ,'unitsInSI' ) - call dataset %close ( ) - call correlationFunctionGroup%writeDataset (binnedProjectedCorrelation ,'correlationFunction' ,'Projected correlation' ,datasetReturned=dataset) - call dataset %writeAttribute(megaParsec ,'unitsInSI' ) - call dataset %close ( ) - call correlationFunctionGroup%writeDataset (binnedProjectedCorrelationCovariance,'correlationFunctionCovariance','Projected correlation covariance',datasetReturned=dataset) - call dataset %writeAttribute(megaParsec**2 , 'unitsInSI' ) - call dataset %close ( ) - call correlationFunctionGroup%close ( ) - call analysisGroup %close ( ) - !$ call hdf5Access%unset() - call deallocateArray(binnedProjectedCorrelation ) - call deallocateArray(binnedProjectedCorrelationCovariance) - end do - return - - contains - - double precision function projectionIntegrandWeight(separation) - !% The weight function applied to the correlation function when integrating to get the projected correlation function. - implicit none - double precision, intent(in ) :: separation - - if (separation > projectedSeparation) then - projectionIntegrandWeight=2.0d0*separation/sqrt(separation**2-projectedSeparation**2) - else - projectionIntegrandWeight=0.0d0 - end if - return - end function projectionIntegrandWeight - - double precision function binningIntegrandWeight(separation) - !% The weight function applied to the projected correlation function when integrating into bins. - implicit none - double precision, intent(in ) :: separation - - binningIntegrandWeight=2.0d0*Pi*separation - return - end function binningIntegrandWeight - - end subroutine Galacticus_Output_Analysis_Correlation_Functions_Output - - subroutine Term_Indices(iMass,wavenumberCount,indexOneHalo,indexTwoHalo,indexDensity) - !% Return the indices in the term covariances array at which one-halo, two-halo, and density terms are stored for the given mass. - implicit none - integer, intent(in ) :: iMass , wavenumberCount - integer, intent( out) :: indexOneHalo, indexTwoHalo , indexDensity - - indexOneHalo=(iMass-1)*(2*wavenumberCount+1) +1 - indexTwoHalo=(iMass-1)*(2*wavenumberCount+1)+ wavenumberCount+1 - indexDensity=(iMass-1)*(2*wavenumberCount+1)+2*wavenumberCount+1 - return - end subroutine Term_Indices - - subroutine validateDescriptors(descriptors) - !% Perform some validation on correlation function descriptors to catch potential errors. - use Galacticus_Error - use ISO_Varying_String - implicit none - type (correlationFunctionDescriptor), dimension(:), intent(in ) :: descriptors - type (varying_string ) :: message - character(len=8 ) :: label - integer :: i - - do i=1,size(descriptors) - if ( & - & descriptors(i)%massSystematicLogM0 < 6.0d0 & - & .or. & - & descriptors(i)%massSystematicLogM0 > 16.0d0 & - & ) then - write (label,'(f8.5)') descriptors(i)%massSystematicLogM0 - message="Error model mass zero-point ["//trim(adjustl(label))//"] for correlation function descriptor ["//trim(descriptors(i)%label)//"] is outside of plausible range" - call Galacticus_Error_Report(message//{introspection:location}) - end if - end do - return - end subroutine validateDescriptors - -end module Galacticus_Output_Analyses_Correlation_Functions -