diff --git a/aux/words.dict b/aux/words.dict
index 7b478a1f43..92536c55fb 100644
--- a/aux/words.dict
+++ b/aux/words.dict
@@ -319,6 +319,7 @@ christoph
chunked
chunking
circum
+circumgalactic
circumnuclear
cladistic
collisional
@@ -669,11 +670,13 @@ sanes
scalarize
scalarizes
scalelengths
+scaler
scalings
sendmail
sersic
sexagesimal
shakura
+sidm
sightline
sigmoid
sinusoid
diff --git a/doc/Galacticus.bib b/doc/Galacticus.bib
index 793ad7a529..c43deb311f 100644
--- a/doc/Galacticus.bib
+++ b/doc/Galacticus.bib
@@ -3715,6 +3715,24 @@ @article{jiang_generating_2014
pages = {193--207}
}
+@article{jiang_semi-analytic_2023,
+ title = {A semi-analytic study of self-interacting dark-matter haloes with baryons},
+ volume = {521},
+ issn = {0035-8711},
+ url = {https://ui.adsabs.harvard.edu/abs/2023MNRAS.521.4630J},
+ doi = {10.1093/mnras/stad705},
+ abstract = {We combine the isothermal Jeans model and the model of adiabatic halo contraction into a semi-analytic procedure for computing the density profile of self-interacting dark-matter (SIDM) haloes with the gravitational influence from the inhabitant galaxies. The model agrees well with cosmological SIDM simulations over the entire core-forming stage up to the onset of gravothermal core-collapse. Using this model, we show that the halo response to baryons is more diverse in SIDM than in CDM and depends sensitively on galaxy size, a desirable feature in the context of the structural diversity of bright dwarfs. The fast speed of the method facilitates analyses that would be challenging for numerical simulations - notably, we quantify the SIDM halo response as functions of the baryonic properties, on a fine mesh grid spanned by the baryon-to-total-mass ratio, Mb/Mvir, and galaxy compactness, r1/2/Rvir; we show with high statistical precision that for typical Milky-Way-like systems, the SIDM profiles are similar to their CDM counterparts; and we delineate the regime of core-collapse in the Mb/Mvir - r1/2/Rvir space, for a given cross section and concentration. Finally, we compare the isothermal Jeans model with the more sophisticated gravothermal fluid model, and show that the former yields faster core formation and agrees better with cosmological simulations. We attribute the difference to whether the target CDM halo is used as a boundary condition or as the initial condition for the gravothermal evolution, and thus comment on possible improvements of the fluid model. We have made our model publicly available at https://github.com/JiangFangzhou/SIDM.},
+ urldate = {2023-08-02},
+ journal = {Monthly Notices of the Royal Astronomical Society},
+ author = {Jiang, Fangzhou and Benson, Andrew and Hopkins, Philip F. and Slone, Oren and Lisanti, Mariangela and Kaplinghat, Manoj and Peter, Annika H. G. and Zeng, Zhichao Carton and Du, Xiaolong and Yang, Shengqi and Shen, Xuejian},
+ month = may,
+ year = {2023},
+ note = {ADS Bibcode: 2023MNRAS.521.4630J},
+ keywords = {Astrophysics - Cosmology and Nongalactic Astrophysics, Astrophysics - High Energy Astrophysical Phenomena, cosmology: dark matter, galaxies: dwarf, galaxies: evolution, galaxies: haloes, galaxies: structure},
+ pages = {4630--4644},
+ file = {Full Text PDF:/home/abensonca/.mozilla/firefox/f54gqgdx.default/zotero/storage/PJTZC47T/Jiang et al. - 2023 - A semi-analytic study of self-interacting dark-mat.pdf:application/pdf},
+}
+
@article{johnson_random_2021,
title = {A {Random} {Walk} {Model} for {Dark} {Matter} {Halo} {Concentrations}},
volume = {908},
diff --git a/parameters/reference/evolutionGalaxyFormation.xml b/parameters/reference/evolutionGalaxyFormation.xml
index a573b61919..59d5b95ba7 100644
--- a/parameters/reference/evolutionGalaxyFormation.xml
+++ b/parameters/reference/evolutionGalaxyFormation.xml
@@ -117,9 +117,7 @@
-
-
-
+
diff --git a/parameters/reference/evolutionGalaxyFormationNBody.xml b/parameters/reference/evolutionGalaxyFormationNBody.xml
index e1a5e118c5..7b5c36c1e4 100644
--- a/parameters/reference/evolutionGalaxyFormationNBody.xml
+++ b/parameters/reference/evolutionGalaxyFormationNBody.xml
@@ -107,9 +107,7 @@
-
-
-
+
diff --git a/parameters/treeAugmentMonteCarlo.xml b/parameters/treeAugmentMonteCarlo.xml
index fc132bc45a..2cb35b46b9 100644
--- a/parameters/treeAugmentMonteCarlo.xml
+++ b/parameters/treeAugmentMonteCarlo.xml
@@ -13,7 +13,7 @@
-
+
@@ -204,6 +204,8 @@
+
+
diff --git a/parameters/treeAugmentNBody.xml b/parameters/treeAugmentNBody.xml
index eb1ef70b82..597f1967e8 100644
--- a/parameters/treeAugmentNBody.xml
+++ b/parameters/treeAugmentNBody.xml
@@ -11,7 +11,7 @@
-
+
@@ -166,9 +166,8 @@
-
-
-
+
+
diff --git a/perl/Galacticus/Build/Components/Classes/Deferred.pm b/perl/Galacticus/Build/Components/Classes/Deferred.pm
index 06927f2535..2a174fa813 100644
--- a/perl/Galacticus/Build/Components/Classes/Deferred.pm
+++ b/perl/Galacticus/Build/Components/Classes/Deferred.pm
@@ -84,7 +84,7 @@ sub Class_Deferred_Binding_Attachers {
my $function =
{
type => "void",
- name => $code::classFunctionName."DeferredFunctionSet",
+ name => $code::classFunctionName."DfrrdFnctnSet",
description => "Set the function to be used for the {\\normalfont \\ttfamily ".$binding->{'method'}."} method of the {\\normalfont \\ttfamily ".$class->{'name'}."} component class.",
variables =>
[
@@ -158,6 +158,8 @@ sub Class_Deferred_Binding_Wrappers {
foreach $code::binding ( grep {$_->{'isDeferred'} && $_->{'bindsTo'} eq 'componentClass'} @{$member->{'bindings'}->{'binding'}} ) {
# Create the name of the function.
$code::classFunctionName = $class->{'name'}.ucfirst($code::binding->{'method'});
+ my $specificType = $code::binding->{'interface'}->{'type'} ne "void" && ! exists($intrinsicTypes{$code::binding->{'interface'}->{'type'}});
+ my $isPointer = $specificType && $code::binding->{'interface'}->{'type'} =~ m/,\s*pointer/;
# Create the function if necesasary.
next
if ( exists($classFunctions->{$code::classFunctionName}->{'wrapper'}) );
@@ -201,8 +203,9 @@ CODE
call {$classFunctionName}Deferred({join(",",@arguments)})
CODE
} else {
+ $code::assigner = $isPointer ? " => " : "=";
$function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
- {$classFunctionName}={$classFunctionName}Deferred({join(",",@arguments)})
+ {$classFunctionName}{$assigner}{$classFunctionName}Deferred({join(",",@arguments)})
CODE
}
$function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
diff --git a/perl/Galacticus/Build/Components/Components.pm b/perl/Galacticus/Build/Components/Components.pm
index 3e5b73c84e..420aeb23f9 100644
--- a/perl/Galacticus/Build/Components/Components.pm
+++ b/perl/Galacticus/Build/Components/Components.pm
@@ -158,39 +158,19 @@ sub Build_Node_Component_Class {
},
{
type => "procedure" ,
- name => "enclosedMass" ,
- function => "Node_Component_Enclosed_Mass_Null" ,
- description => "Compute the mass enclosed within a radius." ,
- mappable => "summation" ,
- returnType => "\\doublezero" ,
- arguments => "\\doublezero\\ radius\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin, \\enumWeightBy\\ [weightBy]\\argin, \\intzero\\ [weightIndex]\\argin"
- },
- {
- type => "procedure" ,
- name => "acceleration" ,
- function => "Node_Component_Acceleration_Null" ,
- description => "Compute the gravitational acceleration at a point." ,
- mappable => "summation" ,
- returnType => "\\doubleone" ,
- arguments => "\\doubleone\\ position\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
+ name => "massDistribution" ,
+ function => "Node_Component_Mass_Distribution_Null" ,
+ description => "Return the mass distribution for this component." ,
+ returnType => "\\textcolor{red}{\\textless class(massDistribution)\\textgreater}" ,
+ arguments => "\\textcolor{red}{\\textless type(enumerationComponentTypeType)\\textgreater} [componentType]\\argin, \\textcolor{red}{\\textless type(enumeratioMassTypeType)\\textgreater} [massType]\\argin, \\textcolor{red}{\\textless type(enumeratioWeightByType)\\textgreater} [weightBy]\\argin, \\intzero\\ [weightIndex]\\argin"
},
{
type => "procedure" ,
- name => "chandrasekharIntegral" ,
- function => "Node_Component_Chandrasekhar_Integral_Null" ,
- description => "Compute the Chandrasekhar integral for a given position and velocity." ,
- mappable => "summation" ,
- returnType => "\\doubleone" ,
- arguments => "\\doubleone\\ position\\argin, \\doubleone\\ velocity\\argin, \\doublezero\\ radiusHalfMass\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
- },
- {
- type => "procedure" ,
- name => "tidalTensor" ,
- function => "Node_Component_Tidal_Tensor_Null" ,
- description => "Compute the gravitational tidal tensor at a point." ,
- mappable => "summation" ,
- returnType => "\\textcolor{red}{\\textless type(tensorRank2Dimension3Symmetric)\\textgreater}" ,
- arguments => "\\doubleone\\ position\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
+ name => "massBaryonic" ,
+ function => "Node_Component_Mass_Baryonic_Null" ,
+ description => "Return the total baryonic mass for this component." ,
+ returnType => "\\doublezero" ,
+ arguments => ""
},
{
type => "procedure" ,
@@ -218,33 +198,6 @@ sub Build_Node_Component_Class {
mappable => "summation" ,
returnType => "\\doublezero" ,
arguments => "\\textcolor{red}{\\textless double(3)\\textgreater} positionCylindrical\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin, \\enumWeightBy\\ [weightBy]\\argin, \\intzero\\ [weightIndex]\\argin"
- },
- {
- type => "procedure" ,
- name => "potential" ,
- function => "Node_Component_Potential_Null" ,
- description => "Compute the gravitational potential." ,
- mappable => "summation" ,
- returnType => "\\doublezero" ,
- arguments => "\\doublezero\\ radius\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
- },
- {
- type => "procedure" ,
- name => "rotationCurve" ,
- function => "Node_Component_Rotation_Curve_Null" ,
- description => "Compute the rotation curve." ,
- mappable => "summation" ,
- returnType => "\\doublezero" ,
- arguments => "\\doublezero\\ radius\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
- },
- {
- type => "procedure" ,
- name => "rotationCurveGradient" ,
- function => "Node_Component_Rotation_Curve_Gradient_Null" ,
- description => "Compute the rotation curve gradient." ,
- mappable => "summation" ,
- returnType => "\\doublezero" ,
- arguments => "\\doublezero\\ radius\\argin, \\enumComponentType\\ [componentType]\\argin, \\enumMassType\\ [massType]\\argin"
}
);
# Add meta-property methods.
diff --git a/perl/Galacticus/Build/Components/Implementations/Deferred.pm b/perl/Galacticus/Build/Components/Implementations/Deferred.pm
index 2c444500fe..61efdf2b73 100644
--- a/perl/Galacticus/Build/Components/Implementations/Deferred.pm
+++ b/perl/Galacticus/Build/Components/Implementations/Deferred.pm
@@ -38,13 +38,18 @@ sub Implementation_Deferred_Binding_Pointers {
# Skip non-deferred bindings.
next
unless ( $binding->{'isDeferred'} );
+ # Determine the name of the procedure template.
+ my $baseMember = $member;
+ while ( exists($baseMember->{'extends'}) && grep {$_->{'method'} eq $binding->{'method'}} @{$baseMember->{'extends'}->{'implementation'}->{'bindings'}->{'binding'}} ) {
+ $baseMember = $baseMember->{'extends'};
+ }
# Create a pointer at the implementation level.
my $componentFunctionName = $class->{'name'}.ucfirst($member->{'name'}).ucfirst($binding->{'method'});
push(
@{$build->{'variables'}},
{
intrinsic => "procedure",
- type => $class->{'name'}.ucfirst($member->{'name'}).ucfirst($binding->{'method'}),
+ type => $class->{'name'}.ucfirst($baseMember->{'name'}).ucfirst($binding->{'method'}),
attributes => [ "pointer" ],
variables => [ $componentFunctionName."Deferred" ]
},
@@ -65,17 +70,22 @@ sub Implementation_Deferred_Binding_Attachers {
foreach my $binding ( grep {$_->{'isDeferred'}} @{$member->{'bindings'}->{'binding'}} ) {
# Create the name of the function.
$code::memberFunctionName = $class->{'name'}.ucfirst($member->{'name'}).ucfirst($binding->{'method'});
+ # Determine the name of the procedure template.
+ my $baseMember = $member;
+ while ( exists($baseMember->{'extends'}) && grep {$_->{'method'} eq $binding->{'method'}} @{$baseMember->{'extends'}->{'implementation'}->{'bindings'}->{'binding'}} ) {
+ $baseMember = $baseMember->{'extends'};
+ }
# Create the function.
my $function =
{
type => "void",
- name => $code::memberFunctionName."DeferredFunctionSet",
+ name => $code::memberFunctionName."DfrrdFnctnSet",
description => "Set the function to be used for the {\\normalfont \\ttfamily ".$binding->{'method'}."} method of the {\\normalfont \\ttfamily ".$member->{'name'}."} implementation of the {\\normalfont \\ttfamily ".$class->{'name'}."} component class.",
variables =>
[
{
intrinsic => "procedure",
- type => $class->{'name'}.ucfirst($member->{'name'}).ucfirst($binding->{'method'}),
+ type => $class->{'name'}.ucfirst($baseMember->{'name'}).ucfirst($binding->{'method'}),
isArgument => 1,
variables => [ "deferredFunction" ]
}
@@ -138,12 +148,16 @@ sub Implementation_Deferred_Binding_Wrappers {
# Iterate over deferred bindings which bind at the class level.
foreach $code::binding ( grep {$_->{'isDeferred'}} @{$code::member->{'bindings'}->{'binding'}} ) {
# Create the name of the function.
- $code::memberFunctionName = $code::class->{'name'}.ucfirst($code::member->{'name'}).ucfirst($code::binding->{'method'});
- $code::returnName = $code::memberFunctionName;
+ $code::memberFunctionName = $code::class->{'name'}.ucfirst($code::member->{'name'}).ucfirst($code::binding->{'method'});
+ $code::returnName = $code::memberFunctionName;
+ my $specificType = $code::binding->{'interface'}->{'type'} ne "void" && ! exists($intrinsicTypes{$code::binding->{'interface'}->{'type'}});
+ my $isPointer = $specificType && $code::binding->{'interface'}->{'type'} =~ m/,\s*pointer/;
+ $code::returnName .="_"
+ if ( $specificType );
# Create the function.
my $function =
{
- type => $code::binding->{'interface'}->{'type'} eq "void" ? "void" : $intrinsicTypes{$code::binding->{'interface'}->{'type'}},
+ type => $code::binding->{'interface'}->{'type'} eq "void" ? "void" : ($specificType ? $code::binding->{'interface'}->{'type'}." => ".$code::returnName : $intrinsicTypes{$code::binding->{'interface'}->{'type'}}),
name => $code::memberFunctionName,
description => "Call the deferred function for the {\\normalfont \\ttfamily ".$code::binding->{'method'}."} method of the {\\normalfont \\ttfamily ".$code::class->{'name'}."} component class if it has been set.",
modules =>
@@ -151,6 +165,8 @@ sub Implementation_Deferred_Binding_Wrappers {
"Error"
]
};
+ push(@{$function->{'modules'}},&List::ExtraUtils::as_array($code::binding->{'interface'}->{'module'}))
+ if ( exists($code::binding->{'interface'}->{'module'}) );
# Handle any rank/shape.
if ( exists($code::binding->{'interface'}->{'rank'}) && $code::binding->{'interface'}->{'rank'} > 0 ) {
die('can not specify both "rank" and "shape"')
@@ -193,8 +209,9 @@ CODE
call {$memberFunctionName}Deferred({join(",",@arguments)})
CODE
} else {
+ $code::assigner = $isPointer ? " => " : "=";
$function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
- {$returnName}={$memberFunctionName}Deferred({join(",",@arguments)})
+ {$returnName}{$assigner}{$memberFunctionName}Deferred({join(",",@arguments)})
CODE
}
$function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
@@ -212,8 +229,9 @@ CODE
call self%{$parentType}%{$binding->{'method'}}({join(",",grep {$_ ne "self"} @arguments)})
CODE
} else {
+ $code::assigner = $isPointer ? " => " : "=";
$function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
- {$returnName}=self%{$parentType}%{$binding->{'method'}}({join(",",grep {$_ ne "self"} @arguments)})
+ {$returnName}{$assigner}self%{$parentType}%{$binding->{'method'}}({join(",",grep {$_ ne "self"} @arguments)})
CODE
}
} else {
diff --git a/perl/Galacticus/Build/Components/TreeNodes/Utils.pm b/perl/Galacticus/Build/Components/TreeNodes/Utils.pm
index af22331227..920029b27d 100644
--- a/perl/Galacticus/Build/Components/TreeNodes/Utils.pm
+++ b/perl/Galacticus/Build/Components/TreeNodes/Utils.pm
@@ -19,8 +19,10 @@ use Galacticus::Build::Components::DataTypes;
{
functions =>
[
- \&Tree_Node_Copy,
- \&Tree_Node_Move
+ \&Tree_Node_Copy ,
+ \&Tree_Node_Move ,
+ \&Tree_Node_Mass_Distribution,
+ \&Tree_Node_Mass_Baryonic
]
}
);
@@ -193,4 +195,383 @@ CODE
);
}
+sub Tree_Node_Mass_Distribution {
+ # Generate a function to construct and return the mass distribution associated with a node.
+ my $build = shift();
+ my $function =
+ {
+ type => "class(massDistributionClass), pointer => massDistribution_",
+ name => "treeNodeMassDistribution",
+ description => "Construct and return the mass distribution associated with {\\normalfont \\ttfamily self}.",
+ modules =>
+ [
+ "Mass_Distributions , only : massDistributionClass , massDistributionComposite, massDistributionList , massDistributionZero, kinematicsDistributionClass, kinematicsDistributionIsothermal" ,
+ "Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType , enumerationWeightByType, componentTypeAll , componentTypeDarkMatterOnly, massTypeAll , massTypeDark, weightByMass"
+ ],
+ variables =>
+ [
+ {
+ intrinsic => "class",
+ type => "treeNode",
+ attributes => [ "intent(inout)" ],
+ variables => [ "self" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationComponentTypeType",
+ attributes => [ "intent(in )", "optional" ],
+ variables => [ "componentType" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationMassTypeType",
+ attributes => [ "intent(in )", "optional" ],
+ variables => [ "massType" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationWeightByType",
+ attributes => [ "intent(in )", "optional" ],
+ variables => [ "weightBy" ]
+ },
+ {
+ intrinsic => "integer",
+ attributes => [ "intent(in )", "optional" ],
+ variables => [ "weightIndex" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationComponentTypeType",
+ variables => [ "componentType_", "componentType__" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationMassTypeType",
+ variables => [ "massType_", "massType__" ]
+ },
+ {
+ intrinsic => "type",
+ type => "enumerationWeightByType",
+ variables => [ "weightBy_" ]
+ },
+ {
+ intrinsic => "integer",
+ variables => [ "weightIndex_" ]
+ },
+ {
+ intrinsic => "class",
+ type => "massDistributionClass",
+ attributes => [ "pointer" ],
+ variables => [ "massDistributionComponent" ]
+ },
+ {
+ intrinsic => "integer",
+ variables => [ "i", "iMassDistribution", "iMassDistributionAll", "iMassDistributionConstruct", "iEmpty" ]
+ },
+ {
+ intrinsic => "logical",
+ variables => [ "construct", "isDarkMatterOnly" ]
+ },
+ {
+ intrinsic => "integer",
+ type => "kind_int8",
+ variables => [ "uniqueID", "uniqueIDParent" ]
+ },
+ {
+ intrinsic => "type",
+ type => "massDistributionList",
+ attributes => [ "pointer" ],
+ variables => [ "massDistributionList_", "next_" ]
+ },
+ {
+ intrinsic => "class",
+ type => "kinematicsDistributionClass",
+ attributes => [ "pointer" ],
+ variables => [ "kinematicsDistribution_" ]
+ }
+ ]
+ };
+ $function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
+! Set defaults.
+if (present(componentType)) then
+ componentType_=componentType
+else
+ componentType_=componentTypeAll
+end if
+if (present(massType)) then
+ massType_=massType
+else
+ massType_=massTypeAll
+end if
+if (present(weightBy)) then
+ weightBy_=weightBy
+else
+ weightBy_=weightByMass
+end if
+if (present(weightIndex)) then
+ weightIndex_=weightIndex
+else
+ weightIndex_=-1
+end if
+! Search for a match to our ID.
+iMassDistribution =0
+iMassDistributionAll=0
+iEmpty =0
+uniqueID =self %uniqueID()
+if (associated(self%parent)) then
+ uniqueIDParent =self%parent%uniqueID()
+else
+ uniqueIDParent =-1_kind_int8
+end if
+do i=1,massDistributionsCount
+ if ( &
+ & massDistributions__(i)%uniqueID == uniqueID &
+ & .and. &
+ & massDistributions__(i)%componentType == componentType_ &
+ & .and. &
+ & massDistributions__(i)%massType == massType_ &
+ & .and. &
+ & massDistributions__(i)%weightBy == weightBy_ &
+ & .and. &
+ & massDistributions__(i)%weightIndex == weightIndex_ &
+ & ) then
+ iMassDistribution=i
+ exit
+ else if (massDistributions__(i)%uniqueID < 0_kind_int8 .and. iEmpty == 0) then
+ iEmpty =i
+ end if
+ if ( &
+ & massDistributions__(i)%uniqueID == uniqueID &
+ & .and. &
+ & massDistributions__(i)%componentType == componentTypeAll &
+ & .and. &
+ & massDistributions__(i)%massType == massTypeAll &
+ & .and. &
+ & massDistributions__(i)%weightBy == weightBy_ &
+ & .and. &
+ & massDistributions__(i)%weightIndex == weightIndex_ &
+ & ) then
+ iMassDistributionAll=i
+ end if
+end do
+! If we found no match, we need to create the distribution.
+construct=.false.
+if (iMassDistribution == 0) then
+ isDarkMatterOnly=.false.
+ if (componentType_ == componentTypeDarkMatterOnly) then
+ isDarkMatterOnly=.true.
+ construct =.true.
+ componentType__ =componentTypeDarkMatterOnly
+ massType__ =massType_
+ else if (iMassDistributionAll == 0 ) then
+ construct =.true.
+ componentType__ =componentTypeAll
+ massType__ =massTypeAll
+ end if
+ ! If no existing all/all mass distribution matched.....
+ if (construct) then
+ if (iEmpty /= 0) then
+ ! If we have an empty slot, use that.
+ iMassDistributionConstruct=iEmpty
+ else
+ ! Simply use the next slot, unless it is occupied by a parent node massDistribution (unless we have no choice because we have run out of slots).
+ do i=1,massDistributionsCount+1
+ massDistributionsLast=mod(massDistributionsLast,massDistributionsCount)+1
+ if (massDistributions__(massDistributionsLast)%uniqueID == uniqueIDParent) cycle
+ exit
+ end do
+ iMassDistributionConstruct= massDistributionsLast
+ !![
+
+ !!]
+ massDistributions__(massDistributionsLast)%uniqueID=-huge(kind_int8)
+ end if
+ if (isDarkMatterOnly) then
+ iMassDistribution =iMassDistributionConstruct
+ else
+ iMassDistributionAll=iMassDistributionConstruct
+ end if
+ if (.not.associated(massDistributions__(iMassDistributionConstruct)%massDistribution_)) then
+ massDistributionList_ => null()
+ next_ => null()
+CODE
+ # Iterate over all component classes
+ foreach $code::class ( &List::ExtraUtils::hashList($build->{'componentClasses'}) ) {
+ next
+ unless ( grep {$code::class->{'name'} eq $_} @{$build->{'componentClassListActive'}} );
+ $function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
+ if (allocated(self%component{ucfirst($class->{'name'})})) then
+ do i=1,size(self%component{ucfirst($class->{'name'})})
+ massDistributionComponent => self%component{ucfirst($class->{'name'})}(i)%massDistribution(componentType__,massType__,weightBy_,weightIndex_)
+ if (associated(massDistributionComponent)) then
+ if (associated(massDistributionList_)) then
+ allocate(next_%next)
+ next_ => next_%next
+ else
+ allocate(massDistributionList_)
+ next_ => massDistributionList_
+ end if
+ next_%massDistribution_ => massDistributionComponent
+ next_%next => null()
+ end if
+ end do
+ end if
+CODE
+ }
+ $function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
+ allocate(massDistributionComposite :: massDistributions__(iMassDistributionConstruct)%massDistribution_)
+ select type (massDistribution__ => massDistributions__(iMassDistributionConstruct)%massDistribution_)
+ type is (massDistributionComposite)
+ !![
+
+ !!]
+ end select
+ massDistributions__(iMassDistributionConstruct)%uniqueID =self%uniqueID ()
+ massDistributions__(iMassDistributionConstruct)%componentType= componentType__
+ massDistributions__(iMassDistributionConstruct)%massType = massType__
+ massDistributions__(iMassDistributionConstruct)%weightBy = weightBy_
+ massDistributions__(iMassDistributionConstruct)%weightIndex = weightIndex_
+ next_ => massDistributionList_
+ do while (associated(next_))
+ !![
+
+ !!]
+ next_ => next_%next
+ end do
+ nullify(massDistributionList_ )
+ end if
+ end if
+ if (isDarkMatterOnly) then
+ ! We already have the relevant mass distribution constructed in the iMassDistribution slot - nothing more to do here.
+ else if (componentType_ == componentTypeAll .and. massType_ == massTypeAll) then
+ ! The all/all mass distribution was required - we have just created it, so return it.
+ iMassDistribution=iMassDistributionAll
+ else
+ ! Some other mass distribution was required - get it as a subset of the all/all mass distribution.
+ iEmpty=0
+ do i=1,massDistributionsCount
+ if (massDistributions__(i)%uniqueID < 0_kind_int8 .and. iEmpty == 0) iEmpty=i
+ end do
+ if (iEmpty /= 0) then
+ ! If we have an empty slot, use that.
+ iMassDistribution=iEmpty
+ else
+ ! Simply use the next slot, unless it is occupied by a parent node massDistribution (unless we have no choice because we have run out of slots).
+ do i=1,massDistributionsCount+1
+ massDistributionsLast=mod(massDistributionsLast,massDistributionsCount)+1
+ massDistributionsLast=mod(massDistributionsLast,massDistributionsCount)+1
+ ! But never replace the all/all distribution.
+ if ( &
+ & massDistributions__(massDistributionsLast)%uniqueID == uniqueID &
+ & .and. &
+ & massDistributions__(massDistributionsLast)%componentType == componentTypeAll &
+ & .and. &
+ & massDistributions__(massDistributionsLast)%massType == massTypeAll &
+ & .and. &
+ & massDistributions__(massDistributionsLast)%weightBy == weightBy_ &
+ & .and. &
+ & massDistributions__(massDistributionsLast)%weightIndex == weightIndex_ &
+ & ) cycle
+ if ( &
+ & massDistributions__(massDistributionsLast)%uniqueID == uniqueIDParent &
+ & ) cycle
+ exit
+ end do
+ iMassDistribution=massDistributionsLast
+ !![
+
+ !!]
+ massDistributions__(massDistributionsLast)%uniqueID=-huge(kind_int8)
+ end if
+ massDistributions__(iMassDistribution)%massDistribution_ => massDistributions__(iMassDistributionAll)%massDistribution_%subset (componentType_,massType_)
+ massDistributions__(iMassDistribution)%uniqueID = self %uniqueID ( )
+ massDistributions__(iMassDistribution)%componentType = componentType_
+ massDistributions__(iMassDistribution)%massType = massType_
+ massDistributions__(iMassDistribution)%weightBy = weightBy_
+ massDistributions__(iMassDistribution)%weightIndex = weightIndex_
+ if (.not.associated(massDistributions__(iMassDistribution)%massDistribution_)) then
+ allocate(massDistributionZero :: massDistributions__(iMassDistribution)%massDistribution_)
+ select type (massDistributions___ => massDistributions__(iMassDistribution)%massDistribution_)
+ type is (massDistributionZero)
+ !![
+
+ !!]
+ allocate(kinematicsDistributionIsothermal :: kinematicsDistribution_)
+ select type (kinematicsDistribution_)
+ type is (kinematicsDistributionIsothermal)
+ !![
+
+ !!]
+ end select
+ call massDistributions___%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ end select
+ end if
+ end if
+end if
+!![
+
+!!]
+CODE
+ # Insert a type-binding for this function into the treeNode type.
+ push(
+ @{$build->{'types'}->{'treeNode'}->{'boundFunctions'}},
+ {
+ type => "procedure",
+ descriptor => $function,
+ name => "massDistribution"
+ }
+ );
+}
+
+sub Tree_Node_Mass_Baryonic {
+ # Generate a function to return the total baryonic mass associated with a node.
+ my $build = shift();
+ my $function =
+ {
+ type => "double precision",
+ name => "treeNodeMassBaryonic",
+ description => "Return the total baryonic mass associated with {\\normalfont \\ttfamily self}.",
+ variables =>
+ [
+ {
+ intrinsic => "class",
+ type => "treeNode",
+ attributes => [ "intent(inout)" ],
+ variables => [ "self" ]
+ },
+ {
+ intrinsic => "integer",
+ variables => [ "i" ]
+ }
+ ]
+ };
+ $function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
+treeNodeMassBaryonic=0.0d0
+CODE
+ # Iterate over all component classes
+ foreach $code::class ( &List::ExtraUtils::hashList($build->{'componentClasses'}) ) {
+ next
+ unless ( grep {$code::class->{'name'} eq $_} @{$build->{'componentClassListActive'}} );
+ $function->{'content'} .= fill_in_string(<<'CODE', PACKAGE => 'code');
+ if (allocated(self%component{ucfirst($class->{'name'})})) then
+ do i=1,size(self%component{ucfirst($class->{'name'})})
+ treeNodeMassbaryonic=treeNodeMassBaryonic+self%component{ucfirst($class->{'name'})}(i)%massBaryonic()
+ end do
+ end if
+CODE
+ }
+ # Insert a type-binding for this function into the treeNode type.
+ push(
+ @{$build->{'types'}->{'treeNode'}->{'boundFunctions'}},
+ {
+ type => "procedure",
+ descriptor => $function,
+ name => "massBaryonic"
+ }
+ );
+}
+
1;
diff --git a/perl/Galacticus/Build/SourceTree/Process/Constructors.pm b/perl/Galacticus/Build/SourceTree/Process/Constructors.pm
index 5ef5c738af..63237ed4f3 100755
--- a/perl/Galacticus/Build/SourceTree/Process/Constructors.pm
+++ b/perl/Galacticus/Build/SourceTree/Process/Constructors.pm
@@ -29,6 +29,15 @@ sub Process_Constructors {
my $depth = 0;
while ( $node ) {
if ( $node->{'type'} eq "constructorAssign" && ! $node->{'directive'}->{'processed'} ) {
+ # All variables named in the directive are assigned to
+ # `self`. Optional arguments and allocation of allocatables
+ # are automatically handled. By default standard assignment
+ # is used. If a variable name is prefixed with `*` then
+ # pointer assignment is used, and any `functionClass`
+ # objects have their reference count incremented. If
+ # prefixed with `*/` then pointer assignment is used but no
+ # reference count increment is performed.
+ #
# Assert that our parent is a function.
unless ( $node->{'parent'}->{'type'} eq "function" || $node->{'parent'}->{'type'} eq "moduleProcedure" ) {
my $nodeRoot = $node;
@@ -54,9 +63,10 @@ sub Process_Constructors {
my $assignmentSource = " ! Auto-generated constructor assignment\n";
(my $variables = $node->{'directive'}->{'variables'}) =~ s/^\s*(.*?)\s*$/$1/;
foreach ( grep {$_ ne ""} split(/\s*,\s*/,$variables) ) {
- my $matches = $_ =~ m/^(\*??)([a-zA-Z0-9_]+)/;
+ my $matches = $_ =~ m/^(\*??)(\/??)([a-zA-Z0-9_]+)/;
my $isPointer = $1 eq "*";
- my $argumentName = $2;
+ my $doCount = $2 ne "/";
+ my $argumentName = $3;
my $assigner = $isPointer ? " => " : "=";
my $hasDefault = $_ =~ m/=\s*(.+)/;
my $default = $hasDefault ? $1 : undef();
@@ -65,7 +75,7 @@ sub Process_Constructors {
# Get the variable declaration.
my $declaration;
if ( $node->{'parent'}->{'type'} eq "moduleProcedure" ) {
-
+ # Nothing to do.
} else {
$declaration = &Galacticus::Build::SourceTree::Parse::Declarations::GetDeclaration($node->{'parent'},$argumentName);
}
@@ -90,6 +100,8 @@ sub Process_Constructors {
( $declaration->{'intrinsic'} eq "type" || $declaration->{'intrinsic'} eq "class" )
&&
$isPointer
+ &&
+ $doCount
) {
my $type = lc($declaration->{'type'});
$type =~ s/\s//g;
diff --git a/perl/Galacticus/Build/SourceTree/Process/EventHooks.pm b/perl/Galacticus/Build/SourceTree/Process/EventHooks.pm
index 944973eb77..63c009984d 100644
--- a/perl/Galacticus/Build/SourceTree/Process/EventHooks.pm
+++ b/perl/Galacticus/Build/SourceTree/Process/EventHooks.pm
@@ -496,7 +496,7 @@ CODE
my $waitTimeWriter = fill_in_string(<<'CODE', PACKAGE => 'code');
subroutine eventsHooksWaitTimes()
#ifdef OMPPROFILE
- use :: Galacticus_HDF5 , only : galacticusOutputFile
+ use :: Output_HDF5 , only : outputFile
use :: IO_HDF5 , only : hdf5Object
use :: HDF5_Access , only : hdf5Access
use :: ISO_Varying_String, only : varying_string , var_str
@@ -518,7 +518,7 @@ CODE
$waitTimeWriter .= fill_in_string(<<'CODE', PACKAGE => 'code');
! Open output group.
!$ call hdf5Access%set()
- metaDataGroup=galacticusOutputFile%openGroup('metaData','Galacticus meta data.' )
+ metaDataGroup=outputFile%openGroup('metaData','Galacticus meta data.' )
waitTimeGroup=metaDataGroup %openGroup('openMP' ,'Meta-data on OpenMP performance.')
! Write wait time data.
call waitTimeGroup%writeDataset(eventHookNames ,"eventHookNames" ,"Names of event hooks" )
diff --git a/perl/Galacticus/Build/SourceTree/Process/FunctionClass.pm b/perl/Galacticus/Build/SourceTree/Process/FunctionClass.pm
index 6c989b076c..3f5f9f5072 100644
--- a/perl/Galacticus/Build/SourceTree/Process/FunctionClass.pm
+++ b/perl/Galacticus/Build/SourceTree/Process/FunctionClass.pm
@@ -1181,6 +1181,9 @@ CODE
# Add a class guard for resets.
$deepCopy->{'resetCode' } .= "type is (".$nonAbstractClass->{'name'}.")\n";
$deepCopy->{'finalizeCode'} .= "type is (".$nonAbstractClass->{'name'}.")\n";
+ # Initialize a list of explicity-deep-copied variables that have been found.
+ my $foundDeepCopyNames;
+ @{$foundDeepCopyNames} = ();
while ( $class ) {
my $node = $class->{'tree'}->{'firstChild'};
$node = $node->{'sibling'}
@@ -1201,7 +1204,7 @@ CODE
my @ignore = exists($class->{'deepCopy'}->{'ignore'}) ? split(/\s*,\s*/,$class->{'deepCopy'}->{'ignore'}->{'variables'}) : ();
$node = $node->{'firstChild'};
while ( $node ) {
- &deepCopyDeclarations($class,$nonAbstractClass,$node,$node->{'declarations'},\@ignore,$lineNumber,$deepCopy)
+ &deepCopyDeclarations($class,$nonAbstractClass,$node,$node->{'declarations'},\@ignore,$lineNumber,$deepCopy,$foundDeepCopyNames)
if ( $node->{'type'} eq "declaration" );
$node = $node->{'sibling'};
}
@@ -1221,7 +1224,7 @@ CODE
unless ( defined($declarationSource) );
my $declaration = &Fortran::Utils::Unformat_Variables($declarationSource);
my @ignore = ();
- &deepCopyDeclarations($class,$nonAbstractClass,$node,$declaration,\@ignore,$lineNumber,$deepCopy);
+ &deepCopyDeclarations($class,$nonAbstractClass,$node,$declaration,\@ignore,$lineNumber,$deepCopy,$foundDeepCopyNames);
}
# Add any objects declared in the functionClassType class.
if ( defined($functionClassType) ) {
@@ -1229,7 +1232,7 @@ CODE
my @ignore = ();
my $node = $functionClassType->{'node'}->{'firstChild'};
while ( $node ) {
- &deepCopyDeclarations($class,$nonAbstractClass,$node,$node->{'declarations'},\@ignore,$lineNumber,$deepCopy)
+ &deepCopyDeclarations($class,$nonAbstractClass,$node,$node->{'declarations'},\@ignore,$lineNumber,$deepCopy,$foundDeepCopyNames)
if ( $node->{'type'} eq "declaration" );
$node = $node->{'sibling'};
}
@@ -1246,6 +1249,19 @@ CODE
$deepCopy->{'code'} .= "end select\n";
# Specify required modules.
$deepCopy->{'modules'}->{'Error'} = 1;
+ # Check that all explicit variables were found.
+ {
+ my $class = $nonAbstractClass;
+ while ( $class ) {
+ if ( exists($class->{'deepCopy'}->{'functionClass'}) ) {
+ foreach my $variable ( split(/\s*,\s*/,$class->{'deepCopy'}->{'functionClass'}->{'variables'}) ) {
+ die("Error: unable to find variable '".$variable."' marked for deep copy in class '".$class->{'name'}."'")
+ unless ( grep {$_ eq lc($variable)} @{$foundDeepCopyNames} );
+ }
+ }
+ $class = ($class->{'extends'} eq $directive->{'name'}) ? undef() : $classes{$class->{'extends'}};
+ }
+ }
}
$deepCopy->{'code' } .= "end select\n";
$deepCopy->{'resetCode' } .= "end select\n";
@@ -1364,6 +1380,8 @@ CODE
my $extensionOf;
# Generate code to output all variables from this class (and any parent class).
@{$stateStore->{'staticVariables'}} = ();
+ my $explicitNamesFound;
+ @{$explicitNamesFound} = ();
my $class = $nonAbstractClass;
while ( $class ) {
my $node = $class->{'tree'}->{'firstChild'};
@@ -1380,7 +1398,7 @@ CODE
# Search the node for declarations.
$node = $node->{'firstChild'};
while ( $node ) {
- &stateStoreVariables($stateStores,$stateStore,$class,$node->{'declarations'})
+ &stateStoreVariables($stateStores,$stateStore,$class,$node->{'declarations'},$explicitNamesFound)
if ( $node->{'type'} eq "declaration" );
$node = $node->{'type'} eq "contains" ? $node->{'firstChild'} : $node->{'sibling'};
}
@@ -1419,17 +1437,31 @@ CODE
my $declaration = &Fortran::Utils::Unformat_Variables($declarationSource);
die("Galacticus::Build::SourceTree::Process::FunctionClass::Process_FunctionClass(): unable to parse variable declaration")
unless ( defined($declaration) );
- &stateStoreVariables($stateStores,$stateStore,undef(),$declaration);
+ &stateStoreVariables($stateStores,$stateStore,undef(),$declaration,$explicitNamesFound);
}
# Add any variables declared in the functionClassType class.
if ( defined($functionClassType) ) {
my $node = $functionClassType->{'node'}->{'firstChild'};
while ( $node ) {
- &stateStoreVariables($stateStores,$stateStore,undef(),$node->{'declarations'})
+ &stateStoreVariables($stateStores,$stateStore,undef(),$node->{'declarations'},$explicitNamesFound)
if ( $node->{'type'} eq "declaration" );
$node = $node->{'type'} eq "contains" ? $node->{'firstChild'} : $node->{'sibling'};
}
}
+ # Check that all explicit variables were found.
+ {
+ my $class = $nonAbstractClass;
+ while ( $class ) {
+ if ( exists($class->{'stateStorable'}->{'functionClass'}) && exists($class->{'stateStorable'}->{'functionClass'}->{'variables'}) ) {
+ foreach my $variable ( split(/\s*,\s*/,$class->{'stateStorable'}->{'functionClass'}->{'variables'}) ) {
+ die("Error: unable to find variable '".$variable."' marked as state storable in class '".$class->{'name'}."'")
+ unless ( grep {$_ eq lc($variable)} @{$explicitNamesFound} );
+ }
+ }
+ # Move to the parent class.
+ $class = ($class->{'extends'} eq $directive->{'name'}) ? undef() : $classes{$class->{'extends'}};
+ }
+ }
# Add code to method.
$stateStores->{'stateFileUsed'} = 1
if ( scalar(@{$stateStore->{'staticVariables'}}) > 0 );
@@ -2447,7 +2479,8 @@ CODE
}
}
} else {
- $modulePostContains->{'content'} .= " use Error\n";
+ $modulePostContains->{'content'} .= " use Error , only : Error_Report\n";
+ $modulePostContains->{'content'} .= " use ISO_Varying_String, only : char\n";
}
$modulePostContains->{'content'} .= " implicit none\n";
$modulePostContains->{'content'} .= $argumentCode;
@@ -2458,7 +2491,7 @@ CODE
$code =~ s/\n/\n /g;
$modulePostContains->{'content'} .= $code."\n";
} else {
- $modulePostContains->{'content'} .= " call Error_Report('this is a null method - initialize the ".$directive->{'name'}." object before use'//".&Galacticus::Build::SourceTree::Process::SourceIntrospection::Location($node,$node->{'line'}).")\n";
+ $modulePostContains->{'content'} .= " call Error_Report('this is a null method - initialize the ".$directive->{'name'}." object before use and/or check that the \"'//char(self%objectType())//'\" class implements this method'//".&Galacticus::Build::SourceTree::Process::SourceIntrospection::Location($node,$node->{'line'}).")\n";
if ( $category eq "function" ) {
# Avoid warnings about unset function values.
$modulePostContains->{'content'} .= " ".$directive->{'name'}.ucfirst($methodName).$extension."=";
@@ -3083,13 +3116,14 @@ sub potentialDescriptorParameters {
sub deepCopyDeclarations {
# Process variable declarations from a node for deep copy.
- my $class = shift() ;
- my $nonAbstractClass = shift() ;
- my $node = shift() ;
- my $declarations = shift() ;
- my @ignore = @{shift()};
- my $lineNumber = shift() ;
- my $deepCopy = shift() ;
+ my $class = shift() ;
+ my $nonAbstractClass = shift() ;
+ my $node = shift() ;
+ my $declarations = shift() ;
+ my @ignore = @{shift()};
+ my $lineNumber = shift() ;
+ my $deepCopy = shift() ;
+ my $foundDeepCopyNames = shift() ;
our $stateStorables;
our $debugging;
our $deepCopyActions;
@@ -3188,6 +3222,7 @@ sub deepCopyDeclarations {
foreach my $object ( @{$declaration->{'variables'}} ) {
(my $name = $object) =~ s/^([a-zA-Z0-9_]+).*/$1/; # Strip away anything (e.g. assignment operators) after the variable name.
if ( grep {lc($_) eq lc($name)} split(/\s*,\s*/,$class->{'deepCopy'}->{'functionClass'}->{'variables'}) ) {
+ push(@{$foundDeepCopyNames},$name);
if ( grep {$_ eq "pointer"} @{$declaration->{'attributes'}} ) {
$deepCopy->{'assignments' } .= "nullify(destination%".$name.")\n";
$deepCopy->{'assignments' } .= "if (associated(self%".$name.")) then\n";
@@ -3333,16 +3368,16 @@ sub deepCopyDeclarations {
}
}
}
-
}
sub stateStoreVariables {
# Generate code to store/restore variables in functionClass objects.
- my $stateStores = shift();
- my $stateStore = shift();
- my $class = shift();
- my $declarations = shift();
- our $stateStorables ;
+ my $stateStores = shift();
+ my $stateStore = shift();
+ my $class = shift();
+ my $declarations = shift();
+ my $explicitNamesFound = shift();
+ our $stateStorables ;
foreach my $declaration ( &List::ExtraUtils::as_array($declarations) ) {
# Identify variable type.
if ( $declaration->{'intrinsic'} eq "procedure" || $declaration->{'intrinsic'} eq "final" ) {
@@ -3459,8 +3494,11 @@ sub stateStoreVariables {
(my $variableName = $_) =~ s/\s*=.*$//;
next
if ( grep {lc($_) eq lc($variableName)} @{$stateStore->{'excludes'}} );
+ my $isExplicit = grep {lc($_) eq lc($variableName)} @explicits;
next
- unless ( (! $isPointer) || grep {lc($_) eq lc($variableName)} @explicits );
+ unless ( (! $isPointer) || $isExplicit );
+ push(@{$explicitNamesFound},lc($variableName))
+ if ( $isExplicit );
my $rank = 0;
if ( grep {$_ =~ m/^dimension\s*\(/} @{$declaration->{'attributes'}} ) {
my $dimensionDeclarator = join(",",map {/^dimension\s*\(([a-zA-Z0-9_,:\s]+)\)/} @{$declaration->{'attributes'}});
diff --git a/perl/Galacticus/Build/SourceTree/Process/NonProcessed.pm b/perl/Galacticus/Build/SourceTree/Process/NonProcessed.pm
index 7ec9225542..0c582c7956 100644
--- a/perl/Galacticus/Build/SourceTree/Process/NonProcessed.pm
+++ b/perl/Galacticus/Build/SourceTree/Process/NonProcessed.pm
@@ -14,7 +14,7 @@ sub Process_NonProcessed {
# Get the tree.
my $tree = shift();
# Non-processed directives that we simply mark as processed to avoid warnings.
- my @nonProcessedDirectives = ( "methods", "workaround", "include", "functionGlobal", "component", "radiusSolverPlausibility", "interTreePositionInsert", "expiry", "scoping", "functionClassType" );
+ my @nonProcessedDirectives = ( "methods", "workaround", "include", "functionGlobal", "component", "radiusSolverPlausibility", "interTreePositionInsert", "expiry", "scoping" );
# Walk the tree, looking for our directive.
my $node = $tree;
my $depth = 0;
diff --git a/perl/Galacticus/Build/SourceTree/Process/ObjectBuilder.pm b/perl/Galacticus/Build/SourceTree/Process/ObjectBuilder.pm
index 6961892b77..32ea8843f8 100755
--- a/perl/Galacticus/Build/SourceTree/Process/ObjectBuilder.pm
+++ b/perl/Galacticus/Build/SourceTree/Process/ObjectBuilder.pm
@@ -439,7 +439,8 @@ sub Process_ObjectBuilder {
$destructorCode .= " else\n";
$destructorCode .= " ! Nullify the pointer.\n";
$destructorCode .= $debugMessage;
- $destructorCode .= " nullify(".$node->{'directive'}->{'name'}.")\n";
+ $destructorCode .= " nullify(".$node->{'directive'}->{'name'}.")\n"
+ unless ( exists($node->{'directive'}->{'nullify'}) && $node->{'directive'}->{'nullify'} eq "no" );
$destructorCode .= " end if\n";
$destructorCode .= "end if\n";
# Build a code node.
diff --git a/perl/Galacticus/Launch/Slurm.pm b/perl/Galacticus/Launch/Slurm.pm
index cfe4d1a707..4dce9dd0e2 100644
--- a/perl/Galacticus/Launch/Slurm.pm
+++ b/perl/Galacticus/Launch/Slurm.pm
@@ -81,7 +81,7 @@ sub Launch {
print $slurmFile "#!/bin/bash\n";
print $slurmFile "#SBATCH -J Galacticus_".$job->{'label'}."\n";
my $currentDirectory = cwd();
- print $slurmFile "#SBATCH --workdir=".$currentDirectory."\n";
+ print $slurmFile "#SBATCH --chdir=".$currentDirectory."\n";
if ( exists($launchScript->{'config'}->{'contact'}->{'email'}) ) {
if ( $launchScript->{'config'}->{'contact'}->{'email'} =~ m/\@/ && exists($launchScript->{'emailReport'}) && $launchScript->{'emailReport'} eq "yes" ) {
print $slurmFile "#SBATCH --mail-user=".$launchScript->{'config'}->{'contact'}->{'email'}."\n";
@@ -112,6 +112,11 @@ sub Launch {
print $slurmFile "export ".$environment."\n";
}
}
+ if ( exists($launchScript->{'slurm'}->{'module'}) ) {
+ foreach my $module ( @{$launchScript->{'slurm'}->{'module'}} ) {
+ print $slurmFile "module load ".$module."\n";
+ }
+ }
my $coreDump = "no";
$coreDump = $launchScript->{'slurm'}->{'coreDump'}
if ( exists($launchScript->{'slurm'}->{'coreDump'}) );
@@ -341,8 +346,10 @@ sub SubmitJobs {
print $scriptFile "if [ ! -z \${SLURM_SUBMIT_DIR+x} ]; then\n";
print $scriptFile " cd \$SLURM_SUBMIT_DIR\n";
print $scriptFile "fi\n";
- print $scriptFile "export ".$_."\n"
+ print $scriptFile "export " .$_."\n"
foreach ( &List::ExtraUtils::as_array($slurmConfig->{'environment'}) );
+ print $scriptFile "module load ".$_."\n"
+ foreach ( &List::ExtraUtils::as_array($slurmConfig->{'module' }) );
print $scriptFile "ulimit -t unlimited\n";
print $scriptFile "ulimit -c unlimited\n";
print $scriptFile "export OMP_NUM_THREADS=".$ompThreads."\n";
diff --git a/schema/componentSchema.xsd b/schema/componentSchema.xsd
index efdb815d1a..6fbcc2a19f 100644
--- a/schema/componentSchema.xsd
+++ b/schema/componentSchema.xsd
@@ -149,6 +149,7 @@
+
diff --git a/schema/objectDestructor.xsd b/schema/objectDestructor.xsd
new file mode 100644
index 0000000000..7e1be04b98
--- /dev/null
+++ b/schema/objectDestructor.xsd
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/schema/referenceAcquire.xsd b/schema/referenceAcquire.xsd
new file mode 100644
index 0000000000..683e1b27ae
--- /dev/null
+++ b/schema/referenceAcquire.xsd
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/schema/referenceConstruct.xsd b/schema/referenceConstruct.xsd
new file mode 100644
index 0000000000..89145f3fa6
--- /dev/null
+++ b/schema/referenceConstruct.xsd
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/analysis/analysesPlot.pl b/scripts/analysis/analysesPlot.pl
index a823b14b96..3e3c9e4170 100755
--- a/scripts/analysis/analysesPlot.pl
+++ b/scripts/analysis/analysesPlot.pl
@@ -184,8 +184,10 @@ sub function1DPlot {
if ( $attributes->{'yAxisIsLog'} ) {
my $negativeY = which($yLower <= 1.0e-10*$data->{'yDataset' });
my $negativeYTarget = which($yLowerTarget <= 1.0e-10*$data->{'yDatasetTarget'});
- $yLower ->($negativeY ) .= $data->{'yDataset' }->($negativeY );
- $yLowerTarget->($negativeYTarget) .= $data->{'yDatasetTarget'}->($negativeYTarget);
+ $yLower ->($negativeY ) .= $data->{'yDataset' }->($negativeY )
+ if ( nelem($negativeY ) > 0 );
+ $yLowerTarget->($negativeYTarget) .= $data->{'yDatasetTarget'}->($negativeYTarget)
+ if ( nelem($negativeYTarget) > 0 );
}
my $yUpperBoth = $yUpper->($nonZero)->append($yUpperTarget->($nonZeroTarget));
my $yLowerBoth = $yLower->($nonZero)->append($yLowerTarget->($nonZeroTarget));
diff --git a/scripts/analysis/analysesPlot.py b/scripts/analysis/analysesPlot.py
index 498add7207..0744396b67 100755
--- a/scripts/analysis/analysesPlot.py
+++ b/scripts/analysis/analysesPlot.py
@@ -248,6 +248,9 @@ def restricted_float(x):
yLower = y-errors
yUpper = y+errors
haveErrorBars = True
+ else:
+ yLower = y
+ yUpper = y
nonZero = np.nonzero(datasets['yDatasetTarget']['data'])
if haveErrorBars:
axes.errorbar(datasets['xDataset']['data'][nonZero],datasets['yDatasetTarget']['data'][nonZero],yerr=errors[nonZero],fmt='none',ecolor='#4c3af2',zorder=phase*10)
diff --git a/scripts/aux/migrations.xml b/scripts/aux/migrations.xml
index 34ee14a44e..447aee9185 100644
--- a/scripts/aux/migrations.xml
+++ b/scripts/aux/migrations.xml
@@ -208,10 +208,23 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/build/libraryInterfaces.pl b/scripts/build/libraryInterfaces.pl
index 6a2b78b9ef..5bc4272a2e 100755
--- a/scripts/build/libraryInterfaces.pl
+++ b/scripts/build/libraryInterfaces.pl
@@ -65,6 +65,40 @@
$functionClass->{'methods'} = $node->{'directive'}->{'method'};
}
$functionClass->{'moduleUses'} = \@moduleUses;
+ # Find the parent-child class dependencies.
+ my $extensions;
+ my $moduleUsesImplementations;
+ foreach my $fileNameImplementation ( &List::ExtraUtils::as_array($directiveLocations->{$functionClass->{'name'}}->{'file'}) ) {
+ my $treeImplementation = &Galacticus::Build::SourceTree::ParseFile($fileNameImplementation);
+ my $depthImplementation = 0;
+ my $nodeImplementation = $treeImplementation;
+ my $nameImplementation;
+ my @moduleUsesImplementation;
+ while ( $nodeImplementation ) {
+ if ( $nodeImplementation->{'type'} eq $functionClass->{'name'} ) {
+ # Find the name of the implementation.
+ $nameImplementation = $nodeImplementation->{'directive'}->{'name'};
+ } elsif ( $nodeImplementation->{'type'} eq "type" && $nodeImplementation->{'name'} eq $nameImplementation ) {
+ # Find the extended class.
+ if ( $nodeImplementation->{'opener'} =~ m/,\s*extends\s*\(\s*([a-zA-Z0-9_]+)\s*\)/ ) {
+ $extensions->{$nodeImplementation->{'name'}} = $1;
+ }
+ } elsif ( $nodeImplementation->{'type'} eq "moduleUse" ) {
+ # Module use statements, extract for later reference.
+ push(@moduleUsesImplementation,$nodeImplementation->{'moduleUse'});
+ }
+
+ $nodeImplementation = &Galacticus::Build::SourceTree::Walk_Tree($nodeImplementation,\$depthImplementation);
+ }
+ @{$moduleUsesImplementations->{$nameImplementation}} = @moduleUsesImplementation
+ unless (
+ exists($functionClass->{$nameImplementation} )
+ &&
+ exists($functionClass->{$nameImplementation}->{'exclude'} )
+ &&
+ $functionClass->{$nameImplementation}->{'exclude'} eq "yes"
+ );
+ }
# Find all implementations of this class.
my $classID = 0;
foreach my $fileNameImplementation ( &List::ExtraUtils::as_array($directiveLocations->{$functionClass->{'name'}}->{'file'}) ) {
@@ -139,18 +173,28 @@
@{$functionClass->{'implementations'}},
$implementation
)
- unless ( $abstractImplementation );
+ unless (
+ $abstractImplementation
+ ||
+ (
+ exists($functionClass->{$nameImplementation} )
+ &&
+ exists($functionClass->{$nameImplementation}->{'exclude'} )
+ &&
+ $functionClass->{$nameImplementation}->{'exclude'} eq "yes"
+ )
+ );
}
# Add Python parent class.
- &interfacesPythonClasses( $python,$functionClass );
+ &interfacesPythonClasses( $python,$functionClass );
# Add pointer get functions.
- &interfacesPointerGet ($code ,$functionClass );
+ &interfacesPointerGet ($code ,$functionClass );
# Add constructors.
- &interfacesConstructors ($code,$python,$functionClass,$libraryFunctionClasses);
+ &interfacesConstructors ($code,$python,$functionClass,$libraryFunctionClasses,$extensions,$moduleUsesImplementations);
# Add interfaces to all methods.
- &interfacesMethods ($code,$python,$functionClass );
+ &interfacesMethods ($code,$python,$functionClass ,$extensions,$moduleUsesImplementations);
# Add a destructor.
- &interfacesDestructor ($code,$python,$functionClass );
+ &interfacesDestructor ($code,$python,$functionClass );
}
}
@@ -272,17 +316,19 @@ sub interfacesPointerGet {
sub interfacesConstructors {
# Build interfaces to constructors.
- my $code = shift();
- my $python = shift();
- $ext::functionClass = shift();
- my $libraryFunctionClasses = shift();
+ my $code = shift();
+ my $python = shift();
+ $ext::functionClass = shift();
+ my $libraryFunctionClasses = shift();
+ my $extensions = shift();
+ my $moduleUsesImplementations = shift();
foreach $ext::implementation ( @{$ext::functionClass->{'implementations'}} ) {
# Extract the list of arguments and process to determine how interfaces should be built.
my @argumentList = @{$ext::implementation->{'arguments'}};
- @argumentList = &assignCTypes (\@argumentList );
- @argumentList = &assignCAttributes (\@argumentList );
- @argumentList = &buildPythonReassignments (\@argumentList );
- @argumentList = &buildFortranReassignments(\@argumentList,$ext::functionClass,$ext::implementation);
+ @argumentList = &assignCTypes (\@argumentList );
+ @argumentList = &assignCAttributes (\@argumentList );
+ @argumentList = &buildPythonReassignments (\@argumentList );
+ @argumentList = &buildFortranReassignments(\@argumentList,$ext::functionClass,$ext::implementation,$extensions,$moduleUsesImplementations);
# Construct pre- and post-arguments content for the call from Fortran to Galacticus.
my $preArguments .= fill_in_string(<<'CODE', PACKAGE => 'ext');
!![
@@ -408,6 +454,8 @@ sub interfacesMethods {
my $code = shift();
my $python = shift();
$ext::functionClass = shift();
+ my $extensions = shift();
+ my $moduleUsesImplementations = shift();
foreach $ext::method ( &List::ExtraUtils::hashList($ext::functionClass->{'methods'},keyAs => 'name') ) {
# Construct function declaration.
my %isoCBindingSymbols;
@@ -429,7 +477,9 @@ sub interfacesMethods {
} elsif ( $ext::method->{'type'} eq "void" ) {
$ext::procedure = "subroutine";
} else {
- die("unsupported type '".$ext::method->{'type'}."'");
+ print "unsupported type '".$ext::method->{'type'}."'\n";
+ delete($ext::functionClass->{'methods'}->{$ext::method});
+ next;
}
$ext::functionDeclaration = $ext::method->{'type'} eq "void" ? "" : $functionCType." :: ".$ext::functionClass->{'name'}.ucfirst($ext::method->{'name'})."L\n";
# Create a list of arguments.
@@ -464,10 +514,10 @@ sub interfacesMethods {
}
}
# Process argument list.
- @argumentList = &assignCTypes (\@argumentList );
- @argumentList = &assignCAttributes (\@argumentList );
- @argumentList = &buildPythonReassignments (\@argumentList );
- @argumentList = &buildFortranReassignments(\@argumentList,$ext::functionClass,undef());
+ @argumentList = &assignCTypes (\@argumentList );
+ @argumentList = &assignCAttributes (\@argumentList );
+ @argumentList = &buildPythonReassignments (\@argumentList );
+ @argumentList = &buildFortranReassignments(\@argumentList,$ext::functionClass,undef(),$extensions,$moduleUsesImplementations);
# Generate the pre- and post-arguments content of the function call.
my $preArguments = ($ext::method->{'type'} eq "void" ? "call " : $ext::functionClass->{'name'}.ucfirst($ext::method->{'name'})."L=").$ext::resultConversionOpen."self_%".$ext::method->{'name'}."( &\n";
my $postArguments = "& )".$ext::resultConversionClose."\n";
@@ -749,9 +799,11 @@ sub buildPythonReassignments {
sub buildFortranReassignments {
# Geneate reassignments of Fortran arguments to allow passing between languages.
- my @argumentList = @{shift()};
- my $functionClass = shift() ;
- my $implementation = shift() ;
+ my @argumentList = @{shift()};
+ my $functionClass = shift() ;
+ my $implementation = shift() ;
+ my $extensions = shift() ;
+ my $moduleUsesImplementations = shift() ;
my @argumentListNew;
while ( @argumentList ) {
# Get the next argument from the list.
@@ -780,12 +832,17 @@ sub buildFortranReassignments {
# Search for any module import of this enumeration type.
my $importModule;
if ( defined($implementation) ) {
- foreach my $useBlock ( @{$implementation->{'moduleUses'}} ) {
- foreach my $module ( keys(%{$useBlock}) ) {
- if ( grep {$_ eq $argument->{'type'}} keys(%{$useBlock->{$module}->{'only'}}) ) {
- $importModule = $module;
+
+ my $className = $implementation->{'name'};
+ while ( defined($className) && ! defined($importModule) ) {
+ foreach my $useBlock ( @{$moduleUsesImplementations->{$className}} ) {
+ foreach my $module ( keys(%{$useBlock}) ) {
+ if ( grep {$_ eq $argument->{'type'}} keys(%{$useBlock->{$module}->{'only'}}) ) {
+ $importModule = $module;
+ }
}
}
+ $className = exists($extensions->{$className}) ? $extensions->{$className} : undef();
}
}
unless ( defined($importModule) ) {
diff --git a/scripts/doc/Code_Analyzer.pl b/scripts/doc/Code_Analyzer.pl
index 999ec91a0d..a2aea4a378 100755
--- a/scripts/doc/Code_Analyzer.pl
+++ b/scripts/doc/Code_Analyzer.pl
@@ -70,10 +70,10 @@
module => { unitName => 1, regEx => "^\\s*module\\s+(?!procedure\\s)([a-z0-9_]+)\$" },
# Find program openings.
program => { unitName => 1, regEx => "^\\s*program\\s+([a-z0-9_]+)" },
- # Find subroutine openings, allowing for pure, elemental and recursive subroutines.
- subroutine => { unitName => 2, regEx => "^\\s*(pure\\s+|elemental\\s+|recursive\\s+|module\\s+)*\\s*subroutine\\s+([a-z0-9_]+)"},
- # Find function openings, allowing for pure, elemental and recursive functions, and different function types.
- function => { unitName => 6, regEx => "^\\s*(pure\\s+|elemental\\s+|recursive\\s+)*\\s*(real\\s*|integer\\s*|double\\s+precision\\s*|double\\s+complex\\s*|character\\s*|logical\\s*|module\\s*)*\\s*(\\(((kind|len)=)??[\\w\\d]*\\))*\\s*function\\s+([a-z0-9_]+)"},
+ # Find subroutine openings, allowing for impure, pure, elemental and recursive subroutines.
+ subroutine => { unitName => 2, regEx => "^\\s*(impure\\s+|pure\\s+|elemental\\s+|recursive\\s+|module\\s+)*\\s*subroutine\\s+([a-z0-9_]+)"},
+ # Find function openings, allowing for impure, pure, elemental and recursive functions, and different function types.
+ function => { unitName => 6, regEx => "^\\s*(impure\\s+|pure\\s+|elemental\\s+|recursive\\s+)*\\s*(real\\s*|integer\\s*|double\\s+precision\\s*|double\\s+complex\\s*|character\\s*|logical\\s*|module\\s*)*\\s*(\\(((kind|len)=)??[\\w\\d]*\\))*\\s*function\\s+([a-z0-9_]+)"},
# Find interfaces.
interface => { unitName => 2, regEx => "^\\s*(abstract\\s+)??interface\\s+([a-z0-9_\\(\\)\\/\\+\\-\\*\\.=]*)"},
# Find types.
diff --git a/source/Galacticus.F90 b/source/Galacticus.F90
index 5415df332b..4a58cae968 100644
--- a/source/Galacticus.F90
+++ b/source/Galacticus.F90
@@ -65,6 +65,8 @@ program Galacticus
#endif
! Register error handlers.
call Error_Handler_Register()
+ ! Show the Galacticus banner.
+ call Display_Banner_Show()
! Check that we have at least one command line argument.
if (Command_Argument_Count() < 1) call usageError()
! Get the name of the parameter file from the first command line argument.
@@ -97,8 +99,6 @@ program Galacticus
call Error_Wait_Set_From_Parameters (parameters)
! Set resource limits.
call System_Limits_Set (parameters)
- ! Show the Galacticus banner.
- call Display_Banner_Show()
! Validate parameter file.
call parameters%checkParameters()
! Perform task.
diff --git a/source/accretion.Bondi_Hoyle_Lyttleton.F90 b/source/accretion.Bondi_Hoyle_Lyttleton.F90
index 083645cad6..c550564b6e 100644
--- a/source/accretion.Bondi_Hoyle_Lyttleton.F90
+++ b/source/accretion.Bondi_Hoyle_Lyttleton.F90
@@ -58,7 +58,7 @@ double precision function Bondi_Hoyle_Lyttleton_Accretion_Rate(mass,density,velo
return
end function Bondi_Hoyle_Lyttleton_Accretion_Rate
- double precision function Bondi_Hoyle_Lyttleton_Accretion_Radius(mass,temperature)
+ double precision function Bondi_Hoyle_Lyttleton_Accretion_Radius(mass,temperature) result(radiusAccretion)
!!{
Computes the Bondi-Hoyle-Lyttleton accretion radius (in Mpc; \citealt{edgar_review_2004}).
!!}
@@ -68,11 +68,12 @@ double precision function Bondi_Hoyle_Lyttleton_Accretion_Radius(mass,temperatur
double precision, intent(in ) :: mass , temperature
double precision :: soundSpeed
- ! Compute the sound speed.
- soundSpeed=Ideal_Gas_Sound_Speed(temperature)
-
- ! Compute the accretion radius
- Bondi_Hoyle_Lyttleton_Accretion_Radius=gravitationalConstantGalacticus*mass/soundSpeed**2
+ if (temperature > 0.0d0) then
+ soundSpeed =Ideal_Gas_Sound_Speed(temperature)
+ radiusAccretion=gravitationalConstantGalacticus*mass/soundSpeed**2
+ else
+ radiusAccretion=huge(0.0d0)
+ end if
return
end function Bondi_Hoyle_Lyttleton_Accretion_Radius
diff --git a/source/accretion.halo.Bertschinger.F90 b/source/accretion.halo.Bertschinger.F90
index e3bdf1b4e9..db455a708f 100644
--- a/source/accretion.halo.Bertschinger.F90
+++ b/source/accretion.halo.Bertschinger.F90
@@ -104,14 +104,20 @@ subroutine bertschingerDestructor(self)
return
end subroutine bertschingerDestructor
- double precision function bertschingerVelocityScale(self,node)
+ double precision function bertschingerVelocityScale(self,node) result(velocityScale)
!!{
Returns the velocity scale to use for {\normalfont \ttfamily node}. Use the maximum circular velocity.
!!}
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class(accretionHaloBertschinger), intent(inout) :: self
type (treeNode ), intent(inout) :: node
-
- bertschingerVelocityScale=self%darkMatterProfileDMO_%circularVelocityMaximum(node)
+ class(massDistributionClass ), pointer :: massDistribution_
+
+ massDistribution_ => self %darkMatterProfileDMO_ %get(node)
+ velocityScale = massDistribution_%velocityRotationCurveMaximum ( )
+ !![
+
+ !!]
return
end function bertschingerVelocityScale
diff --git a/source/accretion.halo.Naoz_Barkana_2007.F90 b/source/accretion.halo.Naoz_Barkana_2007.F90
index 0fa66738f2..3ec3540a32 100644
--- a/source/accretion.halo.Naoz_Barkana_2007.F90
+++ b/source/accretion.halo.Naoz_Barkana_2007.F90
@@ -25,7 +25,6 @@
!!}
use :: Intergalactic_Medium_Filtering_Masses, only : intergalacticMediumFilteringMass, intergalacticMediumFilteringMassClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Virial_Density_Contrast , only : virialDensityContrastClass
!![
@@ -74,7 +73,6 @@
& rateCorrectionComputed
integer (kind=kind_int8 ) :: lastUniqueID
class (intergalacticMediumFilteringMassClass), pointer :: intergalacticMediumFilteringMass_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
contains
!![
@@ -144,7 +142,6 @@ function naozBarkana2007ConstructorParameters(parameters) result(self)
self%massMinimum
-
!!]
@@ -152,7 +149,7 @@ function naozBarkana2007ConstructorParameters(parameters) result(self)
return
end function naozBarkana2007ConstructorParameters
- function naozBarkana2007ConstructorInternal(timeReionization,velocitySuppressionReionization,accretionNegativeAllowed,accretionNewGrowthOnly,rateAdjust,massMinimum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,accretionHaloTotal_,chemicalState_,intergalacticMediumState_,intergalacticMediumFilteringMass_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function naozBarkana2007ConstructorInternal(timeReionization,velocitySuppressionReionization,accretionNegativeAllowed,accretionNewGrowthOnly,rateAdjust,massMinimum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,accretionHaloTotal_,chemicalState_,intergalacticMediumState_,intergalacticMediumFilteringMass_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily naozBarkana2007} halo accretion class.
!!}
@@ -170,10 +167,9 @@ function naozBarkana2007ConstructorInternal(timeReionization,velocitySuppression
class (chemicalStateClass ), intent(in ), target :: chemicalState_
class (intergalacticMediumStateClass ), intent(in ), target :: intergalacticMediumState_
class (intergalacticMediumFilteringMassClass), intent(in ), target :: intergalacticMediumFilteringMass_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
!![
-
+
!!]
self%accretionHaloSimple=accretionHaloSimple(timeReionization,velocitySuppressionReionization,accretionNegativeAllowed,accretionNewGrowthOnly,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,accretionHaloTotal_,chemicalState_,intergalacticMediumState_)
@@ -218,7 +214,6 @@ subroutine naozBarkana2007Destructor(self)
if (calculationResetEvent%isAttached(self,naozBarkana2007CalculationReset)) call calculationResetEvent%detach(self,naozBarkana2007CalculationReset)
!![
-
!!]
return
@@ -298,7 +293,6 @@ double precision function naozBarkana2007FilteredFraction(self,node)
& densityContrastVirial , &
& cosmologyParameters_ =self %cosmologyParameters_ , &
& cosmologyFunctions_ =self %cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self %darkMatterProfileDMO_ , &
& virialDensityContrast_=self %virialDensityContrast_ &
& )
self%filteredFractionStored = self%filteredFractionCompute(massHalo,massFiltering)
@@ -336,7 +330,6 @@ double precision function naozBarkana2007FilteredFractionRate(self,node)
& densityContrastVirial , &
& cosmologyParameters_ =self %cosmologyParameters_ , &
& cosmologyFunctions_ =self %cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self %darkMatterProfileDMO_ , &
& virialDensityContrast_=self %virialDensityContrast_ &
& )
if (.not.self%filteredFractionComputed) then
diff --git a/source/benchmarks.stellar_luminosities.F90 b/source/benchmarks.stellar_luminosities.F90
index 7cec022b9f..459d7e35e8 100644
--- a/source/benchmarks.stellar_luminosities.F90
+++ b/source/benchmarks.stellar_luminosities.F90
@@ -25,11 +25,12 @@ program Benchmark_Stellar_Populations_Luminosities
!!{
Benchmarking of stellar population luminosity calculations.
!!}
- use, intrinsic :: ISO_Fortran_Env, only : output_unit
+ use, intrinsic :: ISO_Fortran_Env , only : output_unit
use :: Abundances_Structure , only : abundances , metallicityTypeLinearByMassSolar
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
use :: Cosmology_Parameters , only : cosmologyParametersSimple
use :: Display , only : displayVerbositySet , verbosityLevelWorking
+ use :: Events_Hooks , only : eventsHooksInitialize
use :: Input_Paths , only : inputPath , pathTypeDataDynamic , pathTypeDataStatic
use :: ISO_Varying_String , only : char , operator(//) , var_str
use :: Input_Parameters , only : inputParameters
@@ -79,6 +80,7 @@ program Benchmark_Stellar_Populations_Luminosities
parameters=inputParameters()
call displayVerbositySet(verbosityLevelWorking)
+ call eventsHooksInitialize()
! Construct cosmology and stellar populations.
cosmologyParameters_ =cosmologyParametersSimple ( &
& OmegaMatter = 0.3d0 , &
@@ -186,8 +188,8 @@ program Benchmark_Stellar_Populations_Luminosities
& Filter_Get_Index(var_str("VIRCAM_NB980" )),Filter_Get_Index(var_str("VIRCAM_NB990" )),Filter_Get_Index(var_str("VIRCAM_Y" )),Filter_Get_Index(var_str("VIRCAM_Z" )), &
& Filter_Get_Index(var_str("WFC3IR_f105w" )),Filter_Get_Index(var_str("WFC3IR_f125w" )),Filter_Get_Index(var_str("WFC3IR_f160w" )),Filter_Get_Index(var_str("WFCAM_H" )), &
& Filter_Get_Index(var_str("WFCAM_J" )),Filter_Get_Index(var_str("WFCAM_K" )),Filter_Get_Index(var_str("WFCAM_Y" )),Filter_Get_Index(var_str("WFCAM_Z" )), &
- & Filter_Get_Index(var_str("WFIRST_BAO-Grism" )),Filter_Get_Index(var_str("WFIRST_F184" )),Filter_Get_Index(var_str("WFIRST_H158" )),Filter_Get_Index(var_str("WFIRST_J129" )), &
- & Filter_Get_Index(var_str("WFIRST_SNPrism" )),Filter_Get_Index(var_str("WFIRST_W149" )),Filter_Get_Index(var_str("WFIRST_Y106" )),Filter_Get_Index(var_str("WFIRST_Z087" )), &
+ & Filter_Get_Index(var_str("Roman_F062" )),Filter_Get_Index(var_str("Roman_F087" )),Filter_Get_Index(var_str("Roman_F106" )),Filter_Get_Index(var_str("Roman_F129" )), &
+ & Filter_Get_Index(var_str("Roman_F146" )),Filter_Get_Index(var_str("Roman_F158" )),Filter_Get_Index(var_str("Roman_F184" )),Filter_Get_Index(var_str("Roman_F213" )), &
& Filter_Get_Index(var_str("WIRCAM_K" )),Filter_Get_Index(var_str("bJ" )),Filter_Get_Index(var_str("xRayFull" )),Filter_Get_Index(var_str("xRayHard" )), &
& Filter_Get_Index(var_str("xRaySoft" )) &
& ]
diff --git a/source/black_holes.accretion_rates.standard.F90 b/source/black_holes.accretion_rates.standard.F90
index 214d2f54d5..82f0aaf9a0 100644
--- a/source/black_holes.accretion_rates.standard.F90
+++ b/source/black_holes.accretion_rates.standard.F90
@@ -22,7 +22,6 @@
!!}
use :: Black_Hole_Binary_Separations, only : blackHoleBinarySeparationGrowthRateClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Accretion_Disks , only : accretionDisksClass
use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
use :: Cooling_Radii , only : coolingRadiusClass
@@ -40,7 +39,6 @@
The standard black hole accretion rate calculation.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (blackHoleBinarySeparationGrowthRateClass), pointer :: blackHoleBinarySeparationGrowthRate_ => null()
class (accretionDisksClass ), pointer :: accretionDisks_ => null()
class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
@@ -83,7 +81,6 @@ function standardConstructorParameters(parameters) result(self)
implicit none
type (blackHoleAccretionRateStandard ) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
class (blackHoleBinarySeparationGrowthRateClass), pointer :: blackHoleBinarySeparationGrowthRate_
class (accretionDisksClass ), pointer :: accretionDisks_
class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
@@ -119,17 +116,15 @@ function standardConstructorParameters(parameters) result(self)
-
!!]
- self=blackHoleAccretionRateStandard(bondiHoyleAccretionEnhancementHotHalo,bondiHoyleAccretionEnhancementSpheroid,bondiHoyleAccretionTemperatureSpheroid,bondiHoyleAccretionHotModeOnly,galacticStructure_,blackHoleBinarySeparationGrowthRate_,hotHaloTemperatureProfile_,accretionDisks_,coolingRadius_,darkMatterHaloScale_)
+ self=blackHoleAccretionRateStandard(bondiHoyleAccretionEnhancementHotHalo,bondiHoyleAccretionEnhancementSpheroid,bondiHoyleAccretionTemperatureSpheroid,bondiHoyleAccretionHotModeOnly,blackHoleBinarySeparationGrowthRate_,hotHaloTemperatureProfile_,accretionDisks_,coolingRadius_,darkMatterHaloScale_)
!![
-
@@ -138,14 +133,13 @@ function standardConstructorParameters(parameters) result(self)
return
end function standardConstructorParameters
- function standardConstructorInternal(bondiHoyleAccretionEnhancementHotHalo,bondiHoyleAccretionEnhancementSpheroid,bondiHoyleAccretionTemperatureSpheroid,bondiHoyleAccretionHotModeOnly,galacticStructure_,blackHoleBinarySeparationGrowthRate_,hotHaloTemperatureProfile_,accretionDisks_,coolingRadius_,darkMatterHaloScale_) result(self)
+ function standardConstructorInternal(bondiHoyleAccretionEnhancementHotHalo,bondiHoyleAccretionEnhancementSpheroid,bondiHoyleAccretionTemperatureSpheroid,bondiHoyleAccretionHotModeOnly,blackHoleBinarySeparationGrowthRate_,hotHaloTemperatureProfile_,accretionDisks_,coolingRadius_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily standard} node operator class.
!!}
use :: Galacticus_Nodes, only : defaultHotHaloComponent
implicit none
type (blackHoleAccretionRateStandard ) :: self
- class (galacticStructureClass ), target, intent(in ) :: galacticStructure_
class (blackHoleBinarySeparationGrowthRateClass), target, intent(in ) :: blackHoleBinarySeparationGrowthRate_
class (accretionDisksClass ), target, intent(in ) :: accretionDisks_
class (hotHaloTemperatureProfileClass ), target, intent(in ) :: hotHaloTemperatureProfile_
@@ -155,7 +149,7 @@ function standardConstructorInternal(bondiHoyleAccretionEnhancementHotHalo,bondi
& bondiHoyleAccretionTemperatureSpheroid
logical , intent(in ) :: bondiHoyleAccretionHotModeOnly
!![
-
+
!!]
! Check if cold mode is explicitly tracked.
@@ -172,7 +166,6 @@ subroutine standardDestructor(self)
!![
-
@@ -193,16 +186,21 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
use :: Ideal_Gases_Thermodynamics , only : Ideal_Gas_Jeans_Length , Ideal_Gas_Sound_Speed
use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr , gigaYear , megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
class (blackHoleAccretionRateStandard), intent(inout) :: self
class (nodeComponentBlackHole ), intent(inout) :: blackHole
- double precision , intent( out) :: rateMassAccretionSpheroid ,rateMassAccretionHotHalo
+ double precision , intent( out) :: rateMassAccretionSpheroid , rateMassAccretionHotHalo
type (treeNode ), pointer :: node
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistributionSpheroid_ , massDistributionHotHalo_, &
+ & massDistributionColdHalo_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
! Lowest gas density to consider when computing accretion rates onto black hole (in units of M☉/Mpc³).
double precision , parameter :: densityGasMinimum =1.0d0
- double precision , dimension(3) :: position
double precision :: radiusAccretion , rateAccretionMaximum , &
& massBlackHole , densityGas , &
& temperatureHotHalo , fractionHotMode , &
@@ -227,9 +225,10 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
& ,blackHole%radialPosition() &
& )
! Set the position.
- position=[radiusAccretion,0.0d0,0.0d0]
+ coordinates = [radiusAccretion,0.0d0,0.0d0]
! Get density of gas at the galactic center.
- densityGas=self%galacticStructure_%density(node,position,coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeSpheroid,massType =massTypeGaseous)
+ massDistributionSpheroid_ => node %massDistribution(componentTypeSpheroid,massTypeGaseous)
+ densityGas = massDistributionSpheroid_%density (coordinates )
! Check if we have a non-negligible gas density.
if (densityGas > densityGasMinimum) then
! Get the spheroid component.
@@ -242,9 +241,9 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
! radius, as the gas should be smoothly distributed on scales below the Jeans length.
if (lengthJeans > radiusAccretion) then
! Set the position.
- position=[lengthJeans,0.0d0,0.0d0]
+ coordinates=[lengthJeans,0.0d0,0.0d0]
! Get density of gas at the galactic center.
- densityGas=self%galacticStructure_%density(node,position,coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeSpheroid,massType=massTypeGaseous)
+ densityGas =massDistributionSpheroid_%density(coordinates)
end if
! Compute the accretion rate.
rateMassAccretionSpheroid=max( &
@@ -264,12 +263,15 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
! Get the hot halo component.
hotHalo => node%hotHalo()
! Get halo gas temperature.
- temperatureHotHalo=self%hotHaloTemperatureProfile_%temperature(node,radius=0.0d0)
+ massDistributionHotHalo_ => node %massDistribution (componentType=componentTypeHotHalo,massType=massTypeGaseous)
+ kinematicsDistribution_ => massDistributionHotHalo_%kinematicsDistribution( )
+ coordinates = [0.0d0,0.0d0,0.0d0]
+ temperatureHotHalo = kinematicsDistribution_%temperature(coordinates)
! Get the accretion radius.
radiusAccretion=Bondi_Hoyle_Lyttleton_Accretion_Radius(massBlackHole,temperatureHotHalo)
radiusAccretion=min(radiusAccretion,hotHalo%outerRadius())
! Set the position.
- position=[radiusAccretion,0.0d0,0.0d0]
+ coordinates=[radiusAccretion,0.0d0,0.0d0]
! Find the fraction of gas in the halo which is in the hot mode. Set this to unity if hot/cold mode is not to be
! considered.
select case (self%bondiHoyleAccretionHotModeOnly)
@@ -289,24 +291,17 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
end if
end select
! Get density of gas at the galactic center - scaled by the fraction in the hot accretion mode.
- densityGas =+fractionHotMode &
- & *self%galacticStructure_%density( &
- & node , &
- & position , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & componentType =componentTypeHotHalo , &
- & massType =massTypeGaseous &
- & )
- if (self%coldModeTracked.and.fractionColdMode > 0.0d0) &
- & densityGas=+densityGas &
- & +fractionColdMode &
- & *self%galacticStructure_%density( &
- & node , &
- & position , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & componentType =componentTypeColdHalo , &
- & massType =massTypeGaseous &
- & )
+ densityGas = +fractionHotMode &
+ & *massDistributionHotHalo_%density (coordinates )
+ if (self%coldModeTracked.and.fractionColdMode > 0.0d0) then
+ massDistributionColdHalo_ => node %massDistribution(componentTypeColdHalo,massTypeGaseous)
+ densityGas = +densityGas &
+ & +fractionColdMode &
+ & *massDistributionColdHalo_%density (coordinates )
+ !![
+
+ !!]
+ end if
! Check if we have a non-zero gas density.
if (densityGas > densityGasMinimum) then
! Compute the accretion rate.
@@ -339,6 +334,11 @@ subroutine standardRateAccretion(self,blackHole,rateMassAccretionSpheroid,rateMa
! No gas density, so zero accretion rate.
rateMassAccretionHotHalo=0.0d0
end if
+ !![
+
+
+
+ !!]
else
rateMassAccretionSpheroid=0.0d0
rateMassAccretionHotHalo =0.0d0
diff --git a/source/black_holes.binaries.initial_separation.tidal_radius.F90 b/source/black_holes.binaries.initial_separation.tidal_radius.F90
index 077aa95355..1d3629b541 100644
--- a/source/black_holes.binaries.initial_separation.tidal_radius.F90
+++ b/source/black_holes.binaries.initial_separation.tidal_radius.F90
@@ -23,7 +23,7 @@
Implements a class for black hole binary initial separation based on tidal disruption of the satellite galaxy.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Mass_Distributions, only : massDistributionClass
use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
!![
@@ -46,10 +46,8 @@
A black hole binary initial separation class in which the radius is based on tidal disruption of the satellite galaxy.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
- type (rootFinder ) :: finder
+ type (rootFinder) :: finder
contains
- final :: tidalRadiusDestructor
procedure :: separationInitial => tidalRadiusSeparationInitial
end type blackHoleBinaryInitialSeparationTidalRadius
@@ -62,10 +60,9 @@
end interface blackHoleBinaryInitialSeparationTidalRadius
! Module-scope variables used in root finding.
- double precision :: massHalf, radiusMassHalf
- type (treeNode ), pointer :: node_
- class (blackHoleBinaryInitialSeparationTidalRadius), pointer :: self_
- !$omp threadprivate(radiusMassHalf,massHalf,node_,self_)
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: massHalf , radiusMassHalf
+ !$omp threadprivate(radiusMassHalf,massHalf,massDistribution_)
contains
@@ -78,29 +75,20 @@ function tidalRadiusConstructorParameters(parameters) result(self)
implicit none
type (blackHoleBinaryInitialSeparationTidalRadius) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=blackHoleBinaryInitialSeparationTidalRadius(galacticStructure_)
+ self=blackHoleBinaryInitialSeparationTidalRadius()
!![
-
!!]
return
end function tidalRadiusConstructorParameters
- function tidalRadiusConstructorInternal(galacticStructure_) result(self)
+ function tidalRadiusConstructorInternal() result(self)
!!{
Internal constructor for the {\normalfont \ttfamily tidalRadius} black hole binary recoil class.
!!}
implicit none
- type (blackHoleBinaryInitialSeparationTidalRadius) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
+ type (blackHoleBinaryInitialSeparationTidalRadius) :: self
self%finder=rootFinder( &
& rootFunction=tidalRadiusRoot , &
@@ -115,25 +103,12 @@ function tidalRadiusConstructorInternal(galacticStructure_) result(self)
return
end function tidalRadiusConstructorInternal
- subroutine tidalRadiusDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily tidalRadius} black hole binary recoil class.
- !!}
- implicit none
- type(blackHoleBinaryInitialSeparationTidalRadius), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine tidalRadiusDestructor
-
double precision function tidalRadiusSeparationInitial(self,node,nodeHost)
!!{
Returns an initial separation for a binary black holes through tidal disruption.
!!}
use :: Galactic_Structure_Options, only : massTypeGalactic
- use :: Galacticus_Nodes , only : nodeComponentBlackHole, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBlackHole
implicit none
class(blackHoleBinaryInitialSeparationTidalRadius), intent(inout), target :: self
type (treeNode ), intent(inout), target :: nodeHost , node
@@ -146,22 +121,23 @@ double precision function tidalRadiusSeparationInitial(self,node,nodeHost)
blackHole => node%blackHole(instance=1)
! If the primary black hole has zero mass (i.e. has been ejected), then return immediately.
if (blackHole%mass() <= 0.0d0) return
+ ! Get the mass distribution.
+ massDistribution_ => node%massDistribution(massType=massTypeGalactic)
! Get the half-mass radius of the satellite galaxy.
- radiusMassHalf=self%galacticStructure_%radiusEnclosingMass(node,massFractional=0.5d0 ,massType=massTypeGalactic)
+ radiusMassHalf=massDistribution_%radiusEnclosingMass (massFractional=0.5d0 )
! Get the mass within the half-mass radius.
- massHalf =self%galacticStructure_%massEnclosed (node, radiusMassHalf,massType=massTypeGalactic)
- ! Return zero radius for massless galaxy.
- if (radiusMassHalf <= 0.0d0 .or. massHalf <= 0.0d0) return
+ massHalf =massDistribution_%massEnclosedBySphere( radiusMassHalf)
+ !![
+
+ !!]
! Solve for the radius around the host at which the satellite gets disrupted.
- self_ => self
- node_ => nodeHost
- tidalRadiusSeparationInitial = self%finder%find( &
- & rootGuess=self%galacticStructure_%radiusEnclosingMass( &
- & nodeHost , &
- & massFractional=0.5d0 , &
- & massType=massTypeGalactic &
- & ) &
- & )
+ if (radiusMassHalf > 0.0d0 .and. massHalf > 0.0d0) then
+ massDistribution_ => nodeHost %massDistribution(massType =massTypeGalactic )
+ tidalRadiusSeparationInitial = self %finder%find (rootGuess=massDistribution_%radiusEnclosingMass(massFractional=0.5d0))
+ !![
+
+ !!]
+ end if
return
end function tidalRadiusSeparationInitial
@@ -174,11 +150,11 @@ Root function used in solving for the radius of tidal disruption of a satellite
double precision, intent(in ) :: radius
! Evaluate the root function.
- tidalRadiusRoot=+self_%galacticStructure_%massEnclosed(node_,radius,massType=massTypeGalactic) &
- & /massHalf &
- & -( &
- & +radius &
- & /radiusMassHalf &
+ tidalRadiusRoot=+massDistribution_%massEnclosedBySphere(radius) &
+ & /massHalf &
+ & -( &
+ & +radius &
+ & /radiusMassHalf &
& )**3
return
end function tidalRadiusRoot
diff --git a/source/black_holes.binaries.separation_growth_rate.standard.F90 b/source/black_holes.binaries.separation_growth_rate.standard.F90
index 1de1d2883f..53c21b2de7 100644
--- a/source/black_holes.binaries.separation_growth_rate.standard.F90
+++ b/source/black_holes.binaries.separation_growth_rate.standard.F90
@@ -25,7 +25,6 @@
!!}
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -82,7 +81,6 @@
private
logical :: stellarDensityChangeBinaryMotion , computeVelocityDispersion
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: standardDestructor
procedure :: growthRate => standardGrowthRate
@@ -108,7 +106,6 @@ function standardConstructorParameters(parameters) result(self)
type (blackHoleBinarySeparationGrowthRateStandard) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: stellarDensityChangeBinaryMotion, computeVelocityDispersion
!![
@@ -127,28 +124,25 @@ function standardConstructorParameters(parameters) result(self)
-
!!]
- self=blackHoleBinarySeparationGrowthRateStandard(stellarDensityChangeBinaryMotion,computeVelocityDispersion,darkMatterHaloScale_,galacticStructure_)
+ self=blackHoleBinarySeparationGrowthRateStandard(stellarDensityChangeBinaryMotion,computeVelocityDispersion,darkMatterHaloScale_)
!![
-
!!]
return
end function standardConstructorParameters
- function standardConstructorInternal(stellarDensityChangeBinaryMotion,computeVelocityDispersion,darkMatterHaloScale_,galacticStructure_) result(self)
+ function standardConstructorInternal(stellarDensityChangeBinaryMotion,computeVelocityDispersion,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily standard} black hole binary separation growth class.
!!}
implicit none
type (blackHoleBinarySeparationGrowthRateStandard) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: stellarDensityChangeBinaryMotion, computeVelocityDispersion
!![
-
+
!!]
return
@@ -163,7 +157,6 @@ subroutine standardDestructor(self)
!![
-
!!]
return
end subroutine standardDestructor
@@ -173,11 +166,13 @@ double precision function standardGrowthRate(self,blackHole)
Returns an initial separation growth rate for a binary black holes that follows a modified version of
\cite{volonteri_assembly_2003}.
!!}
+ use :: Coordinates , only : coordinateCylindrical , assignment(=)
use :: Display , only : displayIndent , displayMessage , displayUnindent
- use :: Galactic_Structure_Options , only : componentTypeDarkHalo , componentTypeSpheroid , coordinateSystemCylindrical, massTypeDark, &
- & massTypeGalactic , massTypeStellar
+ use :: Galactic_Structure_Options , only : componentTypeDarkHalo , componentTypeSpheroid , massTypeDark , massTypeGalactic,&
+ & massTypeStellar
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentSpheroid , treeNode
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Physical , only : speedLight
@@ -188,18 +183,22 @@ double precision function standardGrowthRate(self,blackHole)
type (treeNode ), pointer :: node
class (nodeComponentBlackHole ), pointer :: blackHoleCentral
class (nodeComponentSpheroid ), pointer :: spheroid
- double precision , parameter :: hardeningRateDimensionless =15.0d0
- double precision , parameter :: outerRadiusMultiplier =10.0d0
- double precision , parameter :: dynamicalFrictionMinimumRadius =0.1d0
- double precision :: coulombLogarithmDarkMatter , coulombLogarithmSpheroid , &
- & densityDarkMatter , densitySpheroid , &
- & densityStellar , dynamicalFrictionAcceleration , &
- & dynamicalFrictionXDarkMatter , dynamicalFrictionXSpheroid , &
- & radiusHardBinary , rateGravitationalWaves , &
- & rateScattering , rateScatteringDynamicalFriction, &
- & rateScatteringStars , rotationCurveGradient , &
- & stellarDensityFractionRemaining , velocityDispersionDarkMatter , &
- & velocityDispersionSpheroid
+ class (massDistributionClass ), pointer :: massDistributionSpheroidStellar_ , massDistributionDarkMatterHalo_ , &
+ & massDistributionGalactic_ , massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistributionSpheroidStellar_ , kinematicsDistributionDarkMatterHalo_
+ double precision , parameter :: hardeningRateDimensionless =15.0d0
+ double precision , parameter :: outerRadiusMultiplier =10.0d0
+ double precision , parameter :: dynamicalFrictionMinimumRadius = 0.1d0
+ double precision :: coulombLogarithmDarkMatter , coulombLogarithmSpheroid , &
+ & densityDarkMatter , densitySpheroid , &
+ & densityStellar , dynamicalFrictionAcceleration , &
+ & dynamicalFrictionXDarkMatter , dynamicalFrictionXSpheroid , &
+ & radiusHardBinary , rateGravitationalWaves , &
+ & rateScattering , rateScatteringDynamicalFriction , &
+ & rateScatteringStars , rotationCurveGradient , &
+ & stellarDensityFractionRemaining , velocityDispersionDarkMatter , &
+ & velocityDispersionSpheroid , velocityRotation
+ type (coordinateCylindrical ) :: coordinates
character (len=24 ) :: message
! Get the host node.
@@ -218,27 +217,28 @@ double precision function standardGrowthRate(self,blackHole)
standardGrowthRate=0.0d0
return
end if
+ ! Get required mass distributions.
+ massDistribution_ => node%massDistribution( )
+ massDistributionSpheroidStellar_ => node%massDistribution(componentType=componentTypeSpheroid,massType=massTypeStellar )
+ massDistributionDarkMatterHalo_ => node%massDistribution(componentType=componentTypeDarkHalo,massType=massTypeDark )
+ massDistributionGalactic_ => node%massDistribution( massType=massTypeGalactic)
! Get the spheroid component.
- spheroid => node%spheroid()
- ! Compute the velocity dispersion of stars and dark matter.
+ spheroid => node%spheroid ( )
+ ! Set coordinates of the black hole.
+ coordinates = [blackHole%radialPosition(),0.0d0,0.0d0]
+ ! Compute the velocity dispersion of stars and dark matter.
if (self%computeVelocityDispersion) then
- velocityDispersionSpheroid =self%galacticStructure_%velocityDispersion( &
- & node, &
- & blackHole %radialPosition( ), &
- & spheroid %radius ( )*outerRadiusMultiplier, &
- & componentTypeSpheroid , &
- & massTypeStellar &
- & )
- velocityDispersionDarkMatter=self%galacticStructure_%velocityDispersion( &
- & node, &
- & blackHole %radialPosition( ), &
- & self %darkMatterHaloScale_%radiusVirial (node)*outerRadiusMultiplier, &
- & componentTypeDarkHalo , &
- & massTypeDark &
- & )
+ kinematicsDistributionSpheroidStellar_ => massDistributionSpheroidStellar_ %kinematicsDistribution( )
+ kinematicsDistributionDarkMatterHalo_ => massDistributionDarkMatterHalo_ %kinematicsDistribution( )
+ velocityDispersionSpheroid = kinematicsDistributionSpheroidStellar_%velocityDispersion1D (coordinates,massDistribution_)
+ velocityDispersionDarkMatter = kinematicsDistributionDarkMatterHalo_ %velocityDispersion1D (coordinates,massDistribution_)
+ !![
+
+
+ !!]
else
- velocityDispersionSpheroid =spheroid %velocity ( )
- velocityDispersionDarkMatter=self %darkMatterHaloScale_%velocityVirial(node)
+ velocityDispersionSpheroid = spheroid %velocity ( )
+ velocityDispersionDarkMatter = self %darkMatterHaloScale_%velocityVirial(node)
end if
! Compute the separation growth rate due to emission of gravitational waves.
rateGravitationalWaves=-( &
@@ -295,12 +295,7 @@ double precision function standardGrowthRate(self,blackHole)
! Limit the density fraction to unity.
stellarDensityFractionRemaining=min(stellarDensityFractionRemaining,1.0d0)
! Compute the stellar density, accounting for any loss.
- densityStellar= self%galacticStructure_%density(node , &
- & [blackHole%radialPosition(),0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & componentType =componentTypeSpheroid , &
- & massType =massTypeStellar &
- & ) &
+ densityStellar=+massDistributionSpheroidStellar_%density(coordinates) &
& *stellarDensityFractionRemaining
! Compute the hardening rate due to strong scattering of individual stars.
if (velocityDispersionSpheroid > 0.0d0) then
@@ -316,16 +311,8 @@ double precision function standardGrowthRate(self,blackHole)
! Check if the binary has sufficiently large separation that we should compute the rate of hardening due to dynamical friction.
if (blackHole%radialPosition() > dynamicalFrictionMinimumRadius*radiusHardBinary) then
! Compute the total density, including dark matter.
- densitySpheroid =self%galacticStructure_%density(node , &
- & [blackHole%radialPosition(),0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & massType =massTypeGalactic &
- & )
- densityDarkMatter=self%galacticStructure_%density(node , &
- & [blackHole%radialPosition(),0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & massType =massTypeDark &
- & )
+ densitySpheroid =massDistributionGalactic_ %density(coordinates)
+ densityDarkMatter=massDistributionDarkMatterHalo_%density(coordinates)
! Compute the Coulomb logarithms for dynamical friction.
coulombLogarithmSpheroid = ( &
& blackHole %radialPosition() &
@@ -352,70 +339,67 @@ double precision function standardGrowthRate(self,blackHole)
! Compute the rotation curve of the galaxy and the additional contribution from the active black hole. Add them in
! quadrature to get an estimate of the actual orbital speed of the black hole binary.
! Precompute the "X" term appearing in the dynamical friction formula.
+ velocityRotation=massDistribution_%rotationCurve(blackHole%radialPosition())
if (velocityDispersionSpheroid > 0.0d0) then
- dynamicalFrictionXSpheroid = self%galacticStructure_%velocityRotation( &
- & node , &
- & blackHole%radialPosition() &
- & ) &
- & /sqrt(2.0d0) &
- & /velocityDispersionSpheroid
+ dynamicalFrictionXSpheroid=+velocityRotation &
+ & /sqrt(2.0d0) &
+ & /velocityDispersionSpheroid
else
- dynamicalFrictionXSpheroid=0.0d0
+ dynamicalFrictionXSpheroid=+0.0d0
end if
- dynamicalFrictionXDarkMatter= self%galacticStructure_%velocityRotation( &
- & node , &
- & blackHole%radialPosition() &
- & ) &
- & /sqrt(2.0d0) &
- & /velocityDispersionDarkMatter
+ dynamicalFrictionXDarkMatter =+velocityRotation &
+ & /sqrt(2.0d0) &
+ & /velocityDispersionDarkMatter
! Compute the acceleration due to dynamical friction.
- dynamicalFrictionAcceleration=-4.0d0 &
- & *Pi &
- & *gravitationalConstantGalacticus**2 &
- & *blackHole%mass() &
- & *0.5d0 &
- & /self%galacticStructure_%velocityRotation(node,blackHole%radialPosition())**2 &
- & /Mpc_per_km_per_s_To_Gyr &
- & *( &
- & densitySpheroid &
- & *log(1.0d0+coulombLogarithmSpheroid**2) &
- & *( &
- & erf(dynamicalFrictionXSpheroid) &
- & -( &
- & 2.0d0 &
- & *dynamicalFrictionXSpheroid &
- & /sqrt(Pi) &
- & *exp(-dynamicalFrictionXSpheroid**2) &
- & ) &
- & ) &
- & +densityDarkMatter &
- & *log(1.0d0+coulombLogarithmDarkMatter**2) &
- & *( &
- & erf(dynamicalFrictionXDarkMatter) &
- & -( &
- & 2.0d0 &
- & *dynamicalFrictionXDarkMatter &
- & /sqrt(Pi) &
- & *exp(-dynamicalFrictionXDarkMatter**2) &
- & ) &
- & ) &
+ dynamicalFrictionAcceleration=-4.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus**2 &
+ & *blackHole%mass() &
+ & *0.5d0 &
+ & /velocityRotation**2 &
+ & /Mpc_per_km_per_s_To_Gyr &
+ & *( &
+ & densitySpheroid &
+ & *log(1.0d0+coulombLogarithmSpheroid**2) &
+ & *( &
+ & erf(dynamicalFrictionXSpheroid) &
+ & -( &
+ & 2.0d0 &
+ & *dynamicalFrictionXSpheroid &
+ & /sqrt(Pi) &
+ & *exp(-dynamicalFrictionXSpheroid**2) &
+ & ) &
+ & ) &
+ & +densityDarkMatter &
+ & *log(1.0d0+coulombLogarithmDarkMatter**2) &
+ & *( &
+ & erf(dynamicalFrictionXDarkMatter) &
+ & -( &
+ & 2.0d0 &
+ & *dynamicalFrictionXDarkMatter &
+ & /sqrt(Pi) &
+ & *exp(-dynamicalFrictionXDarkMatter**2) &
+ & ) &
+ & ) &
& )
! Compute the radial inflow velocity due to dynamical friction.
- rotationCurveGradient =( &
- & +self%galacticStructure_%velocityRotation (node,blackHole%radialPosition()) &
- & + blackHole%radialPosition() &
- & *self%galacticStructure_%velocityRotationGradient(node,blackHole%radialPosition()) &
+ rotationCurveGradient =( &
+ & + velocityRotation &
+ & +0.5d0 &
+ & * blackHole%radialPosition() &
+ & / velocityRotation &
+ & *massDistribution_%rotationCurveGradient(blackHole%radialPosition()) &
& )
if (rotationCurveGradient == 0.0d0) then
call displayIndent('dynamical friction calculation report')
write (message,'(a,i12) ') 'nodeIndex = ',node%index()
- write (message,'(a,e12.6)') ' V(r) = ',self%galacticStructure_%velocityRotation (node,blackHole%radialPosition())
+ write (message,'(a,e12.6)') ' V (r) = ', velocityRotation
call displayMessage(trim(message))
- write (message,'(a,e12.6)') ' r = ', blackHole%radialPosition()
+ write (message,'(a,e12.6)') ' r = ', blackHole%radialPosition()
call displayMessage(trim(message))
- write (message,'(a,e12.6)') ' dV(r)/dr = ',self%galacticStructure_%velocityRotationGradient(node,blackHole%radialPosition())
+ write (message,'(a,e12.6)') ' dV²(r)/dr = ',massDistribution_%rotationCurveGradient (blackHole%radialPosition())
call displayMessage(trim(message))
- write (message,'(a,e12.6)') ' a_{df} = ',dynamicalFrictionAcceleration
+ write (message,'(a,e12.6)') ' a_{df} = ', dynamicalFrictionAcceleration
call displayMessage(trim(message))
call displayUnindent('done')
call Error_Report('rotation curve gradient is zero'//{introspection:location})
@@ -432,5 +416,12 @@ double precision function standardGrowthRate(self,blackHole)
end if
! Sum the two contributions to the radial growth rate.
standardGrowthRate=rateScattering+rateGravitationalWaves
+ ! Clean up.
+ !![
+
+
+
+
+ !!]
return
end function standardGrowthRate
diff --git a/source/cooling.cooling_radius.beta_profile.F90 b/source/cooling.cooling_radius.beta_profile.F90
index 1a638be189..047001e209 100644
--- a/source/cooling.cooling_radius.beta_profile.F90
+++ b/source/cooling.cooling_radius.beta_profile.F90
@@ -22,14 +22,12 @@
time scales as inverse density.
!!}
- use :: Cooling_Times , only : coolingTimeClass
- use :: Cooling_Times_Available , only : coolingTimeAvailableClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
- use :: Kind_Numbers , only : kind_int8
- use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Cooling_Times , only : coolingTimeClass
+ use :: Cooling_Times_Available, only : coolingTimeAvailableClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Kind_Numbers , only : kind_int8
+ use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
!![
@@ -60,8 +58,6 @@
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_ => null()
class (coolingTimeClass ), pointer :: coolingTime_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
integer (kind=kind_int8 ) :: lastUniqueID = -1
integer :: abundancesCount , chemicalsCount
@@ -102,52 +98,42 @@ function betaProfileConstructorParameters(parameters) result(self)
class(coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_
class(coolingTimeClass ), pointer :: coolingTime_
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
!![
-
-
-
-
-
-
+
+
+
+
!!]
- self=coolingRadiusBetaProfile(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_)
+ self=coolingRadiusBetaProfile(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_)
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end function betaProfileConstructorParameters
- function betaProfileConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_) result(self)
+ function betaProfileConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_) result(self)
!!{
Internal constructor for the $\beta$-profile cooling radius class.
!!}
- use :: Abundances_Structure , only : Abundances_Property_Count , abundances
+ use :: Abundances_Structure , only : Abundances_Property_Count, abundances
use :: Array_Utilities , only : operator(.intersection.)
use :: Chemical_Abundances_Structure, only : Chemicals_Property_Count
- use :: Error , only : Component_List , Error_Report
+ use :: Error , only : Component_List , Error_Report
use :: Galacticus_Nodes , only : defaultHotHaloComponent
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionBetaProfile
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileVirial
implicit none
- type (coolingRadiusBetaProfile ) :: self
- class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(coolingTimeAvailableClass ), intent(in ), target :: coolingTimeAvailable_
- class(coolingTimeClass ), intent(in ), target :: coolingTime_
- class(hotHaloTemperatureProfileClass), intent(in ), target :: hotHaloTemperatureProfile_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
+ type (coolingRadiusBetaProfile ) :: self
+ class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class(coolingTimeAvailableClass), intent(in ), target :: coolingTimeAvailable_
+ class(coolingTimeClass ), intent(in ), target :: coolingTime_
!![
-
+
!!]
! Initial state of stored solutions.
@@ -185,24 +171,6 @@ function betaProfileConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_
& ) // &
& {introspection:location} &
& )
- ! Check that assumptions are valid. Note that currently we can not check all assumptions. We check that:
- ! * Hot halo temperature profile is isothermal;
- ! * Hot halo density profile is a β-profile.
- ! We do not check that:
- ! * β=2/3;
- ! * Cooling function is always proportional to ρ².
- select type (hotHaloTemperatureProfile_ => self%hotHaloTemperatureProfile_)
- class is (hotHaloTemperatureProfileVirial)
- ! An isothermal profile - this is acceptable.
- class default
- call Error_Report('assumption of isothermal hot halo temperature profile is not met'//{introspection:location})
- end select
- select type (hotHaloMassDistribution_ => self%hotHaloMassDistribution_ )
- class is (hotHaloMassDistributionBetaProfile)
- ! A beta-model profile - this is acceptable.
- class default
- call Error_Report('assumption of β-model hot halo mass distribution is not met' //{introspection:location})
- end select
return
end function betaProfileConstructorInternal
@@ -227,13 +195,11 @@ subroutine betaProfileDestructor(self)
type(coolingRadiusBetaProfile), intent(inout) :: self
!![
-
-
-
-
-
-
-
+
+
+
+
+
!!]
if (calculationResetEvent%isAttached(self,betaProfileCalculationReset)) call calculationResetEvent%detach(self,betaProfileCalculationReset)
return
@@ -263,18 +229,24 @@ double precision function betaProfileRadiusGrowthRate(self,node)
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances
use :: Chemical_Reaction_Rates_Utilities, only : Chemicals_Mass_To_Fraction_Conversion
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , treeNode
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
- class (coolingRadiusBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentHotHalo ), pointer :: hotHalo
- double precision :: coolingTimeZero , timeAvailable , &
- & densityZero , massToDensityConversion, &
- & temperature , outerRadius , &
- & densityOuter , coolingTimeOuter
- type (abundances ) :: hotAbundances
- type (chemicalAbundances ) :: chemicalFractions, chemicalMasses
+ class (coolingRadiusBetaProfile ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: coolingTimeZero , timeAvailable , &
+ & densityZero , massToDensityConversion, &
+ & temperature , outerRadius , &
+ & densityOuter , coolingTimeOuter
+ type (abundances ) :: hotAbundances
+ type (chemicalAbundances ) :: chemicalFractions , chemicalMasses
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -305,13 +277,22 @@ double precision function betaProfileRadiusGrowthRate(self,node)
call self%radiation%timeSet(basic%time())
! Get the outer radius.
outerRadius=hotHalo%outerRadius()
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Get the temperature.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,outerRadius)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ temperature = kinematicsDistribution_ %temperature(coordinates )
! Compute density and cooling time at outer radius and zero radius.
- densityZero =self%hotHaloMassDistribution_ %density(node,0.0d0 )
- densityOuter =self%hotHaloMassDistribution_ %density(node,outerRadius)
- coolingTimeZero =self%coolingTime_ %time (node,temperature,densityZero ,hotAbundances,chemicalFractions*densityZero,self%radiation)
- coolingTimeOuter=self%coolingTime_ %time (node,temperature,densityOuter,hotAbundances,chemicalFractions*densityOuter,self%radiation)
+ densityOuter = massDistribution_ %density (coordinates )
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ densityZero = massDistribution_ %density (coordinates )
+ coolingTimeZero = self %coolingTime_%time (node,temperature,densityZero ,hotAbundances,chemicalFractions*densityZero ,self%radiation)
+ coolingTimeOuter = self %coolingTime_%time (node,temperature,densityOuter,hotAbundances,chemicalFractions*densityOuter,self%radiation)
+ !![
+
+
+ !!]
if (coolingTimeOuter < timeAvailable .or. coolingTimeZero > timeAvailable) then
! Cooling radius is static.
self%radiusGrowthRateStored=0.0d0
@@ -341,18 +322,24 @@ double precision function betaProfileRadius(self,node)
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances
use :: Chemical_Reaction_Rates_Utilities, only : Chemicals_Mass_To_Fraction_Conversion
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , treeNode
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
- class (coolingRadiusBetaProfile), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- double precision :: coolingTimeZero , timeAvailable , &
- & densityZero , massToDensityConversion, &
- & temperature , outerRadius , &
- & densityOuter , coolingTimeOuter
- type (abundances ) :: hotAbundances
- type (chemicalAbundances ) :: chemicalFractions, chemicalMasses
+ class (coolingRadiusBetaProfile ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ class (nodeComponentBasic ) , pointer :: basic
+ class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: coolingTimeZero , timeAvailable , &
+ & densityZero , massToDensityConversion, &
+ & temperature , outerRadius , &
+ & densityOuter , coolingTimeOuter
+ type (abundances ) :: hotAbundances
+ type (chemicalAbundances ) :: chemicalFractions , chemicalMasses
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -382,13 +369,22 @@ double precision function betaProfileRadius(self,node)
call self%radiation%timeSet(basic%time())
! Get the outer radius.
outerRadius=hotHalo%outerRadius()
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Get the temperature.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,outerRadius)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ temperature = kinematicsDistribution_ %temperature(coordinates )
! Compute density and cooling time at outer radius and zero radius.
- densityZero =self%hotHaloMassDistribution_ %density(node,0.0d0 )
- densityOuter =self%hotHaloMassDistribution_ %density(node,outerRadius )
- coolingTimeZero =self%coolingTime_ %time (node,temperature,densityZero ,hotAbundances,chemicalFractions*densityZero,self%radiation)
- coolingTimeOuter=self%coolingTime_ %time (node,temperature,densityOuter,hotAbundances,chemicalFractions*densityOuter,self%radiation)
+ densityOuter = massDistribution_ %density (coordinates )
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ densityZero = massDistribution_ %density (coordinates )
+ coolingTimeZero = self %coolingTime_%time (node,temperature,densityZero ,hotAbundances,chemicalFractions*densityZero ,self%radiation)
+ coolingTimeOuter = self %coolingTime_%time (node,temperature,densityOuter,hotAbundances,chemicalFractions*densityOuter,self%radiation)
+ !![
+
+
+ !!]
if (coolingTimeOuter < timeAvailable) then
! Cooling time available exceeds cooling time at virial radius, return virial radius.
self%radiusStored=outerRadius
diff --git a/source/cooling.cooling_radius.isothermal_profile.F90 b/source/cooling.cooling_radius.isothermal_profile.F90
index 38fbb2d5f2..c099c20526 100644
--- a/source/cooling.cooling_radius.isothermal_profile.F90
+++ b/source/cooling.cooling_radius.isothermal_profile.F90
@@ -22,13 +22,11 @@
time scales as inverse density.
!!}
- use :: Cooling_Times , only : coolingTimeClass
- use :: Cooling_Times_Available , only : coolingTimeAvailableClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
- use :: Kind_Numbers , only : kind_int8
+ use :: Cooling_Times , only : coolingTimeClass
+ use :: Cooling_Times_Available, only : coolingTimeAvailableClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Kind_Numbers , only : kind_int8
!![
@@ -69,8 +67,6 @@
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_ => null()
class (coolingTimeClass ), pointer :: coolingTime_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
integer (kind=kind_int8 ) :: lastUniqueID = -1
integer :: abundancesCount , chemicalsCount
@@ -106,37 +102,31 @@ function isothermalConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (coolingRadiusIsothermal ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_
- class(coolingTimeClass ), pointer :: coolingTime_
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ type (coolingRadiusIsothermal ) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(coolingTimeAvailableClass), pointer :: coolingTimeAvailable_
+ class(coolingTimeClass ), pointer :: coolingTime_
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
!![
-
-
-
-
-
-
+
+
+
+
!!]
- self=coolingRadiusIsothermal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_)
+ self=coolingRadiusIsothermal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_)
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end function isothermalConstructorParameters
- function isothermalConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_) result(self)
+ function isothermalConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingTimeAvailable_,coolingTime_) result(self)
!!{
Internal constructor for the isothermal cooling radius class.
!!}
@@ -146,15 +136,13 @@ function isothermalConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,
use :: Error , only : Component_List , Error_Report
use :: Galacticus_Nodes , only : defaultHotHaloComponent
implicit none
- type (coolingRadiusIsothermal ) :: self
- class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(coolingTimeAvailableClass ), intent(in ), target :: coolingTimeAvailable_
- class(coolingTimeClass ), intent(in ), target :: coolingTime_
- class(hotHaloTemperatureProfileClass), intent(in ), target :: hotHaloTemperatureProfile_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
+ type (coolingRadiusIsothermal ) :: self
+ class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class(coolingTimeAvailableClass), intent(in ), target :: coolingTimeAvailable_
+ class(coolingTimeClass ), intent(in ), target :: coolingTime_
!![
-
+
!!]
! Initial state of stored solutions.
@@ -216,13 +204,11 @@ subroutine isothermalDestructor(self)
type(coolingRadiusIsothermal), intent(inout) :: self
!![
-
-
-
-
-
-
-
+
+
+
+
+
!!]
if (calculationResetEvent%isAttached(self,isothermalCalculationReset)) call calculationResetEvent%detach(self,isothermalCalculationReset)
return
@@ -288,17 +274,23 @@ double precision function isothermalRadius(self,node)
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances
use :: Chemical_Reaction_Rates_Utilities, only : Chemicals_Mass_To_Fraction_Conversion
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , treeNode
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
- class (coolingRadiusIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- double precision :: coolingTime , timeAvailable , &
- & density , massToDensityConversion, &
- & temperature , radiusVirial
- type (abundances ) :: hotAbundances
- type (chemicalAbundances ) :: chemicalFractions, chemicalMasses
+ class (coolingRadiusIsothermal ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ class (nodeComponentBasic ) , pointer :: basic
+ class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (massDistributionClass ) , pointer :: massDistribution_
+ class (kinematicsDistributionClass) , pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: coolingTime , timeAvailable , &
+ & density , massToDensityConversion, &
+ & temperature , radiusVirial
+ type (abundances ) :: hotAbundances
+ type (chemicalAbundances ) :: chemicalFractions , chemicalMasses
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -328,11 +320,19 @@ double precision function isothermalRadius(self,node)
call self%radiation%timeSet(basic%time())
! Get the virial radius.
radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute density, temperature and abundances.
- density =self%hotHaloMassDistribution_ %density (node,radiusVirial)
- temperature =self%hotHaloTemperatureProfile_%temperature(node,radiusVirial)
+ coordinates=[radiusVirial,0.0d0,0.0d0]
+ density =massDistribution_ %density (coordinates)
+ temperature=kinematicsDistribution_ %temperature(coordinates)
+ !![
+
+
+ !!]
! Compute the cooling time at the virial radius.
- coolingTime =self%coolingTime_ %time (node,temperature,density,hotAbundances,chemicalFractions*density,self%radiation)
+ coolingTime =self %coolingTime_%time (node,temperature,density,hotAbundances,chemicalFractions*density,self%radiation)
if (coolingTime < timeAvailable) then
! Cooling time available exceeds cooling time at virial radius, return virial radius.
self%radiusStored=radiusVirial
diff --git a/source/cooling.cooling_radius.simple.F90 b/source/cooling.cooling_radius.simple.F90
index af3df8615b..490c61335e 100644
--- a/source/cooling.cooling_radius.simple.F90
+++ b/source/cooling.cooling_radius.simple.F90
@@ -26,8 +26,6 @@
use :: Cooling_Times , only : coolingTimeClass
use :: Cooling_Times_Available , only : coolingTimeAvailableClass
use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
use :: Kind_Numbers , only : kind_int8
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
use :: Root_Finder , only : rootFinder
@@ -58,8 +56,6 @@ which the time available for cooling is increasing. This method assumes that the
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (coolingTimeClass ), pointer :: coolingTime_ => null()
class (coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
type (rootFinder ) :: finder
integer (kind=kind_int8 ) :: lastUniqueID = -1
@@ -104,34 +100,28 @@ function simpleConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (coolingRadiusSimple ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(coolingTimeAvailableClass ), pointer :: coolingTimeAvailable_
- class(coolingTimeClass ), pointer :: coolingTime_
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ type (coolingRadiusSimple ) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(coolingTimeAvailableClass), pointer :: coolingTimeAvailable_
+ class(coolingTimeClass ), pointer :: coolingTime_
+ class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
!![
-
-
-
-
-
+
+
+
!!]
- self=coolingRadiusSimple(cosmologyFunctions_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_)
+ self=coolingRadiusSimple(cosmologyFunctions_,coolingTimeAvailable_,coolingTime_)
!![
-
-
-
-
-
+
+
+
!!]
return
end function simpleConstructorParameters
- function simpleConstructorInternal(cosmologyFunctions_,coolingTimeAvailable_,coolingTime_,hotHaloTemperatureProfile_,hotHaloMassDistribution_) result(self)
+ function simpleConstructorInternal(cosmologyFunctions_,coolingTimeAvailable_,coolingTime_) result(self)
!!{
Internal constructor for the simple cooling radius class.
!!}
@@ -141,15 +131,13 @@ function simpleConstructorInternal(cosmologyFunctions_,coolingTimeAvailable_,coo
use :: Error , only : Component_List , Error_Report
use :: Galacticus_Nodes , only : defaultHotHaloComponent
implicit none
- type (coolingRadiusSimple ) :: self
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (coolingTimeAvailableClass ), intent(in ), target :: coolingTimeAvailable_
- class (coolingTimeClass ), intent(in ), target :: coolingTime_
- class (hotHaloTemperatureProfileClass), intent(in ), target :: hotHaloTemperatureProfile_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
+ type (coolingRadiusSimple ) :: self
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (coolingTimeAvailableClass), intent(in ), target :: coolingTimeAvailable_
+ class (coolingTimeClass ), intent(in ), target :: coolingTime_
+ double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
!![
-
+
!!]
! Initial state of stored solutions.
@@ -217,12 +205,10 @@ subroutine simpleDestructor(self)
type(coolingRadiusSimple), intent(inout) :: self
!![
-
-
-
-
-
-
+
+
+
+
!!]
if (calculationResetEvent%isAttached(self,simpleCalculationReset)) call calculationResetEvent%detach(self,simpleCalculationReset)
return
@@ -249,18 +235,24 @@ double precision function simpleRadiusGrowthRate(self,node)
!!{
Returns the cooling radius growth rate (in Mpc/Gyr) in the hot atmosphere.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , treeNode
+ use :: Mass_Distributions , only : massDistributionClass, kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
- class (coolingRadiusSimple ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentHotHalo), pointer :: hotHalo
- double precision :: coolingRadius , coolingTimeAvailable , &
- & coolingTimeAvailableIncreaseRate, coolingTimeDensityLogSlope, &
- & coolingTimeTemperatureLogSlope , density , &
- & densityLogSlope , outerRadius , &
- & temperature , temperatureLogSlope , &
- & slope
+ class (coolingRadiusSimple ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: coolingRadius , coolingTimeAvailable , &
+ & coolingTimeAvailableIncreaseRate, coolingTimeDensityLogSlope, &
+ & coolingTimeTemperatureLogSlope , density , &
+ & densityLogSlope , outerRadius , &
+ & temperature , temperatureLogSlope , &
+ & slope
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -282,10 +274,17 @@ double precision function simpleRadiusGrowthRate(self,node)
basic => node%basic()
call self%radiation%timeSet(basic%time())
! Get density and temperature at the cooling radius, plus their gradients.
- density =self%hotHaloMassDistribution_ %density (node,coolingRadius)
- temperature =self%hotHaloTemperatureProfile_%temperature (node,coolingRadius)
- densityLogSlope =self%hotHaloMassDistribution_ %densityLogSlope (node,coolingRadius)
- temperatureLogSlope=self%hotHaloTemperatureProfile_%temperatureLogSlope(node,coolingRadius)
+ coordinates = [coolingRadius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution ( )
+ density = massDistribution_ %density (coordinates )
+ temperature = kinematicsDistribution_%temperature (coordinates )
+ densityLogSlope = massDistribution_ %densityGradientRadial (coordinates,logarithmic=.true. )
+ temperatureLogSlope = kinematicsDistribution_%temperatureGradientLogarithmic(coordinates )
+ !![
+
+
+ !!]
! Get the time available for cooling in node and its rate of increase.
coolingTimeAvailable =self%coolingTimeAvailable_%timeAvailable (node)
coolingTimeAvailableIncreaseRate=self%coolingTimeAvailable_%timeAvailableIncreaseRate(node)
@@ -396,20 +395,37 @@ double precision function coolingRadiusRoot(radius)
!!{
Root function which evaluates the difference between the cooling time at {\normalfont \ttfamily radius} and the time available for cooling.
!!}
+ use :: Mass_Distributions , only : massDistributionClass, kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
- double precision , intent(in ) :: radius
- double precision :: coolingTime , density, &
- & temperature
- type (chemicalAbundances), save :: densityChemicals
+ double precision , intent(in ) :: radius
+ double precision :: coolingTime , density, &
+ & temperature
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_
+ type (chemicalAbundances ), save :: densityChemicals
!$omp threadprivate(densityChemicals)
-
+ type (coordinateSpherical ) :: coordinates
+
! Compute density, temperature and abundances.
- density =self_%hotHaloMassDistribution_ %density (node_,radius )
- temperature =self_%hotHaloTemperatureProfile_%temperature(node_,radius )
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node_ %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ density = massDistribution_ %density (coordinates )
+ if (associated(kinematicsDistribution_)) then
+ temperature = kinematicsDistribution_%temperature (coordinates )
+ else
+ temperature = 0.0d0
+ end if
+ !![
+
+
+ !!]
densityChemicals=fractionsChemical_
call densityChemicals%scale(density)
! Compute the cooling time at the specified radius.
- coolingTime =self_%coolingTime_ %time (node_,temperature,density,abundancesGas_,densityChemicals,self_%radiation)
+ coolingTime=self_%coolingTime_%time(node_,temperature,density,abundancesGas_,densityChemicals,self_%radiation)
! Return the difference between cooling time and time available.
coolingRadiusRoot=coolingTime-coolingTimeAvailable_
return
diff --git a/source/cooling.cooling_rate.Cole2000.F90 b/source/cooling.cooling_rate.Cole2000.F90
index 57a9697ba5..4fabfdebc3 100644
--- a/source/cooling.cooling_rate.Cole2000.F90
+++ b/source/cooling.cooling_rate.Cole2000.F90
@@ -21,8 +21,7 @@
Implementation of a cooling rate class for the \cite{cole_hierarchical_2000} cooling rate calculation.
!!}
- use :: Cooling_Infall_Radii , only : coolingInfallRadiusClass
- use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
+ use :: Cooling_Infall_Radii, only : coolingInfallRadiusClass
!![
@@ -43,8 +42,7 @@
Implementation of cooling rate class for the \cite{cole_hierarchical_2000} cooling rate calculation.
!!}
private
- class(coolingInfallRadiusClass ), pointer :: coolingInfallRadius_ => null()
- class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
+ class(coolingInfallRadiusClass), pointer :: coolingInfallRadius_ => null()
contains
final :: cole2000Destructor
procedure :: rate => cole2000Rate
@@ -66,34 +64,30 @@ function cole2000ConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (coolingRateCole2000 ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(coolingInfallRadiusClass ), pointer :: coolingInfallRadius_
- class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_
+ type (coolingRateCole2000 ) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(coolingInfallRadiusClass), pointer :: coolingInfallRadius_
!![
-
-
+
!!]
- self=coolingRateCole2000(coolingInfallRadius_,hotHaloMassDistribution_)
+ self=coolingRateCole2000(coolingInfallRadius_)
!![
-
-
+
!!]
return
end function cole2000ConstructorParameters
- function cole2000ConstructorInternal(coolingInfallRadius_,hotHaloMassDistribution_) result(self)
+ function cole2000ConstructorInternal(coolingInfallRadius_) result(self)
!!{
Internal constructor for the \cite{cole_hierarchical_2000} cooling rate class.
!!}
implicit none
- type (coolingRateCole2000 ) :: self
- class(coolingInfallRadiusClass ), intent(in ), target :: coolingInfallRadius_
- class(hotHaloMassDistributionClass), intent(in ), target :: hotHaloMassDistribution_
+ type (coolingRateCole2000 ) :: self
+ class(coolingInfallRadiusClass), intent(in ), target :: coolingInfallRadius_
!![
-
+
!!]
return
@@ -107,8 +101,7 @@ subroutine cole2000Destructor(self)
type(coolingRateCole2000), intent(inout) :: self
!![
-
-
+
!!]
return
end subroutine cole2000Destructor
@@ -118,15 +111,20 @@ double precision function cole2000Rate(self,node)
Returns the cooling rate (in $M_\odot$ Gyr$^{-1}$) in the hot atmosphere for the \cite{white_galaxy_1991} cooling rate
model.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentHotHalo, treeNode
- use :: Numerical_Constants_Math, only : Pi
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
- class (coolingRateCole2000 ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basicFormation
- class (nodeComponentHotHalo), pointer :: hotHaloFormation
- double precision :: densityInfall , radiusInfall, &
- & radiusInfallGrowthRate , radiusOuter
+ class (coolingRateCole2000 ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ class (nodeComponentBasic ), pointer :: basicFormation
+ class (nodeComponentHotHalo ), pointer :: hotHaloFormation
+ class (massDistributionClass), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: densityInfall , radiusInfall, &
+ & radiusInfallGrowthRate , radiusOuter
!$GLC attributes unused :: self
! Get formation node components.
@@ -146,9 +144,14 @@ double precision function cole2000Rate(self,node)
cole2000Rate=0.0d0
else
! Find the density at the infall radius.
- densityInfall = self%hotHaloMassDistribution_%density (node%formationNode,radiusInfall)
+ coordinates = [radiusInfall,0.0d0,0.0d0]
+ massDistribution_ => node %formationNode %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ densityInfall = massDistribution_ %density (coordinates )
+ !![
+
+ !!]
! Find infall radius growth rate.
- radiusInfallGrowthRate = self%coolingInfallRadius_ %radiusIncreaseRate(node%formationNode )
+ radiusInfallGrowthRate = self %coolingInfallRadius_%radiusIncreaseRate(node%formationNode )
! Compute the infall rate.
cole2000Rate = +4.0d0 &
& *Pi &
diff --git a/source/cooling.cooling_rate.White-Frenk.F90 b/source/cooling.cooling_rate.White-Frenk.F90
index 757f664b0f..31eb25082c 100644
--- a/source/cooling.cooling_rate.White-Frenk.F90
+++ b/source/cooling.cooling_rate.White-Frenk.F90
@@ -21,9 +21,8 @@
Implementation of a cooling rate class for the \cite{white_galaxy_1991} cooling rate calculation.
!!}
- use :: Cooling_Infall_Radii , only : coolingInfallRadiusClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
+ use :: Cooling_Infall_Radii , only : coolingInfallRadiusClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -44,10 +43,9 @@
Implementation of cooling rate class for the \cite{white_galaxy_1991} cooling rate calculation.
!!}
private
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (coolingInfallRadiusClass ), pointer :: coolingInfallRadius_ => null()
- class (hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
- double precision :: velocityCutOff
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class (coolingInfallRadiusClass), pointer :: coolingInfallRadius_ => null()
+ double precision :: velocityCutOff
contains
final :: whiteFrenk1991Destructor
procedure :: rate => whiteFrenk1991Rate
@@ -69,12 +67,11 @@ function whiteFrenk1991ConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (coolingRateWhiteFrenk1991 ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (coolingInfallRadiusClass ), pointer :: coolingInfallRadius_
- class (hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_
- double precision :: velocityCutOff
+ type (coolingRateWhiteFrenk1991) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class (coolingInfallRadiusClass ), pointer :: coolingInfallRadius_
+ double precision :: velocityCutOff
!![
@@ -83,21 +80,19 @@ function whiteFrenk1991ConstructorParameters(parameters) result(self)
1.0d4
The halo virial velocity (in km/s) above which cooling rates are forced to zero in the \cite{white_galaxy_1991} cooling rate model.
-
-
-
+
+
!!]
- self=coolingRateWhiteFrenk1991(velocityCutOff,darkMatterHaloScale_,coolingInfallRadius_,hotHaloMassDistribution_)
+ self=coolingRateWhiteFrenk1991(velocityCutOff,darkMatterHaloScale_,coolingInfallRadius_)
!![
-
-
-
+
+
!!]
return
end function whiteFrenk1991ConstructorParameters
- function whiteFrenk1991ConstructorInternal(velocityCutOff,darkMatterHaloScale_,coolingInfallRadius_,hotHaloMassDistribution_) result(self)
+ function whiteFrenk1991ConstructorInternal(velocityCutOff,darkMatterHaloScale_,coolingInfallRadius_) result(self)
!!{
Internal constructor for the \cite{white_galaxy_1991} cooling rate class.
!!}
@@ -105,13 +100,12 @@ function whiteFrenk1991ConstructorInternal(velocityCutOff,darkMatterHaloScale_,c
use :: Error , only : Component_List , Error_Report
use :: Galacticus_Nodes, only : defaultHotHaloComponent
implicit none
- type (coolingRateWhiteFrenk1991 ) :: self
- double precision , intent(in ) :: velocityCutOff
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (coolingInfallRadiusClass ), intent(in ), target :: coolingInfallRadius_
- class (hotHaloMassDistributionClass), intent(in ), target :: hotHaloMassDistribution_
+ type (coolingRateWhiteFrenk1991) :: self
+ double precision , intent(in ) :: velocityCutOff
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class (coolingInfallRadiusClass ), intent(in ), target :: coolingInfallRadius_
!![
-
+
!!]
! Check that the properties we need are gettable.
@@ -143,9 +137,8 @@ subroutine whiteFrenk1991Destructor(self)
type(coolingRateWhiteFrenk1991), intent(inout) :: self
!![
-
-
-
+
+
!!]
return
end subroutine whiteFrenk1991Destructor
@@ -155,14 +148,19 @@ double precision function whiteFrenk1991Rate(self,node)
Returns the cooling rate (in $M_\odot$ Gyr$^{-1}$) in the hot atmosphere for the \cite{white_galaxy_1991} cooling rate
model.
!!}
- use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
- use :: Numerical_Constants_Math, only : Pi
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo , treeNode
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
class (coolingRateWhiteFrenk1991), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentHotHalo ), pointer :: hotHalo
- double precision :: densityCooling , radiusInfall , &
- & radiusOuter , velocityVirial, &
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: densityCooling , radiusInfall , &
+ & radiusOuter , velocityVirial, &
& radiusInfallGrowthRate
! Get the virial velocity.
@@ -182,10 +180,15 @@ double precision function whiteFrenk1991Rate(self,node)
whiteFrenk1991Rate=+hotHalo %mass ( ) &
& /self %darkMatterHaloScale_%timescaleDynamical(node)
else
- ! Find the density at the cooling radius.
- densityCooling = self%hotHaloMassDistribution_%density (node,radiusInfall)
+ ! Find the density at the cooling radius.
+ coordinates = [radiusInfall,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ densityCooling = massDistribution_ %density (coordinates )
+ !![
+
+ !!]
! Find infall radius growth rate.
- radiusInfallGrowthRate = self%coolingInfallRadius_ %radiusIncreaseRate(node )
+ radiusInfallGrowthRate = self %coolingInfallRadius_%radiusIncreaseRate(node )
! Compute the infall rate.
whiteFrenk1991Rate = +4.0d0 &
& *Pi &
diff --git a/source/cooling.cooling_rate.velocity_maximum_scaling.F90 b/source/cooling.cooling_rate.velocity_maximum_scaling.F90
index 55ba461778..b2259a76ed 100644
--- a/source/cooling.cooling_rate.velocity_maximum_scaling.F90
+++ b/source/cooling.cooling_rate.velocity_maximum_scaling.F90
@@ -238,21 +238,27 @@ double precision function velocityMaximumScalingRate(self,node)
!!{
Returns the cooling rate (in $M_\odot$ Gyr$^{-1}$) in the hot atmosphere for a model in which this rate scales with the maximum circular velocity of the halo.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class (coolingRateVelocityMaximumScaling), intent(inout) :: self
type (treeNode ), intent(inout) :: node
double precision , parameter :: expArgumentMaximum=100.0d0
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: expFactor , expansionFactor, &
& expArgument , velocityMaximum
! Compute expansion factor and maximum velocity.
- basic => node %basic ( )
- hotHalo => node %hotHalo ( )
- expansionFactor = self%cosmologyFunctions_ %expansionFactor (basic%time())
- velocityMaximum = self%darkMatterProfileDMO_%circularVelocityMaximum(node )
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ basic => node %basic ( )
+ hotHalo => node %hotHalo ( )
+ expansionFactor = self%cosmologyFunctions_ %expansionFactor (basic%time())
+ velocityMaximum = massDistribution_ %velocityRotationCurveMaximum( )
+ !![
+
+ !!]
if (expansionFactor /= self%expansionFactorPrevious .or. velocityMaximum /= self%velocityMaximumPrevious) then
expArgument=log10( &
& +velocityMaximum &
diff --git a/source/cooling.freefall_radii.dark_matter_halo.F90 b/source/cooling.freefall_radii.dark_matter_halo.F90
index ee003ee650..fe8918f58f 100644
--- a/source/cooling.freefall_radii.dark_matter_halo.F90
+++ b/source/cooling.freefall_radii.dark_matter_halo.F90
@@ -108,37 +108,49 @@ subroutine darkMatterHaloDestructor(self)
return
end subroutine darkMatterHaloDestructor
- double precision function darkMatterHaloRadiusGrowthRate(self,node)
+ double precision function darkMatterHaloRadiusGrowthRate(self,node) result(radiusGrowthRate)
!!{
Returns the freefall radius growth rate (in Mpc/Gyr) in the hot atmosphere.
!!}
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class (freefallRadiusDarkMatterHalo ), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- double precision :: timeAvailable, timeAvailableIncreaseRate
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision :: timeAvailable , timeAvailableIncreaseRate
! Get the time available for freefall.
- timeAvailable =+self%freefallTimeAvailable_%timeAvailable (node )
+ timeAvailable = +self %freefallTimeAvailable_ %timeAvailable (node )
! Get the rate of increase of the time available for freefall.
- timeAvailableIncreaseRate =+self%freefallTimeAvailable_%timeAvailableIncreaseRate (node )
+ timeAvailableIncreaseRate = +self %freefallTimeAvailable_ %timeAvailableIncreaseRate(node )
! Get freefall radius increase rate from dark matter profile.
- darkMatterHaloRadiusGrowthRate=+self%darkMatterProfileDMO_ %freefallRadiusIncreaseRate(node,timeAvailable ) &
- & * timeAvailableIncreaseRate
+ massDistribution_ => self %darkMatterProfileDMO_ %get (node )
+ radiusGrowthRate = +massDistribution_%radiusFreefallIncreaseRate (timeAvailable ) &
+ & * timeAvailableIncreaseRate
+ !![
+
+ !!]
return
end function darkMatterHaloRadiusGrowthRate
- double precision function darkMatterHaloRadius(self,node)
+ double precision function darkMatterHaloRadius(self,node) result(radius)
!!{
Return the freefall radius in the darkMatterHalo model.
!!}
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class (freefallRadiusDarkMatterHalo ), intent(inout) :: self
type (treeNode ), intent(inout) :: node
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: timeAvailable
! Get the time available for freefall.
- timeAvailable =self%freefallTimeAvailable_%timeAvailable (node )
+ timeAvailable = self %freefallTimeAvailable_%timeAvailable(node )
! Get freefall radius from dark matter profile.
- darkMatterHaloRadius=self%darkMatterProfileDMO_ %freefallRadius(node,timeAvailable)
+ massDistribution_ => self %darkMatterProfileDMO_ %get (node )
+ radius = massDistribution_%radiusFreefall (timeAvailable)
+ !![
+
+ !!]
return
end function darkMatterHaloRadius
diff --git a/source/cooling.specific_angular_momentum.constant_rotation.F90 b/source/cooling.specific_angular_momentum.constant_rotation.F90
index 95fc1dfe8a..a43259478f 100644
--- a/source/cooling.specific_angular_momentum.constant_rotation.F90
+++ b/source/cooling.specific_angular_momentum.constant_rotation.F90
@@ -22,9 +22,9 @@ Implementation of a specific angular momentum of cooling gas class assuming a co
radius.
!!}
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
- use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
- use :: Kind_Numbers , only : kind_int8
+ use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Kind_Numbers , only : kind_int8
! Enumeration for angular momentum source.
!![
@@ -70,8 +70,8 @@ A cooling specific angular momentum class which assumes a constant rotation velo
Implementation of the specific angular momentum of cooling gas class which assumes a constant rotation velocity as a function of radius.
!!}
private
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
integer (kind=kind_int8 ) :: lastUniqueID
logical :: angularMomentumSpecificComputed
double precision :: angularMomentumSpecificPrevious
@@ -108,7 +108,7 @@ function constantRotationConstructorParameters(parameters) result(self)
type (coolingSpecificAngularMomentumConstantRotation) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
logical :: useInteriorMean
type (varying_string ) :: sourceAngularMomentumSpecificMean, sourceNormalizationRotation
@@ -137,36 +137,36 @@ function constantRotationConstructorParameters(parameters) result(self)
Specifies whether to use the specific angular momentum at the cooling radius, or the mean specific angular momentum interior to that radius.
-
-
+
+
!!]
self=coolingSpecificAngularMomentumConstantRotation( &
+ & darkMatterHaloScale_ , &
& darkMatterProfileDMO_ , &
- & hotHaloMassDistribution_ , &
& enumerationAngularMomentumSourceEncode(char(sourceAngularMomentumSpecificMean),includesPrefix=.false.), &
& enumerationAngularMomentumSourceEncode(char(sourceNormalizationRotation ),includesPrefix=.false.), &
& useInteriorMean &
& )
!![
-
-
+
+
!!]
return
end function constantRotationConstructorParameters
- function constantRotationConstructorInternal(darkMatterProfileDMO_,hotHaloMassDistribution_,sourceAngularMomentumSpecificMean,sourceNormalizationRotation,useInteriorMean) result(self)
+ function constantRotationConstructorInternal(darkMatterHaloScale_,darkMatterProfileDMO_,sourceAngularMomentumSpecificMean,sourceNormalizationRotation,useInteriorMean) result(self)
!!{
Internal constructor for the darkMatterHalo freefall radius class.
!!}
implicit none
type (coolingSpecificAngularMomentumConstantRotation) :: self
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
type (enumerationAngularMomentumSourceType ), intent(in ) :: sourceAngularMomentumSpecificMean, sourceNormalizationRotation
logical , intent(in ) :: useInteriorMean
!![
-
+
!!]
self%lastUniqueID =-1_kind_int8
@@ -194,8 +194,8 @@ subroutine constantRotationDestructor(self)
type(coolingSpecificAngularMomentumConstantRotation), intent(inout) :: self
!![
-
-
+
+
!!]
if (calculationResetEvent%isAttached(self,constantRotationCalculationReset)) call calculationResetEvent%detach(self,constantRotationCalculationReset)
return
@@ -208,6 +208,8 @@ double precision function constantRotationAngularMomentumSpecific(self,node,radi
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, nodeComponentSpin, treeNode
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class (coolingSpecificAngularMomentumConstantRotation), intent(inout) :: self
type (treeNode ), intent(inout) :: node
@@ -215,6 +217,7 @@ double precision function constantRotationAngularMomentumSpecific(self,node,radi
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSpin ), pointer :: spin
class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: angularMomentumSpecificMean, normalizationRotation
! Check if node differs from previous one for which we performed calculations.
@@ -243,9 +246,20 @@ double precision function constantRotationAngularMomentumSpecific(self,node,radi
! Compute the rotation normalization.
select case (self%sourceNormalizationRotation %ID)
case (angularMomentumSourceDarkMatter%ID)
- normalizationRotation=self%darkMatterProfileDMO_ %rotationNormalization(node)
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ normalizationRotation = +massDistribution_ %densityRadialMoment(moment =2.0d0 ,radiusMaximum=self%darkMatterHaloScale_%radiusVirial(node)) &
+ & /massDistribution_ %densityRadialMoment(moment =3.0d0 ,radiusMaximum=self%darkMatterHaloScale_%radiusVirial(node))
+ !![
+
+ !!]
case (angularMomentumSourceHotGas %ID)
- normalizationRotation=self%hotHaloMassDistribution_%rotationNormalization(node)
+ hotHalo => node %hotHalo ( )
+ massDistribution_ => node %massDistribution (componentType=componentTypeHotHalo,massType =massTypeGaseous )
+ normalizationRotation = +massDistribution_ %densityRadialMoment(moment =2.0d0 ,radiusMaximum=hotHalo %outerRadius ( )) &
+ & /massDistribution_ %densityRadialMoment(moment =3.0d0 ,radiusMaximum=hotHalo %outerRadius ( ))
+ !![
+
+ !!]
case default
normalizationRotation=0.0d0
call Error_Report('unknown profile type'//{introspection:location})
@@ -259,13 +273,17 @@ double precision function constantRotationAngularMomentumSpecific(self,node,radi
! Return the computed value.
if (self%useInteriorMean) then
! Find the specific angular momentum interior to the specified radius.
- constantRotationAngularMomentumSpecific=+self %angularMomentumSpecificPrevious &
- & *self%hotHaloMassDistribution_%radialMoment (node,3.0d0,radius) &
- & /self%hotHaloMassDistribution_%radialMoment (node,2.0d0,radius)
+ massDistribution_ => node %massDistribution (componentType=componentTypeHotHalo,massType=massTypeGaseous)
+ constantRotationAngularMomentumSpecific = +self %angularMomentumSpecificPrevious &
+ & *massDistribution_%densityRadialMoment (3.0d0,radius) &
+ & /massDistribution_%densityRadialMoment (2.0d0,radius)
+ !![
+
+ !!]
else
! Find the specific angular momentum at the specified radius.
- constantRotationAngularMomentumSpecific=+self %angularMomentumSpecificPrevious &
- & * radius
+ constantRotationAngularMomentumSpecific = +self %angularMomentumSpecificPrevious &
+ & * radius
end if
else
! Radius is non-positive - return zero.
diff --git a/source/cooling.time_available.Benson_Bower_2010.F90 b/source/cooling.time_available.Benson_Bower_2010.F90
index e546530da3..525bdb6738 100644
--- a/source/cooling.time_available.Benson_Bower_2010.F90
+++ b/source/cooling.time_available.Benson_Bower_2010.F90
@@ -21,12 +21,10 @@
Implementation of a time available for cooling class using the model of \cite{benson_galaxy_2010-1}.
!!}
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
- use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Cooling_Functions , only : coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Chemical_States , only : chemicalStateClass
+ use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Cooling_Functions , only : coolingFunctionClass
+ use :: Cosmology_Functions, only : cosmologyFunctionsClass
+ use :: Chemical_States , only : chemicalStateClass
!![
@@ -46,12 +44,10 @@
Implementation of a time available for cooling class using the model of \cite{benson_galaxy_2010-1}.
!!}
private
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
- class (chemicalStateClass ), pointer :: chemicalState_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class (chemicalStateClass ), pointer :: chemicalState_ => null()
+ type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
integer :: energyRadiatedID
contains
@@ -78,32 +74,26 @@ function bensonBower2010ConstructorParameters(parameters) result(self)
implicit none
type (coolingTimeAvailableBensonBower2010) :: self
type (inputParameters ), intent(inout) :: parameters
- class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(coolingFunctionClass ), pointer :: coolingFunction_
class(chemicalStateClass ), pointer :: chemicalState_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
-
-
-
-
+
+
+
!!]
- self=coolingTimeAvailableBensonBower2010(cosmologyFunctions_,coolingFunction_,hotHaloTemperatureProfile_,chemicalState_,galacticStructure_)
+ self=coolingTimeAvailableBensonBower2010(cosmologyFunctions_,coolingFunction_,chemicalState_)
!![
-
-
-
-
-
+
+
+
!!]
return
end function bensonBower2010ConstructorParameters
- function bensonBower2010ConstructorInternal(cosmologyFunctions_,coolingFunction_,hotHaloTemperatureProfile_,chemicalState_,galacticStructure_) result(self)
+ function bensonBower2010ConstructorInternal(cosmologyFunctions_,coolingFunction_,chemicalState_) result(self)
!!{
Internal constructor for the \cite{benson_galaxy_2010-1} cooling rate class.
!!}
@@ -111,11 +101,9 @@ function bensonBower2010ConstructorInternal(cosmologyFunctions_,coolingFunction_
type (coolingTimeAvailableBensonBower2010) :: self
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(coolingFunctionClass ), intent(in ), target :: coolingFunction_
- class(hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class(chemicalStateClass ), intent(in ), target :: chemicalState_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
allocate(self%radiation)
@@ -134,12 +122,10 @@ subroutine bensonBower2010Destructor(self)
type(coolingTimeAvailableBensonBower2010), intent(inout) :: self
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end subroutine bensonBower2010Destructor
@@ -152,8 +138,10 @@ double precision function bensonBower2010TimeAvailable(self,node)
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances , Chemicals_Property_Count
use :: Chemical_Reaction_Rates_Utilities, only : Chemicals_Mass_To_Density_Conversion
- use :: Galactic_Structure_Options , only : radiusLarge , massTypeGalactic
- use :: Numerical_Constants_Astronomical , only : gigaYear , massSolar , megaParsec
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous , massTypeGalactic
+ use :: Numerical_Constants_Astronomical , only : gigaYear , massSolar , megaParsec
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Physical , only : boltzmannsConstant
use :: Numerical_Constants_Prefixes , only : hecto , centi
@@ -164,6 +152,9 @@ double precision function bensonBower2010TimeAvailable(self,node)
type (treeNode ), intent(inout) :: node
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
double precision :: density , temperature , &
& massToDensityConversion, numberDensityHydrogen, &
& numberDensityAllSpecies, coolingFunction , &
@@ -171,22 +162,34 @@ double precision function bensonBower2010TimeAvailable(self,node)
type (abundances ) :: abundances_
type (chemicalAbundances ) :: chemicalDensities_ , chemicalMasses_
- basic => node %basic ( )
- hotHalo => node %hotHalo ( )
- massNotional = +hotHalo %mass ( ) &
- & +hotHalo %outflowedMass( ) &
- & +self %galacticStructure_%massEnclosed (node,radiusLarge,massType=massTypeGalactic)
+ massDistribution_ => node %massDistribution(massType=massTypeGalactic)
+ basic => node %basic ( )
+ hotHalo => node %hotHalo ( )
+ massNotional = +hotHalo %mass ( ) &
+ & +hotHalo %outflowedMass ( ) &
+ & +massDistribution_%massTotal ( )
+ !![
+
+ !!]
if (massNotional <= 0.0d0) then
bensonBower2010TimeAvailable=0.0d0
return
end if
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentType=componentTypeHotHalo,massType=massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute the mean density and temperature of the hot halo.
density =+massNotional &
& *3.0d0 &
& /4.0d0 &
& /Pi &
& /hotHalo%outerRadius()**3
- temperature=self%hotHaloTemperatureProfile_%temperature(node,hotHalo%outerRadius())
+ coordinates=[hotHalo%outerRadius(),0.0d0,0.0d0]
+ temperature=+kinematicsDistribution_%temperature(coordinates)
+ !![
+
+
+ !!]
! Get the abundances for this node.
abundances_=hotHalo%abundances()
call abundances_%massToMassFraction(hotHalo%mass())
diff --git a/source/dark_matter_halos.spins.F90 b/source/dark_matter_halos.spins.F90
index 2ac4be7df2..876ae02070 100644
--- a/source/dark_matter_halos.spins.F90
+++ b/source/dark_matter_halos.spins.F90
@@ -64,22 +64,23 @@ subroutine assertPropertiesGettable()
return
end subroutine assertPropertiesGettable
- double precision function Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterProfileDMO_,darkMatterHaloScale_,useBullockDefinition)
+ double precision function Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterHaloScale_,useBullockDefinition) result(angularMomentumScale)
!!{
Returns the characteristic angular momentum scale of {\normalfont \ttfamily node} (as used in spin definitions) based on its mass, and energy.
!!}
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
use :: Error , only : Error_Report
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly , massTypeDark
implicit none
type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass), intent(inout), optional :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), intent(inout), optional :: darkMatterHaloScale_
+ class (darkMatterHaloScaleClass ), intent(inout) :: darkMatterHaloScale_
logical , intent(in ), optional :: useBullockDefinition
class (nodeComponentBasic ), pointer :: basic
- !![
+ class (massDistributionClass ), pointer :: massDistribution_
+ !![
!!]
@@ -88,19 +89,21 @@ double precision function Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatte
if (useBullockDefinition_) then
! Use the halo angular momentum scale used in the Bullock et al. (2001; http://adsabs.harvard.edu/abs/2001ApJ...555..240B)
! definition of halo spin.
- if (.not.present(darkMatterHaloScale_ )) call Error_Report('"darkMatterHaloScale_" must be supplied' //{introspection:location})
- Dark_Matter_Halo_Angular_Momentum_Scale=+sqrt(2.0d0) &
- & *basic %mass ( ) &
- & *darkMatterHaloScale_%velocityVirial(node) &
- & *darkMatterHaloScale_%radiusVirial (node)
+ angularMomentumScale=+sqrt(2.0d0) &
+ & *basic %mass ( ) &
+ & *darkMatterHaloScale_%velocityVirial(node) &
+ & *darkMatterHaloScale_%radiusVirial (node)
else
! Use the halo angular momentum scale used in the Peebles (1971; http://adsabs.harvard.edu/abs/1971A%26A....11..377P)
! definition of halo spin.
- if (.not.present(darkMatterProfileDMO_)) call Error_Report('"darkMatterProfileDMO_" must be supplied'//{introspection:location})
- Dark_Matter_Halo_Angular_Momentum_Scale=+gravitationalConstantGalacticus &
- & * basic %mass ( )**2.5d0 &
- & /sqrt(abs(darkMatterProfileDMO_%energy(node) ))
- end if
+ massDistribution_ => node%massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ angularMomentumScale = +gravitationalConstantGalacticus &
+ & * basic %mass ( ) **2.5d0 &
+ & /sqrt(abs(massDistribution_%energy(darkMatterHaloScale_%radiusVirial(node),massDistribution_)))
+ !![
+
+ !!]
+ end if
return
end function Dark_Matter_Halo_Angular_Momentum_Scale
diff --git a/source/dark_matter_halos.spins.distributions.Bett2007.F90 b/source/dark_matter_halos.spins.distributions.Bett2007.F90
index 9c356f62b2..148a75e880 100644
--- a/source/dark_matter_halos.spins.distributions.Bett2007.F90
+++ b/source/dark_matter_halos.spins.distributions.Bett2007.F90
@@ -22,8 +22,8 @@ An implementation of the dark matter halo spin distribution which uses the fitti
\cite{bett_spin_2007}.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMO, darkMatterProfileDMOClass
- use :: Tables , only : table1D , table1DLogarithmicLinear
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Tables , only : table1D , table1DLogarithmicLinear
!![
@@ -39,7 +39,7 @@ An implementation of the dark matter halo spin distribution which uses the fitti
A dark matter halo spin distribution class which assumes a \cite{bett_spin_2007} distribution.
!!}
private
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
double precision :: alpha , lambda0, &
& normalization
type (table1DLogarithmicLinear ) :: distributionTable
@@ -76,8 +76,8 @@ function bett2007ConstructorParameters(parameters) result(self)
implicit none
type (haloSpinDistributionBett2007) :: self
type (inputParameters ), intent(inout) :: parameters
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- double precision :: lambda0 , alpha
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ double precision :: lambda0 , alpha
! Check and read parameters.
!![
@@ -95,17 +95,17 @@ function bett2007ConstructorParameters(parameters) result(self)
\citep{bett_spin_2007}
The parameter $\alpha$ in the halo spin distribution of \cite{bett_spin_2007}.
-
+
!!]
- self=haloSpinDistributionBett2007(lambda0,alpha,darkMatterProfileDMO_)
+ self=haloSpinDistributionBett2007(lambda0,alpha,darkMatterHaloScale_)
!![
-
+
!!]
return
end function bett2007ConstructorParameters
- function bett2007ConstructorInternal(lambda0,alpha,darkMatterProfileDMO_) result(self)
+ function bett2007ConstructorInternal(lambda0,alpha,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily bett2007} dark matter halo spin
distribution class.
@@ -114,12 +114,12 @@ function bett2007ConstructorInternal(lambda0,alpha,darkMatterProfileDMO_) result
use :: Table_Labels , only : extrapolationTypeFix
implicit none
type (haloSpinDistributionBett2007) :: self
- double precision , intent(in ) :: lambda0 , alpha
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- double precision :: spinDimensionless , tableMaximum
+ double precision , intent(in ) :: lambda0 , alpha
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ double precision :: spinDimensionless , tableMaximum
integer :: iSpin
!![
-
+
!!]
! Compute the normalization.
@@ -167,7 +167,7 @@ subroutine bett2007Destructor(self)
type(haloSpinDistributionBett2007), intent(inout) :: self
!![
-
+
!!]
call self%distributionTable %destroy()
if (allocated(self%distributionInverse)) call self%distributionInverse%destroy()
@@ -207,8 +207,8 @@ Compute the spin parameter distribution for the given {\normalfont \ttfamily nod
double precision :: spin_
spin => node%spin ()
- spin_ = +spin%angularMomentum() &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ spin_ = +spin%angularMomentum() &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
bett2007Distribution = +self%normalization &
& *( &
& +spin_ &
diff --git a/source/dark_matter_halos.spins.distributions.N-body_errors.F90 b/source/dark_matter_halos.spins.distributions.N-body_errors.F90
index de49964069..d52db56372 100644
--- a/source/dark_matter_halos.spins.distributions.N-body_errors.F90
+++ b/source/dark_matter_halos.spins.distributions.N-body_errors.F90
@@ -25,7 +25,6 @@
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profile_Scales , only : darkMatterProfileScaleRadius, darkMatterProfileScaleRadiusClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Halo_Mass_Functions , only : haloMassFunctionClass
use :: Root_Finder , only : rootFinder
use :: Statistics_NBody_Halo_Mass_Errors, only : nbodyHaloMassErrorClass
@@ -49,7 +48,6 @@
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
class (haloMassFunctionClass ), pointer :: haloMassFunction_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
type (rootFinder ) :: finder
double precision :: massParticle , time , &
@@ -113,7 +111,6 @@ function nbodyErrorsConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (haloMassFunctionClass ), pointer :: haloMassFunction_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_
double precision :: massParticle , redshift , &
& time , energyEstimateParticleCountMaximum, &
@@ -154,7 +151,6 @@ function nbodyErrorsConstructorParameters(parameters) result(self)
-
!!]
! Find the time corresponding to the given redshift.
@@ -175,7 +171,6 @@ function nbodyErrorsConstructorParameters(parameters) result(self)
& cosmologyFunctions_ , &
& haloMassFunction_ , &
& darkMatterHaloScale_ , &
- & darkMatterProfileDMO_ , &
& darkMatterProfileScaleRadius_ &
& )
!![
@@ -185,13 +180,12 @@ function nbodyErrorsConstructorParameters(parameters) result(self)
-
!!]
return
end function nbodyErrorsConstructorParameters
- function nbodyErrorsConstructorInternal(distributionIntrinsic,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,logNormalRange,time,nbodyHaloMassError_,cosmologyFunctions_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileDMO_,darkMatterProfileScaleRadius_) result(self)
+ function nbodyErrorsConstructorInternal(distributionIntrinsic,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,logNormalRange,time,nbodyHaloMassError_,cosmologyFunctions_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileScaleRadius_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily nbodyErrors} dark matter halo spin distribution class.
!!}
@@ -203,13 +197,12 @@ function nbodyErrorsConstructorInternal(distributionIntrinsic,massParticle,parti
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (haloMassFunctionClass ), intent(in ), target :: haloMassFunction_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterProfileScaleRadiusClass), intent(in ), target :: darkMatterProfileScaleRadius_
double precision , intent(in ) :: massParticle , time , &
& energyEstimateParticleCountMaximum, logNormalRange
integer , intent(in ) :: particleCountMinimum
!![
-
+
!!]
! Store the redshift.
@@ -456,57 +449,67 @@ double precision function massSpinIntegral(massIntrinsic)
!!{
Integral over the halo mass function, spin distribution, halo mass error distribution, and spin error distribution.
!!}
- use :: Display , only : displayMagenta , displayReset
- use :: Error , only : Error_Report , Warn , errorStatusFail, errorStatusSuccess
- use :: Input_Parameters , only : inputParameters
- use :: Numerical_Constants_Math, only : Pi
+ use :: Display , only : displayMagenta , displayReset
+ use :: Error , only : Error_Report , Warn , errorStatusFail, errorStatusSuccess
+ use :: Input_Parameters , only : inputParameters
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeDarkMatterOnly, massTypeDark
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
- double precision , intent(in ) :: massIntrinsic
- double precision , parameter :: rangeIntegration =1.0000d1 ! Range of integration in units of error.
- double precision , parameter :: radiusVelocityDispersionMeanOverSpinSpecificAngularMomentum=0.4175d0 ! Ratio of mean velocity dispersion-radius product to "spin" angular
- ! momentum (i.e. the normalizing angular momentum appearing in the definition of
- ! halo spin): γ = Mσⱼ/Jₛ, Jₛ = GM²˙⁵/|E⁰˙⁵|. This parameter corresponds to γ.
- integer :: errorStatus
- double precision :: logSpinMinimum, logSpinMaximum , &
- & errorMaximum , scaleAbsolute , &
- & tolerance , massSpinIntegralMass
- character (len=8 ) :: label , labelMass , &
- & labelSpin
- type (inputParameters) :: descriptor
+ double precision , intent(in ) :: massIntrinsic
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision , parameter :: rangeIntegration =1.0000d1 ! Range of integration in units of error.
+ double precision , parameter :: radiusVelocityDispersionMeanOverSpinSpecificAngularMomentum=0.4175d0 ! Ratio of mean velocity dispersion-radius product to "spin" angular
+ ! momentum (i.e. the normalizing angular momentum appearing in the definition of
+ ! halo spin): γ = Mσⱼ/Jₛ, Jₛ = GM²˙⁵/|E⁰˙⁵|. This parameter corresponds to γ.
+ integer :: errorStatus
+ double precision :: logSpinMinimum , logSpinMaximum , &
+ & errorMaximum , scaleAbsolute , &
+ & tolerance , massSpinIntegralMass
+ character (len=8 ) :: label , labelMass , &
+ & labelSpin
+ type (inputParameters ) :: descriptor
+ type (coordinateSpherical ) :: coordinatesHalo
! Evaluate the halo mass part of the integrand, unless evaluating the distribution at a fixed point.
if (self%fixedPoint) then
- massSpinIntegralMass =1.0d0
+ massSpinIntegralMass = 1.0d0
else
- massSpinIntegralMass =massIntegral(massIntrinsic)
+ massSpinIntegralMass = massIntegral(massIntrinsic)
end if
! Compute the particle number.
- particleNumber =massIntrinsic /self%massParticle
+ particleNumber = massIntrinsic /self%massParticle
! Evaluate the root-variance of the spin-independent error term which arises from the random walk in angular momentum
! space. Note that the root-variance that goes into non-central χ-square distribution is the width of the Gaussian for a
! single dimension, leading to a factor of √3 in the following.
- errorSpinIndependent =+radiusVelocityDispersionMeanOverSpinSpecificAngularMomentum &
- & /sqrt(particleNumber)
- errorSpinIndependent1D =+errorSpinIndependent &
- & /sqrt(3.0d0)
+ errorSpinIndependent = +radiusVelocityDispersionMeanOverSpinSpecificAngularMomentum &
+ & /sqrt(particleNumber)
+ errorSpinIndependent1D = +errorSpinIndependent &
+ & /sqrt(3.0d0)
! Get the outer radius of the halo.
- radiusHalo =+self%darkMatterHaloScale_ %radiusVirial(node )
+ radiusHalo = +self%darkMatterHaloScale_ %radiusVirial(node)
+ coordinatesHalo = [radiusHalo,0.0d0,0.0d0]
! Get the density at the edge of the halo.
- densityOuterRadius =+self%darkMatterProfileDMO_%density (node,radiusHalo)
+ massDistribution_ => node %massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ densityOuterRadius = +massDistribution_%density (coordinatesHalo )
+ !![
+
+ !!]
! Find the ratio of the mean interior density in the halo to the density at the halo outer radius.
- densityRatioInternalToSurface=+3.0d0 &
- & *massIntrinsic &
- & /4.0d0 &
- & /Pi &
- & /radiusHalo **3 &
- & /densityOuterRadius
+ densityRatioInternalToSurface = +3.0d0 &
+ & *massIntrinsic &
+ & /4.0d0 &
+ & /Pi &
+ & /radiusHalo **3 &
+ & /densityOuterRadius
! Evaluate the distribution.
if (self%fixedPoint) then
massSpinIntegral=spinErrorIntegral(spinFixed)
else
! Evaluate an estimate of the absolute scale of the spin distribution for use in setting an absolute precision level on the
! integration.
- call nodeSpin%angularMomentumSet(spinMeasured*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_))
+ call nodeSpin%angularMomentumSet(spinMeasured*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_))
call Calculations_Reset(node)
scaleAbsolute=self%distributionIntrinsic%distribution(node)
! Evaluate the integral over the spin distribution. We integrate from ±ασ around the measured spin, with σ being the larger
@@ -578,7 +581,7 @@ double precision function spinIntegral(logSpinIntrinsic)
! Compute intrinsic spin.
spinIntrinsic=exp(logSpinIntrinsic)
! Set the intrinsic spin.
- call nodeSpin%angularMomentumSet(spinIntrinsic*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_))
+ call nodeSpin%angularMomentumSet(spinIntrinsic*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_))
call Calculations_Reset(node)
! Compute the integrand.
spinIntegral=+self%distributionIntrinsic%distribution(node ) & ! Weight by the intrinsic spin distribution.
@@ -795,7 +798,6 @@ subroutine nbodyErrorsDestructor(self)
-
!!]
return
@@ -836,8 +838,8 @@ double precision function nbodyErrorsDistribution(self,node)
nodeBasic => node %basic ()
nodeSpin => node %spin ()
mass = nodeBasic%mass ()
- spin = +nodeSpin %angularMomentum() &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ spin = +nodeSpin %angularMomentum() &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
! Ensure the table has sufficient extent.
call self%tabulate(massRequired=mass,spinRequired=spin,tree=node%hostTree)
! Find the interpolating factors.
@@ -878,8 +880,8 @@ double precision function nbodyErrorsDistributionFixedPoint(self,node,spinMeasur
nodeBasic => node %basic ()
nodeSpin => node %spin ()
mass = nodeBasic%mass ()
- spin = +nodeSpin %angularMomentum() &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ spin = +nodeSpin %angularMomentum() &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
! Ensure the table has sufficient extent.
call self%tabulate(massFixed=mass,spinFixed=spin,spinFixedMeasuredMinimum=spinMeasuredMinimum,spinFixedMeasuredMaximum=spinMeasuredMaximum,tree=node%hostTree)
! Find the interpolating factors.
diff --git a/source/dark_matter_halos.spins.distributions.lognormal.F90 b/source/dark_matter_halos.spins.distributions.lognormal.F90
index 01e84c75ac..ea7b84020f 100644
--- a/source/dark_matter_halos.spins.distributions.lognormal.F90
+++ b/source/dark_matter_halos.spins.distributions.lognormal.F90
@@ -22,7 +22,7 @@
log-normal distribution.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMO, darkMatterProfileDMOClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -38,8 +38,8 @@
log-normal distribution.
!!}
private
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- double precision :: median , sigma
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ double precision :: median , sigma
contains
final :: logNormalDestructor
procedure :: sample => logNormalSample
@@ -66,8 +66,8 @@ function logNormalConstructorParameters(parameters) result(self)
implicit none
type (haloSpinDistributionLogNormal) :: self
type (inputParameters ), intent(inout) :: parameters
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- double precision :: median , sigma
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ double precision :: median , sigma
! Check and read parameters.
!![
@@ -87,27 +87,27 @@ function logNormalConstructorParameters(parameters) result(self)
(\citealt{bett_spin_2007}; note that in this reference the value of $\sigma$ quoted is for $\log_{10}\lambda$, while here we use $\log\lambda$)
The width of a log-normal spin distribution.
-
+
!!]
- self=haloSpinDistributionLogNormal(median,sigma,darkMatterProfileDMO_)
+ self=haloSpinDistributionLogNormal(median,sigma,darkMatterHaloScale_)
!![
-
+
!!]
return
end function logNormalConstructorParameters
- function logNormalConstructorInternal(median,sigma,darkMatterProfileDMO_) result(self)
+ function logNormalConstructorInternal(median,sigma,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily logNormal} dark matter halo spin
distribution class.
!!}
implicit none
type (haloSpinDistributionLogNormal) :: self
- double precision , intent(in ) :: median , sigma
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ double precision , intent(in ) :: median , sigma
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
return
@@ -122,7 +122,7 @@ subroutine lognormalDestructor(self)
type(haloSpinDistributionLognormal), intent(inout) :: self
!![
-
+
!!]
return
end subroutine lognormalDestructor
@@ -160,8 +160,8 @@ double precision function logNormalDistribution(self,node)
double precision :: spin_
spin => node%spin ()
- spin_ = +spin%angularMomentum() &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ spin_ = +spin%angularMomentum() &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
logNormalDistribution = +exp( &
& -( &
& +log(spin_) &
diff --git a/source/dark_matter_profiles.F90 b/source/dark_matter_profiles.F90
index 23ba4184b9..773406e13f 100644
--- a/source/dark_matter_profiles.F90
+++ b/source/dark_matter_profiles.F90
@@ -25,185 +25,26 @@ module Dark_Matter_Profiles
!!{
Provides an object that implements non-dark-matter-only dark matter halo profiles.
!!}
- use :: Dark_Matter_Profiles_Generic, only : darkMatterProfileGeneric
- use :: Galacticus_Nodes , only : treeNode
- use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType
- use :: Kind_Numbers , only : kind_int8
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : enumerationStructureErrorCodeType, enumerationWeightByType
+ use :: Kind_Numbers , only : kind_int8
private
!![
darkMatterProfile
- darkMatterProfileGeneric
Dark Matter Halo Profiles
Object providing dark matter halo profiles.
adiabaticGnedin2004
-
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the logarithmic slope of the density profile in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the {\normalfont \ttfamily m}$^\mathrm{th}$ radial moment of the dark matter profile of {\normalfont \ttfamily node} optionally between the given {\normalfont \ttfamily radiusMinimum} and {\normalfont \ttfamily radiusMaximum} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
-
- Return the total energy for the given {\normalfont \ttfamily node} in units of $M_\odot$ km$^2$ s$^{-2}$.
- double precision
- yes
- type(treeNode), intent(inout) :: node
-
-
- Returns the relation between specific angular momentum and rotation velocity (assuming a rotation velocity that is constant in radius) for the given {\normalfont \ttfamily node}. Specifically, the normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} at which the specific angular momentum of a circular orbit equals {\normalfont \ttfamily specificAngularMomentum} (specified in units of km s$^{-1}$ Mpc.
- double precision
- yes
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
-
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the gravitational potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- yes
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
-
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the radius (in Mpc) enclosing a given density threshold (in $M_\odot \hbox{Mpc}^{-3}$) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: density
-
-
- Returns the normalized Fourier space density profile of the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily waveNumber} (given in units of Mpc$^{-1}$).
- double precision
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: wavenumber
-
-
- Returns the freefall radius (in Mpc) corresponding to the given {\normalfont \ttfamily time} (in Gyr) in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: time
-
-
- Returns the rate of increase of the freefall radius (in Mpc/Gyr) corresponding to the given {\normalfont \ttfamily time} (in Gyr) in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: time
-
-
- Returns the radius (in Mpc) enclosing a given mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
+
+ Return the mass distribution of the dark matter profile.
+ class(massDistributionClass)
yes
yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: mass
- Root_Finder Kind_Numbers
-
- double precision :: radiusGuess
- type (rootFinder), save :: finder
- logical , save :: finderConstructed=.false.
- double precision , save :: radiusPrevious =-huge(0.0d0)
- integer (kind_int8 ), save :: uniqueIDPrevious =-1_kind_int8
- !$omp threadprivate(finder,finderConstructed,radiusPrevious,uniqueIDPrevious)
- if(mass <= 0) then
- darkMatterProfileRadiusEnclosingMass=0.0d0
- return
- end if
- ! Initialize the root finder.
- if (.not.finderConstructed) then
- finder=rootFinder( &
- & rootFunction =enclosedMassRoot , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandType =rangeExpandMultiplicative , &
- & toleranceAbsolute =0.0d+0 , &
- & toleranceRelative =1.0d-6 &
- & )
- finderConstructed=.true.
- end if
- if (node%uniqueID() == uniqueIDPrevious) then
- radiusGuess =radiusPrevious
- else
- radiusGuess =self%darkMatterHaloScale_%radiusVirial(node)
- uniqueIDPrevious=node %uniqueID ( )
- end if
- radiusPrevious=finder%find(rootGuess=radiusGuess)
- darkMatterProfileRadiusEnclosingMass=radiusPrevious
- return
- contains
- double precision function enclosedMassRoot(radius)
- !!{
- Root function used in solving for the radius that encloses a given mass.
- !!}
- implicit none
- double precision, intent(in) :: radius
- enclosedMassRoot=self%enclosedMass(node,radius)-mass
- return
- end function enclosedMassRoot
-
+ type (treeNode ), intent(inout), target :: node
+ type (enumerationWeightByType), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
!!]
diff --git a/source/dark_matter_profiles.SIDM.F90 b/source/dark_matter_profiles.SIDM.F90
deleted file mode 100644
index 2fea326714..0000000000
--- a/source/dark_matter_profiles.SIDM.F90
+++ /dev/null
@@ -1,141 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An abstract dark matter halo profile class for SIDM profiles.
- !!}
-
- use :: Dark_Matter_Particles, only : darkMatterParticleClass
-
- !![
-
-
- Abstract dark matter halo profile for self-interacting dark matter particles. Provides a method to compute the
- characteristic radius for interactions, $r_1$, defined as (e.g. Jiang et al. 2020):
- \begin{equation}
- \frac{4}{\sqrt{\pi}} \rho_\mathrm{dm}(r_1) v_\mathrm{rms}(r_1) \frac{\sigma}{m} = \frac{1}{t_\mathrm{age}},
- \end{equation}
- where the left-hand side is the scattering rate per particle, with $\rho_\mathrm{dm}(r)$ being the dark matter density
- profile, $v_\mathrm{rms}$ the average relative velocity between DM particles (which is approximated by the 1D velocity
- dispersion), and $\sigma/m$ is the self-interaction cross-section per unit mass.
-
-
- !!]
- type, abstract, extends(darkMatterProfileClass) :: darkMatterProfileSIDM
- !!{
- An abstract dark matter halo profile class for SIDM profiles.
- !!}
- private
- class (darkMatterProfileClass ), pointer :: darkMatterProfile_ => null()
- class (darkMatterParticleClass), pointer :: darkMatterParticle_ => null()
- integer (kind=kind_int8 ) :: uniqueIDPreviousSIDM
- double precision :: radiusInteractivePrevious
- contains
- !![
-
-
-
- !!]
- procedure :: radiusInteraction => sidmRadiusInteraction
- end type darkMatterProfileSIDM
-
- ! Submodule-scope variables used in root finding.
- class (darkMatterProfileSIDM), pointer :: self_
- type (treeNode ), pointer :: node_
- double precision :: timeAge_, crossSection_
- !$omp threadprivate(self_,node_,timeAge_,crossSection_)
-
-contains
-
- double precision function sidmRadiusInteraction(self,node,timeAge)
- !!{
- Returns the characteristic interaction radius (in Mpc) of the self-interacting dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeComponentBasic
- use :: Numerical_Constants_Prefixes , only : centi , kilo
- use :: Numerical_Constants_Astronomical, only : megaParsec , massSolar
- use :: Root_Finder , only : rootFinder , rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- implicit none
- class (darkMatterProfileSIDM), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ), optional :: timeAge
- class (nodeComponentBasic ) , pointer :: basic
- type (rootFinder ) :: finder
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
-
- if (node%uniqueID() /= self%uniqueIDPreviousSIDM .or. self%radiusInteractivePrevious < 0.0d0) then
- self_ => self
- node_ => node
- if (present(timeAge)) then
- timeAge_ = timeAge
- else
- basic => node %basic ()
- timeAge_ = basic%time ()
- end if
- select type (darkMatterParticle_ => self%darkMatterParticle_)
- class is (darkMatterParticleSelfInteractingDarkMatter)
- crossSection_=+darkMatterParticle_%crossSectionSelfInteraction() &
- & *centi **2 &
- & /megaParsec**2 &
- & *kilo &
- & *massSolar
- class default
- call Error_Report('expected self-interacting dark matter particle'//{introspection:location})
- end select
- finder=rootFinder( &
- & rootFunction =sidmRadiusInteractionRoot, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
- call finder%rangeExpand( &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- self%radiusInteractivePrevious=finder%find (rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- self%uniqueIDPreviousSIDM =node %uniqueID( )
- end if
- sidmRadiusInteraction=self%radiusInteractivePrevious
- return
- end function sidmRadiusInteraction
-
- double precision function sidmRadiusInteractionRoot(radius)
- !!{
- Root function used in seeking the characteristic interaction radius in self-interacting dark matter profiles.
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- sidmRadiusInteractionRoot=+4.0d0 &
- & /sqrt(Pi) &
- & /Mpc_per_km_per_s_To_Gyr &
- & *self_%darkMatterProfile_%density (node_,radius) &
- & *self_%darkMatterProfile_%radialVelocityDispersion(node_,radius) &
- & *crossSection_ &
- & -1.0d0 &
- & /timeAge_
- return
- end function sidmRadiusInteractionRoot
-
diff --git a/source/dark_matter_profiles.SIDM.isothermal.F90 b/source/dark_matter_profiles.SIDM.isothermal.F90
index bb2478e5ed..fc0401e5ad 100644
--- a/source/dark_matter_profiles.SIDM.isothermal.F90
+++ b/source/dark_matter_profiles.SIDM.isothermal.F90
@@ -22,69 +22,26 @@
al. (2022), including the effects of a baryonic potential.
!!}
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Interpolation, only : interpolator
- use :: Numerical_ODE_Solvers , only : odeSolver
-
+ use :: Dark_Matter_Particles, only : darkMatterParticleClass
+
!![
- Dark matter halo profiles for self-interacting dark matter following the ``isothermal'' model of Jiang et al. (2022). This
- model assumes that the dark matter within the interaction radius, $r_1$, has thermalized and can therefore be described by a
- constant velocity dispersion, $\sigma_0$. Under this assumption the spherical Jeans equation has a solution of the form:
- \begin{equation}
- \rho(r) = \rho_0 \exp\left[-\frac{\phi(r)}{\sigma_0^2}\right],
- \end{equation}
- where $\rho(r)$ is the density $\rho_0$ is the density at $r=0$, and the gravitational potential satisfies (Jiang et al. 2022):
- \begin{equation}
- \nabla^2 \phi(r) = 4 \pi \mathrm{G} \left[ \rho_0 \exp \left( - \frac{\phi(r)}{\sigma_0^2} \right) + \rho_\mathrm{b}(r) \right],
- \end{equation}
- where $\rho_\mathrm{b}(r)$ is the density of the baryonic component. This second-order differential equation is solved using the boundary conditions $\phi(r=0)=0$ and
- $\mathrm{d}\phi/\mathrm{d}r(r=0)=0$. The values of $\rho_0$ and $\sigma_0$ are then found by minimizing a function
- \begin{equation}
- \delta^2(\rho_0,\sigma_0) = \left[ \frac{\rho(r_1)}{\rho^\prime(r_1)} - 1 \right]^2 + \left[ \frac{M(r_1)}{M^\prime(r_1)} - 1 \right]^2,
- \end{equation}
- where $M(r)$ is the mass contained within radius $r$, and primes indicate the profile prior to SIDM thermalization.
+ A dark matter halo profile class that builds \refClass{massDistributionSphericalSIDMIsothermalBaryons} objects for
+ isothermal SIDM profiles containing baryons.
!!]
- type, extends(darkMatterProfileSIDM) :: darkMatterProfileSIDMIsothermal
+ type, extends(darkMatterProfileClass) :: darkMatterProfileSIDMIsothermal
!!{
A dark matter halo profile class implementing profiles for self-interacting dark matter following the ``isothermal'' model of Jiang et al. (2022).
!!}
private
- integer (kind=kind_int8) :: uniqueIDPrevious
- double precision :: velocityDispersionCentral
- logical :: solutionsTabulated
- class (* ), pointer :: galacticStructure_ => null()
- type (interpolator ), allocatable :: densityProfile , massProfile
+ class(darkMatterProfileClass ), pointer :: darkMatterProfile_ => null()
+ class(darkMatterParticleClass), pointer :: darkMatterParticle_ => null()
contains
- !![
-
-
-
-
- !!]
- final :: sidmIsothermalDestructor
- procedure :: autoHook => sidmIsothermalAutoHook
- procedure :: calculationReset => sidmIsothermalCalculationReset
- procedure :: density => sidmIsothermalDensity
- procedure :: densityLogSlope => sidmIsothermalDensityLogSlope
- procedure :: radiusEnclosingDensity => sidmIsothermalRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => sidmIsothermalRadiusEnclosingMass
- procedure :: radialMoment => sidmIsothermalRadialMoment
- procedure :: enclosedMass => sidmIsothermalEnclosedMass
- procedure :: potential => sidmIsothermalPotential
- procedure :: circularVelocity => sidmIsothermalCircularVelocity
- procedure :: circularVelocityMaximum => sidmIsothermalCircularVelocityMaximum
- procedure :: radialVelocityDispersion => sidmIsothermalRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => sidmIsothermalRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => sidmIsothermalRotationNormalization
- procedure :: energy => sidmIsothermalEnergy
- procedure :: kSpace => sidmIsothermalKSpace
- procedure :: freefallRadius => sidmIsothermalFreefallRadius
- procedure :: freefallRadiusIncreaseRate => sidmIsothermalFreefallRadiusIncreaseRate
- procedure :: computeSolution => sidmIsothermalComputeSolution
+ final :: sidmIsothermalDestructor
+ procedure :: get => sidmIsothermalGet
end type darkMatterProfileSIDMIsothermal
interface darkMatterProfileSIDMIsothermal
@@ -101,46 +58,37 @@ function sidmIsothermalConstructorParameters(parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily sidmIsothermal} dark matter halo profile class which takes a parameter set as input.
!!}
- use :: Functions_Global, only : galacticStructureConstruct_, galacticStructureDestruct_
- use :: Input_Parameters, only : inputParameter , inputParameters
+ use :: Input_Parameters, only : inputParameters
implicit none
type (darkMatterProfileSIDMIsothermal) :: self
type (inputParameters ), intent(inout) :: parameters
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(darkMatterParticleClass ), pointer :: darkMatterParticle_
class(darkMatterProfileClass ), pointer :: darkMatterProfile_
- class(* ), pointer :: galacticStructure_
!![
-
-
-
+
+
!!]
- call galacticStructureConstruct_(parameters,galacticStructure_)
- self=darkMatterProfileSIDMIsothermal(darkMatterProfile_,darkMatterHaloScale_,darkMatterParticle_,galacticStructure_)
+ self=darkMatterProfileSIDMIsothermal(darkMatterProfile_,darkMatterParticle_)
!![
-
-
-
-
+
+
+
!!]
- call galacticStructureDestruct_(self%galacticStructure_)
return
end function sidmIsothermalConstructorParameters
- function sidmIsothermalConstructorInternal(darkMatterProfile_,darkMatterHaloScale_,darkMatterParticle_,galacticStructure_) result(self)
+ function sidmIsothermalConstructorInternal(darkMatterProfile_,darkMatterParticle_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily sidmIsothermal} dark matter profile class.
!!}
use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
implicit none
type (darkMatterProfileSIDMIsothermal) :: self
- class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class(darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
class(darkMatterProfileClass ), intent(in ), target :: darkMatterProfile_
- class(* ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
! Validate the dark matter particle type.
@@ -150,470 +98,122 @@ function sidmIsothermalConstructorInternal(darkMatterProfile_,darkMatterHaloScal
class default
call Error_Report('SIDM isothermal dark matter profile expects a self-interacting dark matter particle'//{introspection:location})
end select
- self%uniqueIDPrevious =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%uniqueIDPreviousSIDM=-1_kind_int8
return
end function sidmIsothermalConstructorInternal
- subroutine sidmIsothermalAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileSIDMIsothermal), intent(inout) :: self
-
- call calculationResetEvent%attach(self,sidmIsothermalCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileSIDMIsothermal')
- return
- end subroutine sidmIsothermalAutoHook
-
subroutine sidmIsothermalDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily sidmIsothermal} dark matter halo profile class.
!!}
- use :: Functions_Global, only : galacticStructureDestruct_
- use :: Events_Hooks , only : calculationResetEvent
implicit none
type(darkMatterProfileSIDMIsothermal), intent(inout) :: self
!![
-
-
-
+
+
!!]
- if (associated(self%galacticStructure_)) call galacticStructureDestruct_(self%galacticStructure_)
- if (calculationResetEvent%isAttached(self,sidmIsothermalCalculationReset)) call calculationResetEvent%detach(self,sidmIsothermalCalculationReset)
return
end subroutine sidmIsothermalDestructor
- subroutine sidmIsothermalCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%uniqueIDPrevious =uniqueID
- self%genericLastUniqueID =uniqueID
- self%uniqueIDPreviousSIDM =uniqueID
- self%radiusInteractivePrevious =-1.0d0
- self%velocityDispersionCentral =-1.0d0
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%densityProfile )) deallocate(self%densityProfile )
- if (allocated(self%massProfile )) deallocate(self%massProfile )
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine sidmIsothermalCalculationReset
-
- subroutine sidmIsothermalComputeSolution(self,node)
- !!{
- Compute a solution for the isothermal core of an SIDM halo.
- !!}
- use :: Numerical_ODE_Solvers , only : odeSolver
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Multidimensional_Minimizer , only : multiDMinimizer
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer (c_size_t ), parameter :: propertyCount =2
- integer , parameter :: countTable =1000
- double precision , parameter :: odeToleranceAbsolute =1.0d-3, odeToleranceRelative =1.0d-3
- double precision , parameter :: fractionRadiusInitial =1.0d-6
- double precision , dimension(propertyCount+1) :: properties , propertyScales
- double precision , dimension(countTable ) :: radiusTable , densityTable , &
- & massTable
- double precision , dimension(propertyCount ) :: locationMinimum
- type (odeSolver ) :: odeSolver_
- type (multiDMinimizer ) :: minimizer_
- integer :: i , iteration
- logical :: converged
- double precision :: densityCentral , velocityDispersionCentral , &
- & densityInteraction , massInteraction , &
- & radiusInteraction , radius , &
- & velocityDispersionInteraction , mass , &
- & density
-
- ! Find the interaction radius.
- radiusInteraction =self%radiusInteraction (node )
- ! Properties of the original density profile at the interaction radius.
- densityInteraction =self%darkMatterProfile_%density (node,radiusInteraction)
- massInteraction =self%darkMatterProfile_%enclosedMass (node,radiusInteraction)
- ! Find the velocity dispersion scale.
- velocityDispersionInteraction=sqrt(gravitationalConstantGalacticus*massInteraction/radiusInteraction)
- ! Set ODE solver scales.
- propertyScales =[velocityDispersionInteraction**2,velocityDispersionInteraction**2/radiusInteraction,massInteraction]
- ! Construct an ODE solver.
- odeSolver_ =odeSolver (propertyCount+1,sidmIsothermalODEs ,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative,scale=propertyScales)
- ! Construct a minimizer.
- minimizer_ =multiDMinimizer(propertyCount ,sidmIsothermalFitMetric )
- ! Seek the solution.
- call minimizer_%set(x=[0.0d0,1.0d0],stepSize=[1.0d0,1.0d0])
- iteration=0
- converged=.false.
- do while (.not.converged .and. iteration < 100)
- call minimizer_%iterate()
- iteration=iteration+1
- converged=minimizer_%testSize(toleranceAbsolute=1.0d-3)
- end do
- locationMinimum =minimizer_%x()
- densityCentral =exp(locationMinimum(1))*densityInteraction
- velocityDispersionCentral= locationMinimum(2) *velocityDispersionInteraction
- ! Tabulate solutions for density and mass.
- radiusTable=Make_Range(rangeMinimum=0.0d0,rangeMaximum=radiusInteraction,rangeNumber=countTable,rangeType=rangeTypeLinear)
- densityTable(1)=densityCentral
- massTable (1)=0.0d0
- do i=2,countTable
- radius =fractionRadiusInitial*radiusInteraction
- properties=0.0d0
- call odeSolver_%solve(radius,radiusTable(i),properties)
- densityTable(i)=+densityCentral &
- & *exp( &
- & -properties(1) &
- & /velocityDispersionCentral**2 &
- & )
- massTable (i)=+ properties(3)
- end do
- allocate(self%densityProfile)
- allocate(self% massProfile)
- self% densityProfile=interpolator(radiusTable, densityTable)
- self% massProfile=interpolator(radiusTable, massTable)
- self%velocityDispersionCentral= velocityDispersionCentral
- return
-
- contains
-
- double precision function sidmIsothermalFitMetric(propertiesCentral)
- !!{
- Evaluate the fit metric.
- !!}
- implicit none
- double precision, intent(in ), dimension(:) :: propertiesCentral
- double precision , dimension(propertyCount+1) :: properties
- double precision :: radius
-
- ! Extract current parameters.
- densityCentral =exp(propertiesCentral(1))*densityInteraction
- velocityDispersionCentral= propertiesCentral(2) *velocityDispersionInteraction
- ! Solve the ODE to r₁.
- radius =fractionRadiusInitial*radiusInteraction
- properties=0.0d0
- call odeSolver_%solve(radius,radiusInteraction,properties)
- ! Extract density and mass at r₁.
- density=+densityCentral &
- & *exp( &
- & -properties(1) &
- & /velocityDispersionCentral**2 &
- & )
- mass =+ properties(3)
- ! Evaluate the fit metric.
- sidmIsothermalFitMetric=+(density/densityInteraction-1.0d0)**2 &
- & +( mass/ massInteraction-1.0d0)**2
- return
- end function sidmIsothermalFitMetric
-
- integer function sidmIsothermalODEs(radius,properties,propertiesRateOfChange)
- !!{
- Define the ODE system to solve for isothermal self-interacting dark matter cores.
- !!}
- use :: Functions_Global , only : galacticStructureDensity_
- use :: Galactic_Structure_Options , only : massTypeBaryonic
- use :: Interface_GSL , only : GSL_Success
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- double precision, intent(in ) :: radius
- double precision, intent(in ), dimension(:) :: properties
- double precision, intent( out), dimension(:) :: propertiesRateOfChange
- double precision :: densityDarkMatter , densityBaryons
-
- densityDarkMatter =+densityCentral &
- & *exp( &
- & -max(properties(1),0.0d0) &
- & /velocityDispersionCentral**2 &
- & )
- densityBaryons =+galacticStructureDensity_(self%galacticStructure_,node,position=[radius,0.0d0,0.0d0],massType=massTypeBaryonic)
- propertiesRateOfChange (1)=+properties(2)
- propertiesRateOfChange (2)=+4.0d0 &
- & *Pi &
- & *gravitationalConstantGalacticus &
- & *( &
- & +densityDarkMatter &
- & +densityBaryons &
- & )
- if (radius > 0.0d0) &
- & propertiesRateOfChange(2)=+propertiesRateOfChange(2) &
- & -2.0d0 &
- & *properties (2) &
- & /radius
- propertiesRateOfChange (3)=+4.0d0 &
- & *Pi &
- & *radius**2 &
- & *densityDarkMatter
- sidmIsothermalODEs = GSL_Success
- return
- end function sidmIsothermalODEs
-
- end subroutine sidmIsothermalComputeSolution
-
- double precision function sidmIsothermalDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalDensity=self%darkMatterProfile_%density(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalDensity=self%densityProfile%interpolate(radius)
- end if
- return
- end function sidmIsothermalDensity
-
- double precision function sidmIsothermalDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalDensityLogSlope=self%darkMatterProfile_%densityLogSlope(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalDensityLogSlope=self%densityProfile%derivative(radius)*radius/self%densityProfile%interpolate(radius)
- end if
- return
- end function sidmIsothermalDensityLogSlope
-
- double precision function sidmIsothermalEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalEnclosedMass=self%darkMatterProfile_%enclosedMass(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalEnclosedMass=self%massProfile%interpolate(radius)
- end if
- return
- end function sidmIsothermalEnclosedMass
-
- double precision function sidmIsothermalRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- sidmIsothermalRadiusEnclosingDensity=self%radiusEnclosingDensityNumerical(node,density)
- return
- end function sidmIsothermalRadiusEnclosingDensity
-
- double precision function sidmIsothermalRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- sidmIsothermalRadiusEnclosingMass=self%radiusEnclosingMassNumerical(node,mass)
- return
- end function sidmIsothermalRadiusEnclosingMass
-
- double precision function sidmIsothermalRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- sidmIsothermalRadialMoment=self%radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- return
- end function sidmIsothermalRadialMoment
-
- double precision function sidmIsothermalPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalPotential=self%darkMatterProfile_%potential(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalPotential=self%darkMatterProfile_%potential(node,self%radiusInteraction(node))-self%velocityDispersionCentral**2*log(self%densityProfile%interpolate(radius)/self%densityProfile%interpolate(self%radiusInteraction(node)))
- end if
- return
- end function sidmIsothermalPotential
-
- double precision function sidmIsothermalCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- sidmIsothermalCircularVelocity=self%circularVelocityNumerical(node,radius)
- return
- end function sidmIsothermalCircularVelocity
-
- double precision function sidmIsothermalCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalCircularVelocityMaximum=self%circularVelocityMaximumNumerical(node)
- return
- end function sidmIsothermalCircularVelocityMaximum
-
- double precision function sidmIsothermalRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalRadialVelocityDispersion=self%darkMatterProfile_%radialVelocityDispersion(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalRadialVelocityDispersion=self%velocityDispersionCentral
- end if
- return
- end function sidmIsothermalRadialVelocityDispersion
-
- double precision function sidmIsothermalRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- sidmIsothermalRadiusFromSpecificAngularMomentum=self%radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- return
- end function sidmIsothermalRadiusFromSpecificAngularMomentum
-
- double precision function sidmIsothermalRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalRotationNormalization=self%rotationNormalizationNumerical(node)
- return
- end function sidmIsothermalRotationNormalization
-
- double precision function sidmIsothermalEnergy(self,node)
- !!{
- Return the energy of a sidmIsothermal halo density profile.
- !!}
- implicit none
- class(darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalEnergy=self%energyNumerical(node)
- return
- end function sidmIsothermalEnergy
-
- double precision function sidmIsothermalKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the sidmIsothermal density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- sidmIsothermalKSpace=self%kSpaceNumerical(node,waveNumber)
- return
- end function sidmIsothermalKSpace
-
- double precision function sidmIsothermalFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the sidmIsothermal density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
+ function sidmIsothermalGet(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalSIDMIsothermalBaryons, kinematicsDistributionSIDMIsothermal, nonAnalyticSolversNumerical, massDistributionSpherical, &
+ & sphericalSIDMIsothermalBaryonsInitializor
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionSIDMIsothermal ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileSIDMIsothermal ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionBaryonic, massDistributionDecorated
+ class (nodeComponentBasic ), pointer :: basic
+ procedure(sphericalSIDMIsothermalBaryonsInitializor), pointer :: initializationFunction
+ class (* ), pointer :: initializationSelf , initializationArgument
+ !![
+
+ !!]
- sidmIsothermalFreefallRadius=self%freefallRadiusNumerical(node,time)
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalSIDMIsothermalBaryons :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalSIDMIsothermalBaryons)
+ massDistributionDecorated => self%darkMatterProfile_%get (node,weightBy,weightIndex)
+ massDistributionBaryonic => null ( )
+ basic => node %basic( )
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ initializationFunction => sidmIsothermalInitialize
+ initializationSelf => self
+ initializationArgument => node
+ !![
+
+
+ massDistributionSphericalSIDMIsothermalBaryons( &
+ & timeAge =basic%time (), &
+ & nonAnalyticSolver = nonAnalyticSolversNumerical , &
+ & massDistribution_ = massDistributionDecorated , &
+ & massDistributionBaryonic= massDistributionBaryonic , &
+ & darkMatterParticle_ =self %darkMatterParticle_ , &
+ & initializationFunction = initializationFunction , &
+ & initializationSelf = initializationSelf , &
+ & initializationArgument = initializationArgument , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionSIDMIsothermal( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function sidmIsothermalFreefallRadius
+ end function sidmIsothermalGet
- double precision function sidmIsothermalFreefallRadiusIncreaseRate(self,node,time)
+ subroutine sidmIsothermalInitialize(self,node,massDistributionBaryonic)
!!{
- Returns the rate of increase of the freefall radius in the sidmIsothermal density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
+ Initialize the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : massTypeBaryonic
implicit none
- class (darkMatterProfileSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
+ class(* ), intent(inout), target :: self , node
+ class(massDistributionClass), intent( out), pointer :: massDistributionBaryonic
- sidmIsothermalFreefallRadiusIncreaseRate=self%freefallRadiusIncreaseRateNumerical(node,time)
+ select type (self)
+ type is (darkMatterProfileSIDMIsothermal)
+ select type (node)
+ type is (treeNode)
+ massDistributionBaryonic => node%massDistribution(massType=massTypeBaryonic)
+ class default
+ call Error_Report('unexpected class'//{introspection:location})
+ end select
+ class default
+ call Error_Report('unexpected class'//{introspection:location})
+ end select
return
- end function sidmIsothermalFreefallRadiusIncreaseRate
+ end subroutine sidmIsothermalInitialize
diff --git a/source/dark_matter_profiles.accelerator.F90 b/source/dark_matter_profiles.accelerator.F90
index 4b1a19a3f5..b8509c42aa 100644
--- a/source/dark_matter_profiles.accelerator.F90
+++ b/source/dark_matter_profiles.accelerator.F90
@@ -20,10 +20,7 @@
!!{
An accelerator class for non-dark-matter-only dark matter halo profiles.
!!}
-
- use :: Binary_Search_Trees, only : binaryTree
- use :: Kind_Numbers , only : kind_int8
-
+
!![
An accelerator class for non-dark-matter-only dark matter halo profiles.
@@ -34,37 +31,11 @@
An accelerator class for non-dark-matter-only dark matter halo profiles.
!!}
private
- integer (kind_int8 ), dimension(2) :: uniqueIDPrevious
- class (darkMatterProfileClass), pointer :: darkMatterProfile_ => null()
- type (binaryTree ), dimension(2) :: treeMassEnclosed
- integer :: treePrevious
- double precision :: toleranceRelative , factorRadiusMaximum, &
- & factorRadiusLogarithmicMaximum
+ class (darkMatterProfileClass), pointer :: darkMatterProfile_ => null()
+ double precision :: toleranceRelative , factorRadiusMaximum
contains
- !![
-
-
-
- !!]
- final :: acceleratorDestructor
- procedure :: autoHook => acceleratorAutoHook
- procedure :: calculationReset => acceleratorCalculationReset
- procedure :: density => acceleratorDensity
- procedure :: densityLogSlope => acceleratorDensityLogSlope
- procedure :: radiusEnclosingDensity => acceleratorRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => acceleratorRadiusEnclosingMass
- procedure :: radialMoment => acceleratorRadialMoment
- procedure :: enclosedMass => acceleratorEnclosedMass
- procedure :: potential => acceleratorPotential
- procedure :: circularVelocity => acceleratorCircularVelocity
- procedure :: circularVelocityMaximum => acceleratorCircularVelocityMaximum
- procedure :: radialVelocityDispersion => acceleratorRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => acceleratorRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => acceleratorRotationNormalization
- procedure :: energy => acceleratorEnergy
- procedure :: kSpace => acceleratorKSpace
- procedure :: freefallRadius => acceleratorFreefallRadius
- procedure :: freefallRadiusIncreaseRate => acceleratorFreefallRadiusIncreaseRate
+ final :: acceleratorDestructor
+ procedure :: get => acceleratorGet
end type darkMatterProfileAccelerator
interface darkMatterProfileAccelerator
@@ -87,8 +58,7 @@ function acceleratorConstructorParameters(parameters) result(self)
type (darkMatterProfileAccelerator) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileClass ), pointer :: darkMatterProfile_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- double precision :: toleranceRelative , factorRadiusMaximum
+ double precision :: toleranceRelative , factorRadiusMaximum
!![
@@ -103,358 +73,104 @@ function acceleratorConstructorParameters(parameters) result(self)
The maximum factor by which to interpolate in radius.
-
-
+
!!]
- self=darkMatterProfileAccelerator(toleranceRelative,factorRadiusMaximum,darkMatterHaloScale_,darkMatterProfile_)
+ self=darkMatterProfileAccelerator(toleranceRelative,factorRadiusMaximum,darkMatterProfile_)
!![
-
-
+
!!]
return
end function acceleratorConstructorParameters
- function acceleratorConstructorInternal(toleranceRelative,factorRadiusMaximum,darkMatterHaloScale_,darkMatterProfile_) result(self)
+ function acceleratorConstructorInternal(toleranceRelative,factorRadiusMaximum,darkMatterProfile_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily accelerator} dark matter profile class.
!!}
implicit none
type (darkMatterProfileAccelerator) :: self
class (darkMatterProfileClass ), intent(in ), target :: darkMatterProfile_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- double precision , intent(in) :: toleranceRelative , factorRadiusMaximum
+ double precision , intent(in) :: toleranceRelative , factorRadiusMaximum
!![
-
+
!!]
- self%uniqueIDPrevious =-1_kind_int8
- self%treePrevious =+1
- self%factorRadiusLogarithmicMaximum=+log(sqrt(factorRadiusMaximum))
return
end function acceleratorConstructorInternal
- subroutine acceleratorAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileAccelerator), intent(inout) :: self
-
- call calculationResetEvent%attach(self,acceleratorCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileAccelerator')
- return
- end subroutine acceleratorAutoHook
-
subroutine acceleratorDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily accelerator} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileAccelerator), intent(inout) :: self
!![
-
-
+
!!]
- if (calculationResetEvent%isAttached(self,acceleratorCalculationReset)) call calculationResetEvent%detach(self,acceleratorCalculationReset)
return
end subroutine acceleratorDestructor
- subroutine acceleratorCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- integer :: i
- !$GLC attributes unused :: node
-
- ! Trees are maintained for two nodes - this is often advantageous as queries are often made for satellite and host nodes
- ! together. If the current node is one for which we currently have a tree, invalidate that tree. Otherwise, if the current
- ! node is a satellite in the current host node, place it into the second tree, otherwise, into the first.
- if (uniqueID == self%uniqueIDPrevious(1)) then
- i =+1
- else if (uniqueID == self%uniqueIDPrevious(2)) then
- i =+2
- else
- if (node%isSatellite() .and. node%parent%uniqueID() == self%uniqueIDPrevious(1)) then
- i=2
- else
- i=1
- end if
- end if
- self%uniqueIDPrevious(i)=uniqueID
- self%treePrevious =i
- if (associated(self%treeMassEnclosed(i)%root)) deallocate(self%treeMassEnclosed(i)%root)
- return
- end subroutine acceleratorCalculationReset
-
- double precision function acceleratorDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorDensity=self%darkMatterProfile_%density(node,radius)
- return
- end function acceleratorDensity
-
- double precision function acceleratorDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorDensityLogSlope=self%darkMatterProfile_%densityLogSlope(node,radius)
- return
- end function acceleratorDensityLogSlope
-
- double precision function acceleratorRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- acceleratorRadiusEnclosingDensity=self%darkMatterProfile_%radiusEnclosingDensity(node,density)
- return
- end function acceleratorRadiusEnclosingDensity
-
- double precision function acceleratorRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- acceleratorRadiusEnclosingMass=self%darkMatterProfile_%radiusEnclosingMass(node,mass)
- return
- end function acceleratorRadiusEnclosingMass
-
- double precision function acceleratorRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- acceleratorRadialMoment=self%darkMatterProfile_%radialMoment(node,moment,radiusMinimum,radiusMaximum)
- return
- end function acceleratorRadialMoment
-
- double precision function acceleratorEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Binary_Search_Trees, only : binaryTreeNode
- use :: Numerical_Comparison, only : Values_Agree
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (binaryTreeNode ), pointer :: left1 , left2 , &
- & right1 , right2
- double precision :: massEnclosed1 , massEnclosed2, &
- & radiusLogarithmic
- logical :: found
- integer :: i
-
- if (node%uniqueID() == self%uniqueIDPrevious(1)) then
- i=1
- else if (node%uniqueID() == self%uniqueIDPrevious(2)) then
- i=2
- else
- call self%calculationReset(node,node%uniqueID())
- i=self%treePrevious
- end if
- found=.false.
- radiusLogarithmic=log(radius)
- call self%treeMassEnclosed(i)%bracket(radiusLogarithmic,left1,right1)
- if (associated(left1).and.associated(right1)) then
- if (associated(left1,right1)) then
- acceleratorEnclosedMass=exp(left1%value)
- found =.true.
- else
- if ( &
- & +radiusLogarithmic- left1%key < self%factorRadiusLogarithmicMaximum &
- & .and. &
- & -radiusLogarithmic+right1%key < self%factorRadiusLogarithmicMaximum &
- & ) then
- left2 => left1%predecessor()
- right2 => right1% successor()
- if (associated(left2).and.associated(right2)) then
- massEnclosed1=(radiusLogarithmic-left1%key)*(right1%value-left1%value)/(right1%key-left1%key)+left1%value
- massEnclosed2=(radiusLogarithmic-left2%key)*(right2%value-left2%value)/(right2%key-left2%key)+left2%value
- if (Values_Agree(massEnclosed1,massEnclosed2,relTol=self%toleranceRelative)) then
- acceleratorEnclosedMass=exp(massEnclosed1)
- found =.true.
- end if
- end if
- end if
- end if
- end if
- if (.not.found) then
- acceleratorEnclosedMass=self%darkMatterProfile_%enclosedMass(node,radius)
- call self%treeMassEnclosed(i)%insert(radiusLogarithmic,log(acceleratorEnclosedMass))
- end if
- return
- end function acceleratorEnclosedMass
-
- double precision function acceleratorPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- acceleratorPotential=self%darkMatterProfile_%potential(node,radius,status)
- return
- end function acceleratorPotential
-
- double precision function acceleratorCircularVelocity(self,node,radius)
+ function acceleratorGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalAccelerator, kinematicsDistributionCollisionless, massDistributionSpherical, nonAnalyticSolversNumerical
implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorCircularVelocity=self%darkMatterProfile_%circularVelocity(node,radius)
- return
- end function acceleratorCircularVelocity
-
- double precision function acceleratorCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorCircularVelocityMaximum=self%darkMatterProfile_%circularVelocityMaximum(node)
- return
- end function acceleratorCircularVelocityMaximum
-
- double precision function acceleratorRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorRadialVelocityDispersion=self%darkMatterProfile_%radialVelocityDispersion(node,radius)
- return
- end function acceleratorRadialVelocityDispersion
-
- double precision function acceleratorRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- acceleratorRadiusFromSpecificAngularMomentum=self%darkMatterProfile_%radiusFromSpecificAngularMomentum(node,specificAngularMomentum)
- return
- end function acceleratorRadiusFromSpecificAngularMomentum
-
- double precision function acceleratorRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorRotationNormalization=self%darkMatterProfile_%rotationNormalization(node)
- return
- end function acceleratorRotationNormalization
-
- double precision function acceleratorEnergy(self,node)
- !!{
- Return the energy of the dark matter halo density profile.
- !!}
- implicit none
- class(darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorEnergy=self%darkMatterProfile_%energy(node)
- return
- end function acceleratorEnergy
-
- double precision function acceleratorKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the dark matter halo density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- acceleratorKSpace=+self%darkMatterProfile_%kSpace(node,waveNumber)
- return
- end function acceleratorKSpace
-
- double precision function acceleratorFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the dark matter halo density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
-
- acceleratorFreefallRadius=self%darkMatterProfile_%freefallRadius(node,time)
- return
- end function acceleratorFreefallRadius
-
- double precision function acceleratorFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the freefall radius in the dark matter halo density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless), pointer :: kinematicsDistribution_
+ class (darkMatterProfileAccelerator ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ !![
+
+ !!]
- acceleratorFreefallRadiusIncreaseRate=self%darkMatterProfile_%freefallRadiusIncreaseRate(node,time)
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalAccelerator :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAccelerator)
+ massDistributionDecorated => self%darkMatterProfile_%get(node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalAccelerator( &
+ & toleranceRelative =self%toleranceRelative , &
+ & factorRadiusMaximum=self%factorRadiusMaximum , &
+ & massDistribution_ = massDistributionDecorated , &
+ & nonAnalyticSolver = nonAnalyticSolversNumerical, &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function acceleratorFreefallRadiusIncreaseRate
+ end function acceleratorGet
diff --git a/source/dark_matter_profiles.adiabatic_Gnedin2004.F90 b/source/dark_matter_profiles.adiabatic_Gnedin2004.F90
index acf6d2d81d..22ed9c9985 100644
--- a/source/dark_matter_profiles.adiabatic_Gnedin2004.F90
+++ b/source/dark_matter_profiles.adiabatic_Gnedin2004.F90
@@ -21,79 +21,17 @@
An implementation of adiabaticGnedin2004 dark matter halo profiles.
!!}
- use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMO , darkMatterProfileDMOClass
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
- use :: Galactic_Structure_Options , only : componentTypeAll , massTypeBaryonic , radiusLarge , weightByMass , &
- & weightIndexNull , enumerationComponentTypeType , enumerationMassTypeType , enumerationWeightByType
- use :: Math_Exponentiation , only : fastExponentiator
- use :: Root_Finder , only : rootFinder
+ use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMO , darkMatterProfileDMOClass
+ use :: Mass_Distributions , only : enumerationNonAnalyticSolversType
- ! Number of previous radius solutions to store.
!![
-
-
-
- !!]
- integer, parameter :: adiabaticGnedin2004StoreCount=10
-
- !![
-
+
- A dark matter profile class which applies adiabatic contraction to the halo as it responds to the presence of
- baryons. Adiabatic contraction follows the algorithm of \cite{gnedin_response_2004}. The parameters $A$ and $\omega$ of
- that model are specified via input parameters {\normalfont \ttfamily A} and {\normalfont \ttfamily omega} respectively.
-
- Given the final radius, $r_\mathrm{f}$, the corresponding initial radius, $r_\mathrm{i}$, is found by solving:
- \begin{equation}
- f_\mathrm{i} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) r_\mathrm{i} = f_\mathrm{f} M_\mathrm{total,0}(\bar{r}_\mathrm{i})
- r_\mathrm{f} + V^2_\mathrm{b}(\bar{r}_\mathrm{f}) \bar{r}_\mathrm{f} r_\mathrm{f}/ \mathrm{G},
- \label{eq:adiabaticContractionGnedinSolution}
- \end{equation}
- where $M_\mathrm{total,0}(r)$ is the initial total matter profile, $V_\mathrm{b}(r)$ is the baryonic contribution to the
- rotation curve, $f_\mathrm{i}$, is the fraction of mass within the virial radius compared to the node mass\footnote{In
- \protect\glc\ the ``node mass'' refers to the total mass of the node, assuming it has the universal complement of
- baryons. Since some halos may contain less than the complete complement of baryons it is possible that $f_\mathrm{i}<1$.},
- $f_\mathrm{f}=(\Omega_\mathrm{M}-\Omega_\mathrm{b})/\Omega_\mathrm{M}+M_\mathrm{satellite, baryonic}/M_\mathrm{total}$,
- $M_\mathrm{satellite, baryonic}$ is the baryonic mass in any satellite halos, $M_\mathrm{total}$ is the node mass, and
- \begin{equation}
- {\bar{r} \over r_0} = A \left({r \over r_0}\right)^\omega,
- \label{eq:adiabaticContractionGnedinPowerLaw}
- \end{equation}
- where the pivot radius $r_0$ is set to $f_0 r_\mathrm{vir}$ where $f_0=${\normalfont \ttfamily [radiusFractionalPivot]}, and
- $r_\mathrm{vir}$ is the virial radius. The original \cite{gnedin_response_2004} assumed $f_0=1$, but the revised model of
- \cite{gnedin_halo_2011} found that $f_0=0.03$ lead to an improved model (less scatter in the best fit values of $(A,\omega)$
- when comparing to N-body simulations).
-
- Note that we explicitly assume that the initial, uncontracted total density profile has the same shape as the initial dark
- matter density profile, that contraction of the halo occurs with no shell crossing, and that satellite halos trace the dark
- matter profile of their host halo. The derivative, $\mathrm{d} r_\mathrm{f}/\mathrm{d}d_\mathrm{i}\equiv r^\prime_\mathrm{i}$
- is found by taking the derivative of eqn.~(\ref{eq:adiabaticContractionGnedinSolution}) to give:
- \begin{eqnarray}
- & & f_\mathrm{i} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) r^\prime_\mathrm{i} + f_\mathrm{i} 4 \pi
- \bar{r}_\mathrm{i}^2 \rho_\mathrm{total,0}(\bar{r}_\mathrm{i}) {\mathrm{d} \bar{r}_\mathrm{i}\over\mathrm{d} r_\mathrm{i}}
- r_\mathrm{i} r^\prime_\mathrm{i} \nonumber \\
- & = & f_\mathrm{f} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) + f_\mathrm{i} 4 \pi \bar{r}_\mathrm{i}^2
- \rho_\mathrm{total,0}(\bar{r}_\mathrm{i}) {\mathrm{d} \bar{r}_\mathrm{i}\over\mathrm{d} r_\mathrm{i}} r_\mathrm{f}
- r^\prime_\mathrm{i} \nonumber \\
- & + & V^2_\mathrm{b}(\bar{r}_\mathrm{f}) \bar{r}_\mathrm{f} / \mathrm{G} + V^2_\mathrm{b}(\bar{r}_\mathrm{f})
- {\mathrm{d}\bar{r}_\mathrm{f}\over \mathrm{d} r_\mathrm{f}} r_\mathrm{f}/ \mathrm{G} +
- {\mathrm{d}V^2_\mathrm{b}\over\mathrm{d} \bar{r}_\mathrm{f}}(\bar{r}_\mathrm{f}) {\mathrm{d}\bar{r}_\mathrm{f}\over
- \mathrm{d} r_\mathrm{f}} \bar{r}_\mathrm{f} r_\mathrm{f}/ \mathrm{G},
- \end{eqnarray}
- where
- \begin{equation}
- {\mathrm{d}\bar{r} \over \mathrm{d} r} = A \left({r \over r_0}\right)^{\omega-1},
- \end{equation}
- and which can then be solved numerically for $r^\prime_\mathrm{i}$.
+ A dark matter profile class which builds \refClass{massDistributionSphericalAdiabaticGnedin2004} objects to apply adiabatic
+ contraction to other dark matter profiles.
-
-
-
-
-
-
!!]
type, extends(darkMatterProfileClass) :: darkMatterProfileAdiabaticGnedin2004
@@ -101,66 +39,16 @@
A dark matter halo profile class implementing adiabaticGnedin2004 dark matter halos.
!!}
private
- logical :: isRecursive , parentDeferred
- class (darkMatterProfileAdiabaticGnedin2004), pointer :: recursiveSelf => null()
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (* ), pointer :: galacticStructure_ => null()
- type (enumerationNonAnalyticSolversType ) :: nonAnalyticSolver
- type (rootFinder ) :: finder
- ! Parameters of the adiabatic contraction algorithm.
- double precision :: A , omega , &
- & radiusFractionalPivot
- ! Stored solutions for reuse.
- integer (kind=kind_int8 ) :: lastUniqueID
- integer :: radiusPreviousIndex , radiusPreviousIndexMaximum
- double precision , dimension(adiabaticGnedin2004StoreCount) :: radiusPrevious , radiusInitialPrevious
- type (fastExponentiator ) :: radiusExponentiator
- ! Quantities used in solving the initial radius root function.
- double precision :: baryonicFinalTerm , baryonicFinalTermDerivative, &
- & darkMatterDistributedFraction , initialMassFraction , &
- & radiusFinal , radiusFinalMean , &
- & darkMatterFraction , radiusVirial , &
- & toleranceRelative
- logical :: massesComputed
- contains
- !![
-
-
-
-
-
-
-
-
- !!]
- final :: adiabaticGnedin2004Destructor
- procedure :: autoHook => adiabaticGnedin2004AutoHook
- procedure :: calculationReset => adiabaticGnedin2004CalculationReset
- procedure :: density => adiabaticGnedin2004Density
- procedure :: densityLogSlope => adiabaticGnedin2004DensityLogSlope
- procedure :: radiusEnclosingDensity => adiabaticGnedin2004RadiusEnclosingDensity
- procedure :: radiusEnclosingMass => adiabaticGnedin2004RadiusEnclosingMass
- procedure :: radialMoment => adiabaticGnedin2004RadialMoment
- procedure :: enclosedMass => adiabaticGnedin2004EnclosedMass
- procedure :: potential => adiabaticGnedin2004Potential
- procedure :: circularVelocity => adiabaticGnedin2004CircularVelocity
- procedure :: circularVelocityMaximum => adiabaticGnedin2004CircularVelocityMaximum
- procedure :: radialVelocityDispersion => adiabaticGnedin2004RadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => adiabaticGnedin2004RadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => adiabaticGnedin2004RotationNormalization
- procedure :: energy => adiabaticGnedin2004Energy
- procedure :: kSpace => adiabaticGnedin2004KSpace
- procedure :: freefallRadius => adiabaticGnedin2004FreefallRadius
- procedure :: freefallRadiusIncreaseRate => adiabaticGnedin2004FreefallRadiusIncreaseRate
- procedure :: radiusInitial => adiabaticGnedin2004RadiusInitial
- procedure :: radiusInitialDerivative => adiabaticGnedin2004RadiusInitialDerivative
- procedure :: computeFactors => adiabaticGnedin2004ComputeFactors
- procedure :: radiusOrbitalMean => adiabaticGnedin2004RadiusOrbitalMean
- procedure :: radiusOrbitalMeanDerivative => adiabaticGnedin2004RadiusOrbitalMeanDerivative
- procedure :: deepCopy => adiabaticGnedin2004DeepCopy
- procedure :: deepCopyReset => adiabaticGnedin2004DeepCopyReset
- procedure :: deepCopyFinalize => adiabaticGnedin2004DeepCopyFinalize
+ class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
+ double precision :: A , omega , &
+ & radiusFractionalPivot , toleranceRelative , &
+ & darkMatterFraction
+ contains
+ final :: adiabaticGnedin2004Destructor
+ procedure :: get => adiabaticGnedin2004Get
end type darkMatterProfileAdiabaticGnedin2004
interface darkMatterProfileAdiabaticGnedin2004
@@ -170,32 +58,21 @@
module procedure adiabaticGnedin2004ConstructorParameters
module procedure adiabaticGnedin2004ConstructorInternal
end interface darkMatterProfileAdiabaticGnedin2004
-
- ! Module-scope quantities used in solving the initial radius root function.
- type (enumerationComponentTypeType ), parameter :: componentType =componentTypeAll
- type (enumerationMassTypeType ), parameter :: massType =massTypeBaryonic
- type (enumerationWeightByType ), parameter :: weightBy =weightByMass
- integer , parameter :: weightIndex =weightIndexNull
- double precision , parameter :: toleranceAbsolute=0.0d0
- type (treeNode ), pointer :: node_
- class (darkMatterProfileAdiabaticGnedin2004), pointer :: self_
- !$omp threadprivate(self_,node_)
-
+
contains
- recursive function adiabaticGnedin2004ConstructorParameters(parameters) result(self)
+ function adiabaticGnedin2004ConstructorParameters(parameters) result(self)
!!{
Default constructor for the {\normalfont \ttfamily adiabaticGnedin2004} dark matter halo profile class.
!!}
- use :: Input_Parameters, only : inputParameters
- use :: Functions_Global, only : galacticStructureConstruct_, galacticStructureDestruct_
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
+ use :: Input_Parameters , only : inputParameters
implicit none
type (darkMatterProfileAdiabaticGnedin2004) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (* ), pointer :: galacticStructure_
type (varying_string ) :: nonAnalyticSolver
double precision :: A , omega , &
& radiusFractionalPivot, toleranceRelative
@@ -238,23 +115,22 @@ recursive function adiabaticGnedin2004ConstructorParameters(parameters) result(s
!!]
- call galacticStructureConstruct_(parameters,galacticStructure_)
- self=darkMatterProfileAdiabaticGnedin2004(A,omega,radiusFractionalPivot,toleranceRelative,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_)
+ self=darkMatterProfileAdiabaticGnedin2004(A,omega,radiusFractionalPivot,toleranceRelative,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
+
!!]
- if (associated(galacticStructure_)) call galacticStructureDestruct_(galacticStructure_)
return
end function adiabaticGnedin2004ConstructorParameters
- recursive function adiabaticGnedin2004ConstructorInternal(A,omega,radiusFractionalPivot,toleranceRelative,nonAnalyticSolver,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function adiabaticGnedin2004ConstructorInternal(A,omega,radiusFractionalPivot,toleranceRelative,nonAnalyticSolver,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily adiabaticGnedin2004} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversIsValid
+ use :: Error , only : Error_Report
implicit none
type (darkMatterProfileAdiabaticGnedin2004) :: self
double precision , intent(in ) :: A , omega , &
@@ -262,53 +138,24 @@ recursive function adiabaticGnedin2004ConstructorInternal(A,omega,radiusFraction
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (* ), intent(in ), target :: galacticStructure_
type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
!![
-
+
!!]
! Validate.
if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- ! Construct the object.
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID=-1_kind_int8
- self%massesComputed =.false.
- self%radiusExponentiator=fastExponentiator(1.0d-3,1.0d0,omega,1.0d4,.false.)
! Evaluate the dark matter fraction.
self%darkMatterFraction=+1.0d0 &
& -self%cosmologyParameters_%OmegaBaryon() &
& /self%cosmologyParameters_%OmegaMatter()
- ! Construct a root finder.
- self%finder=rootFinder( &
- & rootFunction =adiabaticGnedin2004Solver, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
- ! Set recursive properties.
- self%isRecursive =.false.
- self%parentDeferred=.false.
return
end function adiabaticGnedin2004ConstructorInternal
- subroutine adiabaticGnedin2004AutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
-
- call calculationResetEvent%attach(self,adiabaticGnedin2004CalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileAdiabaticGnedin2004')
- return
- end subroutine adiabaticGnedin2004AutoHook
-
subroutine adiabaticGnedin2004Destructor(self)
!!{
Destructor for the {\normalfont \ttfamily adiabaticGnedin2004} dark matter halo profile class.
!!}
- use :: Events_Hooks , only : calculationResetEvent
- use :: Functions_Global, only : galacticStructureDestruct_
implicit none
type(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
@@ -317,690 +164,149 @@ subroutine adiabaticGnedin2004Destructor(self)
!!]
- if (associated (self%galacticStructure_ )) call galacticStructureDestruct_ (self%galacticStructure_ )
- if (calculationResetEvent%isAttached(self,adiabaticGnedin2004CalculationReset)) call calculationResetEvent%detach(self,adiabaticGnedin2004CalculationReset)
return
end subroutine adiabaticGnedin2004Destructor
- subroutine adiabaticGnedin2004CalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- ! Reset calculations for this profile.
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%radiusPreviousIndex = 0
- self%radiusPreviousIndexMaximum = 0
- self%radiusPrevious =-1.0d0
- self%massesComputed =.false.
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine adiabaticGnedin2004CalculationReset
-
- double precision function adiabaticGnedin2004EnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004EnclosedMass=self%recursiveSelf%enclosedMass(node,radius)
- return
- end if
- adiabaticGnedin2004EnclosedMass=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%enclosedMass(node,self%radiusInitial(node,radius))
- return
- end function adiabaticGnedin2004EnclosedMass
-
- double precision function adiabaticGnedin2004Density(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusInitial , radiusInitialDerivative, &
- & densityInitial
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004Density=self%recursiveSelf%density(node,radius)
- return
- end if
- radiusInitial =self %radiusInitial(node,radius )
- densityInitial=self%darkMatterProfileDMO_%density (node,radiusInitial)
- if (radius == radiusInitial) then
- adiabaticGnedin2004Density=+self%darkMatterFraction &
- & *densityInitial
- else
- radiusInitialDerivative = self%radiusInitialDerivative(node,radius)
- adiabaticGnedin2004Density=+self%darkMatterFraction &
- & *densityInitial &
- & *( &
- & +radiusInitial &
- & /radius &
- & ) **2 &
- & *radiusInitialDerivative
- end if
- return
- end function adiabaticGnedin2004Density
-
- double precision function adiabaticGnedin2004DensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- logical :: fallThrough
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004DensityLogSlope=self%recursiveSelf%densityLogSlope(node,radius)
- return
- end if
- fallThrough=self%nonAnalyticSolver == nonAnalyticSolversFallThrough
- if (.not.fallThrough) fallThrough=radius == self%radiusInitial(node,radius)
- if (fallThrough) then
- adiabaticGnedin2004DensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope (node,radius)
- else
- adiabaticGnedin2004DensityLogSlope=self %densityLogSlopeNumerical(node,radius)
- end if
- return
- end function adiabaticGnedin2004DensityLogSlope
-
- double precision function adiabaticGnedin2004RadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RadiusEnclosingDensity=self%recursiveSelf%radiusEnclosingDensity(node,density)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- adiabaticGnedin2004RadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function adiabaticGnedin2004RadiusEnclosingDensity
-
- double precision function adiabaticGnedin2004RadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RadiusEnclosingMass=self%recursiveSelf %radiusEnclosingMass (node,mass)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- adiabaticGnedin2004RadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function adiabaticGnedin2004RadiusEnclosingMass
-
- double precision function adiabaticGnedin2004RadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the radial moment of the density in the dark matter profile of {\normalfont \ttfamily node} between the given
- {\normalfont \ttfamily radiusMinimum} and {\normalfont \ttfamily radiusMaximum} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RadialMoment=self%recursiveSelf%radialMoment(node,moment,radiusMinimum,radiusMaximum)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- adiabaticGnedin2004RadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function adiabaticGnedin2004RadialMoment
-
- double precision function adiabaticGnedin2004Potential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
- logical :: fallThrough
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004Potential=self%recursiveSelf%potential(node,radius,status)
- return
- end if
- fallThrough=self%nonAnalyticSolver == nonAnalyticSolversFallThrough
- if (.not.fallThrough) fallThrough=radius == self%radiusInitial(node,radius)
- if (fallThrough) then
- ! No adiabatic contraction - use the dark-matter-only result.
- adiabaticGnedin2004Potential=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- ! Adiabatic contraction is present - fall back to using a numerical calculation.
- adiabaticGnedin2004Potential=+self %potentialNumerical(node,radius,status)
- end if
- return
- end function adiabaticGnedin2004Potential
-
- double precision function adiabaticGnedin2004CircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004CircularVelocity=self%recursiveSelf%circularVelocity(node,radius)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004CircularVelocity=self%darkMatterProfileDMO_%circularVelocity (node,radius)
- else
- adiabaticGnedin2004CircularVelocity=self %circularVelocityNumerical(node,radius)
- end if
- return
- end function adiabaticGnedin2004CircularVelocity
-
- double precision function adiabaticGnedin2004CircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004CircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- adiabaticGnedin2004CircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function adiabaticGnedin2004CircularVelocityMaximum
-
- double precision function adiabaticGnedin2004RadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RadialVelocityDispersion=self%recursiveSelf%radialVelocityDispersion(node,radius)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion (node,radius)
- else
- adiabaticGnedin2004RadialVelocityDispersion=self %radialVelocityDispersionNumerical(node,radius)
- end if
- return
- end function adiabaticGnedin2004RadialVelocityDispersion
-
- double precision function adiabaticGnedin2004RadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RadiusFromSpecificAngularMomentum=self%recursiveSelf%radiusFromSpecificAngularMomentum(node,specificAngularMomentum)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- adiabaticGnedin2004RadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function adiabaticGnedin2004RadiusFromSpecificAngularMomentum
-
- double precision function adiabaticGnedin2004RotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004RotationNormalization=self%recursiveSelf%rotationNormalization(node)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004RotationNormalization=self%darkMatterProfileDMO_%rotationNormalization (node)
- else
- adiabaticGnedin2004RotationNormalization=self %rotationNormalizationNumerical(node)
- end if
- return
- end function adiabaticGnedin2004RotationNormalization
-
- double precision function adiabaticGnedin2004Energy(self,node)
- !!{
- Return the energy of a adiabaticGnedin2004 halo density profile.
- !!}
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004Energy=self%recursiveSelf%energy(node)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004Energy=self%darkMatterProfileDMO_%energy (node)
- else
- adiabaticGnedin2004Energy=self %energyNumerical(node)
- end if
- return
- end function adiabaticGnedin2004Energy
-
- double precision function adiabaticGnedin2004KSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the adiabaticGnedin2004 density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$), using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004KSpace=self%recursiveSelf%kSpace(node,wavenumber)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004KSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- adiabaticGnedin2004KSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function adiabaticGnedin2004KSpace
-
- double precision function adiabaticGnedin2004FreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the adiabaticGnedin2004 density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004FreefallRadius=self%recursiveSelf%freefallRadius(node,time)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004FreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- adiabaticGnedin2004FreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function adiabaticGnedin2004FreefallRadius
-
- double precision function adiabaticGnedin2004FreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the adiabaticGnedin2004 density profile at the specified
- {\normalfont \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
-
- ! Use recursive self if necessary.
- if (self%isRecursive) then
- adiabaticGnedin2004FreefallRadiusIncreaseRate=self%recursiveSelf%freefallRadiusIncreaseRate(node,time)
- return
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- adiabaticGnedin2004FreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- adiabaticGnedin2004FreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
- return
- end function adiabaticGnedin2004FreefallRadiusIncreaseRate
-
- double precision function adiabaticGnedin2004RadiusInitial(self,node,radius)
- !!{
- Compute the initial radius in the dark matter halo using the adiabatic contraction algorithm of
- \cite{gnedin_response_2004}.
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- integer :: i , j , &
- & iMod
- double precision :: radiusUpperBound, massEnclosed
+ function adiabaticGnedin2004Get(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , massTypeBaryonic , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalAdiabaticGnedin2004, kinematicsDistributionCollisionless, massDistributionSpherical , kinematicsDistributionClass, &
+ & sphericalAdiabaticGnedin2004Initializor , kinematicsDistributionUndecorator , nonAnalyticSolversFallThrough
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_ , kinematicsDistribution__
+ class (darkMatterProfileAdiabaticGnedin2004 ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated , massDistributionBaryonic
+ double precision :: massBaryonicSelfTotal , massBaryonicTotal , &
+ & darkMatterDistributedFraction, initialMassFraction
+ procedure (sphericalAdiabaticGnedin2004Initializor), pointer :: initializationFunction
+ class (* ), pointer :: initializationSelf , initializationArgument
+ !![
+
+ !!]
- ! Reset stored solutions if the node has changed.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Check for a previously computed solution.
- if (self%radiusPreviousIndexMaximum > 0 .and. any(self%radiusPrevious(1:self%radiusPreviousIndexMaximum) == radius)) then
- adiabaticGnedin2004RadiusInitial=0.0d0
- do i=1,self%radiusPreviousIndexMaximum
- if (self%radiusPrevious(i) == radius) then
- adiabaticGnedin2004RadiusInitial=self%radiusInitialPrevious(i)
- exit
- end if
- end do
- return
- end if
- ! Get the virial radius of the node.
- self%radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
- ! Return radius unchanged if larger than the virial radius.
- if (radius >= self%radiusVirial) then
- adiabaticGnedin2004RadiusInitial=radius
- return
- end if
- ! Compute the various factors needed by this calculation.
- call self%computeFactors(node,radius,computeGradientFactors=.false.)
- !! Note that even if no baryons are present at this radius we can not assume that the initial radius is unchanged because it
- !! is possible that the initial fraction of baryons and the fraction of mass distributed as the dark matter are not equal, fᵢ
- !! ≠ fᵪ.
- ! Check that solution is within bounds.
- if (adiabaticGnedin2004Solver(self%radiusVirial) < 0.0d0) then
- adiabaticGnedin2004RadiusInitial=self%radiusVirial
- return
- end if
- j=-1
- if (self%radiusPreviousIndexMaximum > 0) then
- ! No exact match exists, look for approximate matches.
- do i=1,self%radiusPreviousIndexMaximum
- iMod=modulo(self%radiusPreviousIndex-i,adiabaticGnedin2004StoreCount)+1
- if (abs(radius-self%radiusPrevious(iMod))/self%radiusPrevious(iMod) < self%toleranceRelative) then
- j=iMod
- exit
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Set the baryonic component to zero - we will compute this later during initialization.
+ massDistributionBaryonic => null()
+ massBaryonicTotal = 0.0d0
+ massBaryonicSelfTotal = 0.0d0
+ darkMatterDistributedFraction = 0.0d0
+ initialMassFraction = 0.0d0
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalAdiabaticGnedin2004 :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAdiabaticGnedin2004)
+ massDistributionDecorated => self%darkMatterProfileDMO_%get(node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ initializationFunction => adiabaticGnedin2004Initialize
+ initializationSelf => self
+ initializationArgument => node
+ !![
+
+
+ massDistributionSphericalAdiabaticGnedin2004( &
+ & A =self %A , &
+ & omega =self %omega , &
+ & radiusVirial =self%darkMatterHaloScale_%radiusVirial (node), &
+ & radiusFractionalPivot =self %radiusFractionalPivot , &
+ & darkMatterFraction =self %darkMatterFraction , &
+ & darkMatterDistributedFraction= darkMatterDistributedFraction , &
+ & massFractionInitial = initialMassFraction , &
+ & nonAnalyticSolver =self %nonAnalyticSolver , &
+ & toleranceRelative =self %toleranceRelative , &
+ & massDistribution_ = massDistributionDecorated , &
+ & massDistributionBaryonic = massDistributionBaryonic , &
+ & initializationFunction = initializationFunction , &
+ & initializationSelf = initializationSelf , &
+ & initializationArgument = initializationArgument , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ kinematicsDistribution__ => massDistributionDecorated%kinematicsDistribution()
+ if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
+ allocate(kinematicsDistributionUndecorator :: kinematicsDistribution_)
+ select type (kinematicsDistribution_)
+ type is (kinematicsDistributionUndecorator )
+ !![
+
+
+ kinematicsDistributionUndecorator(kinematicsDistribution__)
+
+
+ !!]
+ end select
+ else
+ allocate(kinematicsDistributionCollisionless :: kinematicsDistribution_)
+ select type (kinematicsDistribution_)
+ type is (kinematicsDistributionCollisionless)
+ !![
+
+
+ kinematicsDistributionCollisionless(kinematicsDistribution__)
+
+
+ !!]
+ end select
end if
- end do
- end if
- ! Find the solution for initial radius.
- if (j == -1) then
- ! No previous solution to use as an initial guess. Instead, we make an estimate of the initial radius under the
- ! assumption that the mass of dark matter (in the initial profile) enclosed within the mean initial radius is the
- ! same as enclosed within the mean final radius. Since the initial and final radii are typically not too
- ! different, and since the mean radius is a weak (ѡ<1) function of the radius this is a useful
- ! approximation. Furthermore, since it will underestimate the actual mass within the initial mean radius it gives
- ! an overestimate of the initial radius. This means that we have a bracketing of the initial radius which we can
- ! use in the solver.
- massEnclosed=+self%darkMatterProfileDMO_%enclosedMass(node,self%radiusOrbitalMean(self%radiusFinal))
- if (massEnclosed > 0.0d0) then
- radiusUpperBound=+( &
- & +self%baryonicFinalTerm &
- & / massEnclosed &
- & +self%darkMatterDistributedFraction &
- & *self%radiusFinal &
- & ) &
- & / self%initialMassFraction
- if (radiusUpperBound < radius) radiusUpperBound=radius
- else
- radiusUpperBound=radius
- end if
- call self%finder%rangeExpand( &
- & rangeExpandUpward =1.1d0 , &
- & rangeExpandDownward =0.9d0 , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- adiabaticGnedin2004RadiusInitial=self%finder%find(rootRange=[radius,radiusUpperBound])
- else
- ! Use previous solution as an initial guess.
- call self%finder%rangeExpand( &
- & rangeExpandDownward =1.0d0/sqrt(1.0d0+self%toleranceRelative), &
- & rangeExpandUpward =1.0d0*sqrt(1.0d0+self%toleranceRelative), &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive , &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- adiabaticGnedin2004RadiusInitial=self%finder%find( &
- & rootRange=[ &
- & self%radiusInitialPrevious(j)/sqrt(1.0d0+self%toleranceRelative), &
- & self%radiusInitialPrevious(j)*sqrt(1.0d0+self%toleranceRelative) &
- & ] &
- & )
- end if
- ! Store this solution.
- self%radiusPreviousIndex =modulo(self%radiusPreviousIndex ,adiabaticGnedin2004StoreCount)+1
- self%radiusPreviousIndexMaximum =min (self%radiusPreviousIndexMaximum+1,adiabaticGnedin2004StoreCount)
- self%radiusPrevious (self%radiusPreviousIndex)=radius
- self%radiusInitialPrevious (self%radiusPreviousIndex)=adiabaticGnedin2004RadiusInitial
- return
- end function adiabaticGnedin2004RadiusInitial
-
- double precision function adiabaticGnedin2004RadiusInitialDerivative(self,node,radius)
- !!{
- Compute the derivative of the initial radius in the dark matter halo using the adiabatic contraction algorithm of
- \cite{gnedin_response_2004}.
- !!}
- use :: Display , only : displayMessage, displayIndent, displayUnindent
- use :: Error , only : Error_Report
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (varying_string ), save :: message
- !$omp threadprivate(message)
- character (len=12 ) :: label
- double precision :: radiusInitial , radiusInitialMean , &
- & massDarkMatterInitial , densityDarkMatterInitial , &
- & radiusInitialMeanSelfDerivative, radiusFinalMeanSelfDerivative, &
- & numerator , denominator
-
- ! Reset stored solutions if the node has changed.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Compute the various factors needed by this calculation.
- call self%computeFactors(node,radius,computeGradientFactors=.true.)
- ! Return unit derivative if radius is larger than the virial radius.
- if (radius >= self%radiusVirial) then
- adiabaticGnedin2004RadiusInitialDerivative=1.0d0
- return
- end if
- ! Validate.
- if (radius <= 0.0d0) call Error_Report('non-positive radius')
- ! Compute initial radius, and derivatives of initial and final mean radii.
- radiusInitial =self %radiusInitial (node,radius )
- radiusInitialMeanSelfDerivative=self %radiusOrbitalMeanDerivative( radiusInitial )
- radiusFinalMeanSelfDerivative =self %radiusOrbitalMeanDerivative( radius )
- ! Find the initial mean orbital radius.
- radiusInitialMean =self %radiusOrbitalMean ( radiusInitial )
- ! Get the mass of dark matter inside the initial radius.
- massDarkMatterInitial =self%darkMatterProfileDMO_%enclosedMass (node,radiusInitialMean)
- ! Get the density of dark matter at the initial radius.
- densityDarkMatterInitial =self%darkMatterProfileDMO_%density (node,radiusInitialMean)
- ! Find the solution for the derivative of the initial radius.
- numerator =+( &
- & +massDarkMatterInitial &
- & *self%darkMatterDistributedFraction &
- & +self%baryonicFinalTerm &
- & *( &
- & +1.0d0 / radius &
- & +radiusFinalMeanSelfDerivative/self%radiusFinalMean &
- & ) &
- & +self%baryonicFinalTermDerivative &
- & )
- denominator =+( &
- & +massDarkMatterInitial*self%initialMassFraction &
- & +( &
- & +self%initialMassFraction *radiusInitial &
- & -self%darkMatterDistributedFraction*radius &
- & ) &
- & *4.0d0 &
- & *Pi &
- & *radiusInitialMean**2 &
- & *densityDarkMatterInitial &
- & *radiusInitialMeanSelfDerivative &
- & )
- if (exponent(numerator)-exponent(denominator) > maxExponent(0.0d0)) then
- call displayIndent ('Radius derivative calculation')
- write (label,'(e12.6)') radius
- message='r_final = '//label//' Mpc'
- call displayMessage (message)
- write (label,'(e12.6)') radiusInitial
- message='r_initial = '//label//' Mpc'
- call displayMessage (message)
- write (label,'(e12.6)') self%radiusFinalMean
- message='⟨r_final⟩ = '//label//' Mpc'
- call displayMessage (message)
- write (label,'(e12.6)') radiusInitialMean
- message='⟨r_initial⟩ = '//label//' Mpc'
- call displayMessage (message)
- write (label,'(e12.6)') radiusInitialMeanSelfDerivative
- message='d⟨r_initial⟩/dr_initial = '//label
- call displayMessage (message)
- write (label,'(e12.6)') radiusFinalMeanSelfDerivative
- message='d⟨r_final⟩/dr_final = '//label
- call displayMessage (message)
- write (label,'(e12.6)') massDarkMatterInitial
- message='M_dark,initial = '//label//' M☉'
- call displayMessage (message)
- write (label,'(e12.6)') self%baryonicFinalTerm
- message='M_baryonic,final = '//label//' M☉'
- call displayMessage (message)
- write (label,'(e12.6)') self%baryonicFinalTermDerivative
- message='dM_braryonic,final/dr_initial = '//label//' M☉/Mpc'
- call displayMessage (message)
- call displayUnindent('' )
- call Error_Report('Overflow in initial radius derivative calculation'//{introspection:location})
- end if
- adiabaticGnedin2004RadiusInitialDerivative=+numerator &
- & /denominator
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ end select
return
- end function adiabaticGnedin2004RadiusInitialDerivative
+ end function adiabaticGnedin2004Get
- subroutine adiabaticGnedin2004ComputeFactors(self,node,radius,computeGradientFactors)
+ subroutine adiabaticGnedin2004Initialize(self,node,massDistributionBaryonic,darkMatterDistributedFraction,massFractionInitial)
!!{
- Compute various factors needed when solving for the initial radius in the dark matter halo using the adiabatic contraction
- algorithm of \cite{gnedin_response_2004}.
+ Initialize the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , optimizeForEnclosedMassSummation , optimizeForRotationCurveGradientSummation , optimizeForRotationCurveSummation, &
- & reductionSummation , treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Functions_Global , only : galacticStructureMassEnclosed_ , galacticStructureVelocityRotation_, galacticStructureVelocityRotationGradient_
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : massTypeBaryonic
+ use :: Mass_Distributions , only : massDistributionSphericalAdiabaticGnedin2004
+ use :: Error , only : Error_Report
implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- logical , intent(in ) :: computeGradientFactors
- type (treeNode ) , pointer :: nodeCurrent , nodeHost
- class (nodeComponentBasic ) , pointer :: basic
- double precision :: massBaryonicSelfTotal , massBaryonicTotal , &
- & velocityCircularSquaredGradient, velocityCircularSquared
-
- ! Set module-scope pointers to node and self.
- node_ => node
- self_ => self
- ! Store the final radius and its orbit-averaged mean.
- self%radiusFinal = radius
- self%radiusFinalMean=self%radiusOrbitalMean(radius)
- ! Compute the baryonic contribution to the rotation curve.
- velocityCircularSquared=galacticStructureVelocityRotation_(self%galacticStructure_,node,self%radiusFinalMean,componentType,massType)**2
- self%baryonicFinalTerm=velocityCircularSquared*self%radiusFinalMean*self%radiusFinal/gravitationalConstantGalacticus
- ! Compute the baryonic contribution to the rotation curve.
- if (computeGradientFactors) then
- velocityCircularSquaredGradient =+galacticStructureVelocityRotationGradient_(self%galacticStructure_,node,self%radiusFinalMean,componentType,massType) &
- & *2.0d0 &
- & *sqrt(velocityCircularSquared)
- self%baryonicFinalTermDerivative=+ velocityCircularSquaredGradient &
- & *self%radiusOrbitalMeanDerivative(self%radiusFinal) &
- & *self%radiusFinalMean &
- & *self%radiusFinal &
- & / gravitationalConstantGalacticus
- end if
- ! Compute the initial baryonic contribution from this halo, and any satellites.
- if (.not.self%massesComputed) then
- massBaryonicTotal = 0.0d0
- massBaryonicSelfTotal = 0.0d0
- nodeCurrent => node
- nodeHost => node
- do while (associated(nodeCurrent))
- massBaryonicTotal=+massBaryonicTotal &
- & +galacticStructureMassEnclosed_( &
- & self%galacticStructure_, &
- & nodeCurrent , &
- & radiusLarge , &
- & componentType , &
- & massType , &
- & weightBy , &
- & weightIndex &
- & )
- if (associated(nodeCurrent,nodeHost)) then
- massBaryonicSelfTotal=massBaryonicTotal
- do while (associated(nodeCurrent%firstSatellite))
- nodeCurrent => nodeCurrent%firstSatellite
- end do
- if (associated(nodeCurrent,nodeHost)) nodeCurrent => null()
- else
+ class (* ), intent(inout), target :: self , node
+ class (massDistributionClass), intent( out), pointer :: massDistributionBaryonic
+ double precision , intent( out) :: darkMatterDistributedFraction, massFractionInitial
+ type (treeNode ) , pointer :: nodeCurrent
+ class (nodeComponentBasic ) , pointer :: basic
+ double precision :: massBaryonicSelf , massBaryonicTotal , &
+ & massBaryonicSubhalos
+
+ select type (self)
+ type is (darkMatterProfileAdiabaticGnedin2004)
+ select type (node)
+ type is (treeNode)
+ ! Compute the initial baryonic contribution from this halo, and any satellites.
+ massDistributionBaryonic => node%massDistribution(massType=massTypeBaryonic)
+ massBaryonicSelf = node%massBaryonic ( )
+ ! Compute baryonic mass in subhalos.
+ massBaryonicSubhalos = 0.0d0
+ nodeCurrent => node
+ do while (associated(nodeCurrent%firstSatellite))
+ nodeCurrent => nodeCurrent%firstSatellite
+ end do
+ if (associated(nodeCurrent,node)) nodeCurrent => null()
+ do while (associated(nodeCurrent))
+ massBaryonicSubhalos=+ massBaryonicSubhalos &
+ & +nodeCurrent%massBaryonic ()
if (associated(nodeCurrent%sibling)) then
nodeCurrent => nodeCurrent%sibling
do while (associated(nodeCurrent%firstSatellite))
@@ -1010,279 +316,23 @@ subroutine adiabaticGnedin2004ComputeFactors(self,node,radius,computeGradientFac
nodeCurrent => nodeCurrent%parent
if (associated(nodeCurrent,node)) nodeCurrent => null()
end if
- end if
- end do
- ! Limit masses to physical values.
- massBaryonicSelfTotal=max(massBaryonicSelfTotal,0.0d0)
- massBaryonicTotal =max(massBaryonicTotal ,0.0d0)
- ! Compute the fraction of matter assumed to be distributed like the dark matter.
- basic => node%basic()
- self%darkMatterDistributedFraction =min((self%cosmologyParameters_%OmegaMatter()-self%cosmologyParameters_%OmegaBaryon())/self%cosmologyParameters_%OmegaMatter()+(massBaryonicTotal-massBaryonicSelfTotal)/basic%mass(),1.0d0)
- ! Compute the initial mass fraction.
- self%initialMassFraction =min((self%cosmologyParameters_%OmegaMatter()-self%cosmologyParameters_%OmegaBaryon())/self%cosmologyParameters_%OmegaMatter()+ massBaryonicTotal /basic%mass(),1.0d0)
- ! Record that masses (and mass fractions) have been computed.
- self%massesComputed=.true.
- end if
- return
- end subroutine adiabaticGnedin2004ComputeFactors
-
- double precision function adiabaticGnedin2004RadiusOrbitalMean(self,radius)
- !!{
- Returns the orbit averaged radius for dark matter corresponding the given {\normalfont \ttfamily radius} using the model of
- \cite{gnedin_response_2004}.
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- double precision , intent(in ) :: radius
-
- adiabaticGnedin2004RadiusOrbitalMean=+self%A &
- & *self%radiusFractionalPivot &
- & *self%radiusVirial &
- & *self%radiusExponentiator%exponentiate( &
- & + radius &
- & /self%radiusFractionalPivot &
- & /self%radiusVirial &
- & )
- return
- end function adiabaticGnedin2004RadiusOrbitalMean
-
- double precision function adiabaticGnedin2004RadiusOrbitalMeanDerivative(self,radius)
- !!{
- Returns the derivative of the orbit averaged radius for dark matter corresponding the given {\normalfont \ttfamily radius} using the model of
- \cite{gnedin_response_2004}.
- !!}
- implicit none
- class (darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- double precision , intent(in ) :: radius
-
- adiabaticGnedin2004RadiusOrbitalMeanDerivative=+self%A &
- & *self%omega &
- & *( &
- & + radius &
- & /self%radiusFractionalPivot &
- & /self%radiusVirial &
- & )**(self%omega-1.0d0)
- return
- end function adiabaticGnedin2004RadiusOrbitalMeanDerivative
-
- double precision function adiabaticGnedin2004Solver(radiusInitial)
- !!{
- Root function used in finding the initial radius in the dark matter halo when solving for adiabatic contraction.
- !!}
- implicit none
- double precision, intent(in ) :: radiusInitial
- double precision :: massDarkMatterInitial, radiusInitialMean
-
- ! Find the initial mean orbital radius.
- radiusInitialMean =self_ %radiusOrbitalMean( radiusInitial )
- ! Get the mass of dark matter inside the initial radius.
- massDarkMatterInitial=self_%darkMatterProfileDMO_%enclosedMass (node_,radiusInitialMean)
- ! Compute the root function.
- adiabaticGnedin2004Solver=+massDarkMatterInitial &
- & *( &
- & +self_%initialMassFraction* radiusInitial &
- & -self_%darkMatterDistributedFraction *self_%radiusFinal &
- & ) &
- & -self_%baryonicFinalTerm
- return
- end function adiabaticGnedin2004Solver
-
- subroutine adiabaticGnedin2004DeepCopyReset(self)
- !!{
- Perform a deep copy reset of the object.
- !!}
- use :: Functions_Global, only : galacticStructureDeepCopyReset_
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
-
- self % copiedSelf => null()
- if (.not.self%isRecursive) self%recursiveSelf => null()
- if (associated(self%cosmologyParameters_ )) call self%cosmologyParameters_ %deepCopyReset()
- if (associated(self%darkMatterHaloScale_ )) call self%darkMatterHaloScale_ %deepCopyReset()
- if (associated(self%darkMatterProfileDMO_)) call self%darkMatterProfileDMO_%deepCopyReset()
- if (associated(self%galacticStructure_ )) call galacticStructureDeepCopyReset_(self%galacticStructure_)
- return
- end subroutine adiabaticGnedin2004DeepCopyReset
-
- subroutine adiabaticGnedin2004DeepCopyFinalize(self)
- !!{
- Finalize a deep reset of the object.
- !!}
- use :: Functions_Global, only : galacticStructureDeepCopyFinalize_
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
-
- if (self%isRecursive) call adiabaticGnedin2004FindParent(self)
- if (associated(self%cosmologyParameters_ )) call self%cosmologyParameters_ %deepCopyFinalize()
- if (associated(self%darkMatterHaloScale_ )) call self%darkMatterHaloScale_ %deepCopyFinalize()
- if (associated(self%darkMatterProfileDMO_)) call self%darkMatterProfileDMO_%deepCopyFinalize()
- if (associated(self%galacticStructure_ )) call galacticStructureDeepCopyFinalize_(self%galacticStructure_)
- return
- end subroutine adiabaticGnedin2004DeepCopyFinalize
-
- subroutine adiabaticGnedin2004DeepCopy(self,destination)
- !!{
- Perform a deep copy of the object.
- !!}
- use :: Error , only : Error_Report
- use :: Functions_Global , only : galacticStructureDeepCopy_
-#ifdef OBJECTDEBUG
- use :: Display , only : displayMessage , verbosityLevelSilent
- use :: MPI_Utilities , only : mpiSelf
- use :: Function_Classes , only : debugReporting
- use :: ISO_Varying_String, only : operator(//) , var_str
- use :: String_Handling , only : operator(//)
-#endif
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout), target :: self
- class(darkMatterProfileClass ), intent(inout) :: destination
-
- call self%darkMatterProfileClass%deepCopy(destination)
- select type (destination)
- type is (darkMatterProfileAdiabaticGnedin2004)
- destination%finder =self%finder
- destination%A =self%A
- destination%omega =self%omega
- destination%radiusFractionalPivot =self%radiusFractionalPivot
- destination%lastUniqueID =self%lastUniqueID
- destination%radiusPreviousIndex =self%radiusPreviousIndex
- destination%radiusPreviousIndexMaximum =self%radiusPreviousIndexMaximum
- destination%radiusPrevious =self%radiusPrevious
- destination%radiusInitialPrevious =self%radiusInitialPrevious
- destination%radiusExponentiator =self%radiusExponentiator
- destination%baryonicFinalTerm =self%baryonicFinalTerm
- destination%baryonicFinalTermDerivative =self%baryonicFinalTermDerivative
- destination%darkMatterDistributedFraction =self%darkMatterDistributedFraction
- destination%initialMassFraction =self%initialMassFraction
- destination%radiusFinal =self%radiusFinal
- destination%radiusFinalMean =self%radiusFinalMean
- destination%radiusVirial =self%radiusVirial
- destination%darkMatterFraction =self%darkMatterFraction
- destination%massesComputed =self%massesComputed
- destination%isRecursive =self%isRecursive
- destination%parentDeferred =.false.
- if (self%isRecursive) then
- if (associated(self%recursiveSelf%recursiveSelf)) then
- ! If the parent self's recursiveSelf pointer is set, it indicates that it was deep-copied, and the pointer points to
- ! that copy. In that case we set the parent self of our destination to that copy.
- destination%recursiveSelf => self%recursiveSelf%recursiveSelf
- else
- ! The parent self does not appear to have been deep-copied yet. Retain the same parent self pointer in our copy, but
- ! indicate that we need to look for the new parent later.
- destination%recursiveSelf => self%recursiveSelf
- destination%parentDeferred = .true.
- end if
- else
- ! This is a parent of a recursively-constructed object. Record the location of our copy so that it can be used to set
- ! the parent in deep copies of the child object.
- call adiabaticGnedin2004DeepCopyAssign(self,destination)
- destination%recursiveSelf => null()
- end if
- nullify(destination%cosmologyParameters_)
- if (associated(self%cosmologyParameters_)) then
- if (associated(self%cosmologyParameters_%copiedSelf)) then
- select type(s => self%cosmologyParameters_%copiedSelf)
- class is (cosmologyParametersClass)
- destination%cosmologyParameters_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%cosmologyParameters_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%cosmologyParameters_,mold=self%cosmologyParameters_)
- call self%cosmologyParameters_%deepCopy(destination%cosmologyParameters_)
- self%cosmologyParameters_%copiedSelf => destination%cosmologyParameters_
- call destination%cosmologyParameters_%autoHook()
- end if
-#ifdef OBJECTDEBUG
- if (debugReporting.and.mpiSelf%isMaster()) call displayMessage(var_str('functionClass[own] (class : ownerName : ownerLoc : objectLoc : sourceLoc): cosmologyparameters : [destination] : ')//loc(destination)//' : '//loc(destination%cosmologyParameters_)//' : '//{introspection:location:compact},verbosityLevelSilent)
-#endif
- end if
- nullify(destination%darkMatterProfileDMO_)
- if (associated(self%darkMatterProfileDMO_)) then
- if (associated(self%darkMatterProfileDMO_%copiedSelf)) then
- select type(s => self%darkMatterProfileDMO_%copiedSelf)
- class is (darkMatterProfileDMOClass)
- destination%darkMatterProfileDMO_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%darkMatterProfileDMO_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%darkMatterProfileDMO_,mold=self%darkMatterProfileDMO_)
- call self%darkMatterProfileDMO_%deepCopy(destination%darkMatterProfileDMO_)
- self%darkMatterProfileDMO_%copiedSelf => destination%darkMatterProfileDMO_
- call destination%darkMatterProfileDMO_%autoHook()
- end if
-#ifdef OBJECTDEBUG
- if (debugReporting.and.mpiSelf%isMaster()) call displayMessage(var_str('functionClass[own] (class : ownerName : ownerLoc : objectLoc : sourceLoc): darkmatterprofiledmo_ : [destination] : ')//loc(destination)//' : '//loc(destination%darkMatterProfileDMO_)//' : '//{introspection:location:compact},verbosityLevelSilent)
-#endif
- end if
- nullify(destination%galacticStructure_)
- if (associated(self%galacticStructure_)) then
- allocate(destination%galacticStructure_,mold=self%galacticStructure_)
- call galacticStructureDeepCopy_(self%galacticStructure_,destination%galacticStructure_)
-#ifdef OBJECTDEBUG
- if (debugReporting.and.mpiSelf%isMaster()) call displayMessage(var_str('functionClass[own] (class : ownerName : ownerLoc : objectLoc : sourceLoc): galacticstructure : [destination] : ')//loc(destination)//' : '//loc(destination%galacticStructure_)//' : '//{introspection:location:compact},verbosityLevelSilent)
-#endif
- end if
- nullify(destination%darkMatterHaloScale_)
- if (associated(self%darkMatterHaloScale_)) then
- if (associated(self%darkMatterHaloScale_%copiedSelf)) then
- select type(s => self%darkMatterHaloScale_%copiedSelf)
- class is (darkMatterHaloScaleClass)
- destination%darkMatterHaloScale_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%darkMatterHaloScale_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%darkMatterHaloScale_,mold=self%darkMatterHaloScale_)
- call self%darkMatterHaloScale_%deepCopy(destination%darkMatterHaloScale_)
- self%darkMatterHaloScale_%copiedSelf => destination%darkMatterHaloScale_
- call destination%darkMatterHaloScale_%autoHook()
- end if
-#ifdef OBJECTDEBUG
- if (debugReporting.and.mpiSelf%isMaster()) call displayMessage(var_str('functionClass[own] (class : ownerName : ownerLoc : objectLoc : sourceLoc): darkmatterhaloscale : [destination] : ')//loc(destination)//' : '//loc(destination%darkMatterHaloScale_)//' : '//{introspection:location:compact},verbosityLevelSilent)
-#endif
- end if
- call destination%finder%deepCopyActions()
+ end do
+ ! Compute the total baryonic mass.
+ massBaryonicTotal=+massBaryonicSubhalos &
+ & +massBaryonicSelf
+ ! Limit masses to physical values.
+ massBaryonicSelf =max(massBaryonicSelf ,0.0d0)
+ massBaryonicTotal=max(massBaryonicTotal,0.0d0)
+ ! Compute the fraction of matter assumed to be distributed like the dark matter.
+ basic => node%basic()
+ darkMatterDistributedFraction = min(self%darkMatterFraction+(massBaryonicTotal-massBaryonicSelf)/basic%mass(),1.0d0)
+ ! Compute the initial mass fraction.
+ massFractionInitial = min(self%darkMatterFraction+ massBaryonicTotal /basic%mass(),1.0d0)
+ class default
+ call Error_Report('unexpected class'//{introspection:location})
+ end select
class default
- call Error_Report('destination and source types do not match'//{introspection:location})
- end select
- return
- end subroutine adiabaticGnedin2004DeepCopy
-
- subroutine adiabaticGnedin2004DeepCopyAssign(self,destination)
- !!{
- Perform pointer assignment during a deep copy of the object.
- !!}
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
- class(darkMatterProfileClass ), intent(inout), target :: destination
-
- select type (destination)
- type is (darkMatterProfileAdiabaticGnedin2004)
- self%recursiveSelf => destination
+ call Error_Report('unexpected class'//{introspection:location})
end select
return
- end subroutine adiabaticGnedin2004DeepCopyAssign
-
- subroutine adiabaticGnedin2004FindParent(self)
- !!{
- Find the deep-copied parent of a recursive child.
- !!}
- use :: Error, only : Error_Report
- implicit none
- class(darkMatterProfileAdiabaticGnedin2004), intent(inout) :: self
-
- if (self%parentDeferred) then
- if (associated(self%recursiveSelf%recursiveSelf)) then
- self%recursiveSelf => self%recursiveSelf%recursiveSelf
- else
- call Error_Report("recursive child's parent was not copied"//{introspection:location})
- end if
- self%parentDeferred=.false.
- end if
- return
- end subroutine adiabaticGnedin2004FindParent
+ end subroutine adiabaticGnedin2004Initialize
diff --git a/source/dark_matter_profiles.dark_matter_only.F90 b/source/dark_matter_profiles.dark_matter_only.F90
index a08600f6df..290f5a4486 100644
--- a/source/dark_matter_profiles.dark_matter_only.F90
+++ b/source/dark_matter_profiles.dark_matter_only.F90
@@ -21,8 +21,8 @@
An implementation of non-dark-matter-only dark matter halo profiles which are unchanged from their dark-matter-only counterpart.
!!}
- use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMO, darkMatterProfileDMOClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
!![
@@ -38,23 +38,8 @@
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
double precision :: darkMatterFraction
contains
- final :: darkMatterOnlyDestructor
- procedure :: density => darkMatterOnlyDensity
- procedure :: densityLogSlope => darkMatterOnlyDensityLogSlope
- procedure :: radiusEnclosingDensity => darkMatterOnlyRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => darkMatterOnlyRadiusEnclosingMass
- procedure :: radialMoment => darkMatterOnlyRadialMoment
- procedure :: enclosedMass => darkMatterOnlyEnclosedMass
- procedure :: potential => darkMatterOnlyPotential
- procedure :: circularVelocity => darkMatterOnlyCircularVelocity
- procedure :: circularVelocityMaximum => darkMatterOnlyCircularVelocityMaximum
- procedure :: radialVelocityDispersion => darkMatterOnlyRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => darkMatterOnlyRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => darkMatterOnlyRotationNormalization
- procedure :: energy => darkMatterOnlyEnergy
- procedure :: kSpace => darkMatterOnlyKSpace
- procedure :: freefallRadius => darkMatterOnlyFreefallRadius
- procedure :: freefallRadiusIncreaseRate => darkMatterOnlyFreefallRadiusIncreaseRate
+ final :: darkMatterOnlyDestructor
+ procedure :: get => darkMatterOnlyGet
end type darkMatterProfileDarkMatterOnly
interface darkMatterProfileDarkMatterOnly
@@ -72,30 +57,27 @@ function darkMatterOnlyConstructorParameters(parameters) result(self)
Constructor for the {\normalfont \ttfamily darkMatterOnly} non-dark-matter-only dark matter halo profile class which takes
a parameter set as input.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Input_Parameters, only : inputParameters
implicit none
type (darkMatterProfileDarkMatterOnly) :: self
type (inputParameters ), intent(inout) :: parameters
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
!![
-
!!]
- self=darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_)
+ self=darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileDMO_)
!![
-
!!]
return
end function darkMatterOnlyConstructorParameters
- function darkMatterOnlyConstructorInternal(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
+ function darkMatterOnlyConstructorInternal(cosmologyParameters_,darkMatterProfileDMO_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily darkMatterOnly} dark matter profile class.
!!}
@@ -103,9 +85,8 @@ function darkMatterOnlyConstructorInternal(cosmologyParameters_,darkMatterHaloSc
type (darkMatterProfileDarkMatterOnly) :: self
class(cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
! Evaluate the dark matter fraction.
@@ -124,238 +105,79 @@ subroutine darkMatterOnlyDestructor(self)
!![
-
!!]
return
end subroutine darkMatterOnlyDestructor
- double precision function darkMatterOnlyDensity(self,node,radius)
+ function darkMatterOnlyGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : weightByMass
+ use :: Mass_Distributions , only : massDistributionSpherical, massDistributionSphericalScaler, kinematicsDistributionSphericalScaler, kinematicsDistributionClass
+ use :: Error , only : Error_Report
implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- darkMatterOnlyDensity=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%density(node,radius)
- return
- end function darkMatterOnlyDensity
-
- double precision function darkMatterOnlyDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- darkMatterOnlyDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope(node,radius)
- return
- end function darkMatterOnlyDensityLogSlope
-
- double precision function darkMatterOnlyRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- darkMatterOnlyRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity(node,density/self%darkMatterFraction)
- return
- end function darkMatterOnlyRadiusEnclosingDensity
-
- double precision function darkMatterOnlyRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- darkMatterOnlyRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass(node,mass/self%darkMatterFraction)
- return
- end function darkMatterOnlyRadiusEnclosingMass
-
- double precision function darkMatterOnlyRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- darkMatterOnlyRadialMoment=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%radialMoment(node,moment,radiusMinimum,radiusMaximum)
- return
- end function darkMatterOnlyRadialMoment
-
- double precision function darkMatterOnlyEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- darkMatterOnlyEnclosedMass=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%enclosedMass(node,radius)
- return
- end function darkMatterOnlyEnclosedMass
-
- double precision function darkMatterOnlyPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- darkMatterOnlyPotential=+self%darkMatterFraction &
- & *self%darkMatterProfileDMO_%potential(node,radius,status)
- return
- end function darkMatterOnlyPotential
-
- double precision function darkMatterOnlyCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- darkMatterOnlyCircularVelocity=+sqrt(self%darkMatterFraction ) &
- & * self%darkMatterProfileDMO_%circularVelocity(node,radius)
- return
- end function darkMatterOnlyCircularVelocity
-
- double precision function darkMatterOnlyCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- darkMatterOnlyCircularVelocityMaximum=+sqrt(self%darkMatterFraction ) &
- & * self%darkMatterProfileDMO_%circularVelocityMaximum(node)
- return
- end function darkMatterOnlyCircularVelocityMaximum
-
- double precision function darkMatterOnlyRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- darkMatterOnlyRadialVelocityDispersion=+sqrt(self%darkMatterFraction ) &
- & * self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- return
- end function darkMatterOnlyRadialVelocityDispersion
-
- double precision function darkMatterOnlyRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout), target :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- darkMatterOnlyRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum(node,specificAngularMomentum/sqrt(self%darkMatterFraction))
- return
- end function darkMatterOnlyRadiusFromSpecificAngularMomentum
-
- double precision function darkMatterOnlyRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- darkMatterOnlyRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization(node)
- return
- end function darkMatterOnlyRotationNormalization
-
- double precision function darkMatterOnlyEnergy(self,node)
- !!{
- Return the energy of the dark matter halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- darkMatterOnlyEnergy=+self%darkMatterFraction **2 &
- & *self%darkMatterProfileDMO_%energy(node)
- return
- end function darkMatterOnlyEnergy
-
- double precision function darkMatterOnlyKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the dark matter halo density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- ! This is normalized by mass, so no need to include the dark matter fraction.
- darkMatterOnlyKSpace=+self%darkMatterProfileDMO_%kSpace(node,waveNumber)
- return
- end function darkMatterOnlyKSpace
-
- double precision function darkMatterOnlyFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the dark matter halo density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
-
- darkMatterOnlyFreefallRadius=self%darkMatterProfileDMO_%freefallRadius(node,time*sqrt(self%darkMatterFraction))
- return
- end function darkMatterOnlyFreefallRadius
-
- double precision function darkMatterOnlyFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the freefall radius in the dark matter halo density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDarkMatterOnly), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionSphericalScaler), pointer :: kinematicsDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistributionDMO
+ class (darkMatterProfileDarkMatterOnly ), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDMO
+ !![
+
+ !!]
- darkMatterOnlyFreefallRadiusIncreaseRate=+ sqrt(self%darkMatterFraction) &
- & *self%darkMatterProfileDMO_%freefallRadiusIncreaseRate(node,time*sqrt(self%darkMatterFraction))
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get the dark matter-only mass distribution.
+ massDistributionDMO => self %darkMatterProfileDMO_%get (node,weightBy,weightIndex)
+ kinematicsDistributionDMO => massDistributionDMO %kinematicsDistribution( )
+ if (.not.associated(massDistributionDMO)) return
+ select type (massDistributionDMO)
+ class is (massDistributionSpherical)
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalScaler :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalScaler)
+ !![
+
+
+ massDistributionSphericalScaler( &
+ & factorScalingLength= 1.0d0 , &
+ & factorScalingMass =self%darkMatterFraction , &
+ & massDistribution_ = massDistributionDMO &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionSphericalScaler( &
+ & factorScalingLength = 1.0d0 , &
+ & factorScalingMass =self%darkMatterFraction , &
+ & kinematicsDistribution_= kinematicsDistributionDMO &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ class default
+ call Error_Report('a spherical mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
return
- end function darkMatterOnlyFreefallRadiusIncreaseRate
+ end function darkMatterOnlyGet
diff --git a/source/dark_matter_profiles.generic.F90 b/source/dark_matter_profiles.generic.F90
deleted file mode 100644
index 41e5264338..0000000000
--- a/source/dark_matter_profiles.generic.F90
+++ /dev/null
@@ -1,1309 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 base class for dark matter profiles from which both dark-matter-only and
-non-dark-matter-only profiles inherit.
-!!}
-
-module Dark_Matter_Profiles_Generic
- !!{
- A base class for dark matter profiles from which both dark-matter-only and non-dark-matter-only profiles inherit. Implements
- numerical calculations of certain halo properties which are to be used as a fall-back option when no analytical solution
- exists.
- !!}
- use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Function_Classes , only : functionClass
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile, treeNode
- use :: Kind_Numbers , only : kind_int8
- use :: Numerical_Interpolation, only : interpolator
- private
- public :: darkMatterProfileGeneric
-
- !![
-
- !!]
- type, extends(functionClass), abstract :: darkMatterProfileGeneric
- !!{
- A dark matter halo profile class implementing numerical calculations for generic dark matter halos.
- !!}
- ! Note that the following components can not be "private", as private components of parent types which are accessed through a
- ! "USE" association are inaccessible to the child type
- ! (e.g. https://www.ibm.com/support/knowledgecenter/SSGH4D_15.1.3/com.ibm.xlf1513.aix.doc/language_ref/extensible.html).
- class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- ! Tolerances used in numerical solutions.
- double precision :: toleranceRelativeVelocityDispersion = 1.0d-6
- double precision :: toleranceRelativeVelocityDispersionMaximum = 1.0d-3
- double precision :: toleranceRelativePotential = 1.0d-6
- ! Unique ID for memoization
- integer (kind_int8 ) :: genericLastUniqueID
- ! Memoized solutions for the radial velocity dispersion.
- double precision , allocatable, dimension(:) :: genericVelocityDispersionRadialVelocity , genericVelocityDispersionRadialRadius
- double precision :: genericVelocityDispersionRadialRadiusMinimum , genericVelocityDispersionRadialRadiusMaximum, &
- & genericVelocityDispersionRadialRadiusOuter
- type (interpolator ), allocatable :: genericVelocityDispersionRadial
- ! Memoized solutions for the enclosed mass.
- double precision , allocatable, dimension(:) :: genericEnclosedMassMass , genericEnclosedMassRadius
- double precision :: genericEnclosedMassRadiusMinimum , genericEnclosedMassRadiusMaximum
- type (interpolator ), allocatable :: genericEnclosedMass
- contains
- !![
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- !!]
- procedure(genericDensityInterface ), deferred :: density
- procedure(genericEnclosedMassNumerical), deferred :: enclosedMass
- procedure :: enclosedMassNumerical => genericEnclosedMassNumerical
- procedure :: enclosedMassDifferenceNumerical => genericEnclosedMassDifferenceNumerical
- procedure :: potentialNumerical => genericPotentialNumerical
- procedure :: potentialDifferenceNumerical => genericPotentialDifferenceNumerical
- procedure :: circularVelocityNumerical => genericCircularVelocityNumerical
- procedure :: radialVelocityDispersionNumerical => genericRadialVelocityDispersionNumerical
- procedure :: jeansEquationIntegrand => genericJeansEquationIntegrand
- procedure :: jeansEquationRadius => genericJeansEquationRadius
- procedure :: radialMomentNumerical => genericRadialMomentNumerical
- procedure :: rotationNormalizationNumerical => genericRotationNormalizationNumerical
- procedure :: kSpaceNumerical => genericKSpaceNumerical
- procedure :: energyNumerical => genericEnergyNumerical
- procedure :: freefallRadiusNumerical => genericFreefallRadiusNumerical
- procedure :: freefallRadiusIncreaseRateNumerical => genericFreefallRadiusIncreaseRateNumerical
- procedure :: radiusEnclosingDensityNumerical => genericRadiusEnclosingDensityNumerical
- procedure :: radiusEnclosingMassNumerical => genericRadiusEnclosingMassNumerical
- procedure :: circularVelocityMaximumNumerical => genericCircularVelocityMaximumNumerical
- procedure :: radiusCircularVelocityMaximumNumerical => genericRadiusCircularVelocityMaximumNumerical
- procedure :: radiusFromSpecificAngularMomentumNumerical => genericRadiusFromSpecificAngularMomentumNumerical
- procedure :: densityLogSlopeNumerical => genericDensityLogSlopeNumerical
- procedure :: solverSet => genericSolverSet
- procedure , nopass :: solverUnset => genericSolverUnset
- procedure :: calculationResetGeneric => genericCalculationResetGeneric
- end type darkMatterProfileGeneric
-
- abstract interface
- double precision function genericDensityInterface(self,node,radius)
- !!{
- Returns the density (in $M_\odot/$Mpc$^3$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- import darkMatterProfileGeneric, treeNode
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- end function genericDensityInterface
- end interface
-
- ! Module-scope pointers used in integrand functions and root finding.
- type :: genericSolver
- class(darkMatterProfileGeneric), pointer :: self => null()
- type (treeNode ), pointer :: node => null()
- end type genericSolver
- type (genericSolver ), allocatable, dimension(:) :: solvers
- integer , parameter :: solversIncrement =10
- integer :: solversCount = 0
- class (nodeComponentBasic ), pointer :: genericBasic
- class (nodeComponentDarkMatterProfile), pointer :: genericDarkMatterProfile
- double precision :: genericTime , genericRadiusFreefall , genericDensity , genericMass , &
- & genericSpecificAngularMomentum , genericMassGrowthRate , genericScaleGrowthRate, genericScale, &
- & genericShape , genericShapeGrowthRate
- !$omp threadprivate(solvers,solversCount,genericBasic,genericTime,genericRadiusFreefall,genericDensity,genericMass,genericSpecificAngularMomentum,genericMassGrowthRate,genericDarkMatterProfile,genericScaleGrowthRate,genericScale,genericShape,genericShapeGrowthRate)
-
- !![
-
- nonAnalyticSolvers
- Used to specify the type of solution to use when no analytic solution is available.
- yes
- public
- yes
-
-
-
- !!]
-
-contains
-
- double precision function genericEnclosedMassNumerical(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileGeneric ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- genericEnclosedMassNumerical=self%enclosedMassDifferenceNumerical(node,0.0d0,radius)
- return
- end function genericEnclosedMassNumerical
-
- double precision function genericEnclosedMassDifferenceNumerical(self,node,radiusLower,radiusUpper)
- !!{
- Returns the enclosed mass difference (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} between the
- given {\normalfont \ttfamily radiusLower} and {\normalfont \ttfamily radiusUpper} (given in units of Mpc) using a numerical
- calculation.
- !!}
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Integration , only : integrator
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Table_Labels , only : extrapolationTypeExtrapolate
- use :: Numerical_Interpolation, only : gsl_interp_linear
- use :: Error , only : Error_Report
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radiusLower , radiusUpper
- double precision , parameter :: countPointsPerOctave =4.0d+00
- double precision , parameter :: radiusVirialFractionSmall=1.0d-12
- double precision , dimension(:) , allocatable :: masses , radii
- type (integrator ), save :: integrator_
- logical , save :: initialized =.false.
- !$omp threadprivate(integrator_,initialized)
- integer (c_size_t ) :: countRadii , iMinimum , &
- & iMaximum , i
- logical :: remakeTable
- double precision :: radiusIntegralLower , radiusIntegralUpper, &
- & radiusMinimum , radiusMaximum , &
- & radiusVirial
-
- ! Validate input.
- if (radiusUpper < radiusLower) call Error_Report('radiusUpper ≥ radiusLower is required'//{introspection:location})
- if (radiusUpper <= 0.0d0) then
- genericEnclosedMassDifferenceNumerical=0.0d0
- return
- end if
- ! Reset calculations if necessary.
- if (node%uniqueID() /= self%genericLastUniqueID) call self%calculationResetGeneric(node,node%uniqueID())
- ! Determine if the table must be rebuilt.
- remakeTable=.false.
- if (.not.allocated(self%genericEnclosedMassMass)) then
- remakeTable=.true.
- else
- remakeTable= radiusLower < self%genericEnclosedMassRadiusMinimum &
- & .or. &
- & radiusUpper > self%genericEnclosedMassRadiusMaximum
- end if
- if (remakeTable) then
- ! Initialize integrator if necessary.
- if (.not.initialized) then
- integrator_=integrator(genericMassIntegrand,toleranceRelative=1.0d-2)
- initialized=.true.
- end if
- ! Find the range of radii at which to compute the enclosed mass, and construct the arrays.
- call self%solverSet (node)
- radiusVirial =self%darkMatterHaloScale_%radiusVirial(node)
- !! Set an initial range of radii that brackets the requested radii.
- if (radiusLower <= 0.0d0) then
- radiusMinimum=max(0.5d0*radiusUpper,radiusVirial*radiusVirialFractionSmall)
- else
- radiusMinimum=max(0.5d0*radiusLower,radiusVirial*radiusVirialFractionSmall)
- end if
- radiusMaximum=2.0d0*radiusUpper
- !! Round to the nearest factor of 2.
- radiusMinimum=2.0d0**floor (log(radiusMinimum)/log(2.0d0))
- radiusMaximum=2.0d0**ceiling(log(radiusMaximum)/log(2.0d0))
- !! Expand to encompass any pre-existing range.
- if (allocated(self%genericEnclosedMassRadius)) then
- radiusMinimum=min(radiusMinimum,self%genericEnclosedMassRadiusMinimum)
- radiusMaximum=max(radiusMaximum,self%genericEnclosedMassRadiusMaximum)
- end if
- !! Construct arrays.
- countRadii=nint(log(radiusMaximum/radiusMinimum)/log(2.0d0)*countPointsPerOctave+1.0d0)
- allocate(radii (countRadii))
- allocate(masses(countRadii))
- radii=Make_Range(radiusMinimum,radiusMaximum,int(countRadii),rangeTypeLogarithmic)
- ! Copy in any usable results from any previous solution.
- !! Assume by default that no previous solutions are usable.
- iMinimum=+huge(0_c_size_t)
- iMaximum=-huge(0_c_size_t)
- !! Check that a pre-existing solution exists.
- if (allocated(self%genericEnclosedMassRadius)) then
- iMinimum=nint(log(self%genericEnclosedMassRadiusMinimum/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
- iMaximum=nint(log(self%genericEnclosedMassRadiusMaximum/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
- masses(iMinimum:iMaximum)=self%genericEnclosedMassMass
- end if
- ! Solve for the enclosed mass where old results were unavailable.
- do i=1,countRadii
- ! Skip cases for which we have a pre-existing solution.
- if (i >= iMinimum .and. i <= iMaximum) cycle
- ! Find the limits for the integral.
- if (i == 1) then
- radiusIntegralLower=0.0d0
- else
- radiusIntegralLower=radii(i-1)
- end if
- radiusIntegralUpper =radii(i )
- ! Evaluate the integral.
- masses (i)= integrator_%integrate(radiusIntegralLower,radiusIntegralUpper)
- if (i > 1) masses(i)=+masses(i ) &
- & +masses(i-1)
- end do
- call self%solverUnset( )
- ! Build the interpolator.
- if (allocated(self%genericEnclosedMass)) deallocate(self%genericEnclosedMass)
- allocate(self%genericEnclosedMass)
- self%genericEnclosedMass=interpolator(log(radii),log(masses),interpolationType=gsl_interp_linear,extrapolationType=extrapolationTypeExtrapolate)
- ! Store the current results for future re-use.
- if (allocated(self%genericEnclosedMassRadius)) deallocate(self%genericEnclosedMassRadius)
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- allocate(self%genericEnclosedMassRadius(countRadii))
- allocate(self%genericEnclosedMassMass (countRadii))
- self%genericEnclosedMassRadius =radii
- self%genericEnclosedMassMass =masses
- self%genericEnclosedMassRadiusMinimum=radiusMinimum
- self%genericEnclosedMassRadiusMaximum=radiusMaximum
- end if
- ! Interpolate in the table to find the mass difference.
- genericEnclosedMassDifferenceNumerical =+exp(self%genericEnclosedMass %interpolate(log(radiusUpper)))
- if (radiusLower > 0.0d0) &
- & genericEnclosedMassDifferenceNumerical=+ genericEnclosedMassDifferenceNumerical &
- & +exp(self%genericEnclosedMass %interpolate(log(radiusLower)))
- return
- end function genericEnclosedMassDifferenceNumerical
-
- double precision function genericMassIntegrand(radius)
- !!{
- Integrand for mass in generic dark matter profiles.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- genericMassIntegrand=4.0d0*Pi*radius**2*solvers(solversCount)%self%density(solvers(solversCount)%node,radius)
- else
- genericMassIntegrand=0.0d0
- end if
- return
- end function genericMassIntegrand
-
- double precision function genericPotentialNumerical(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc) using a numerical calculation.
- !!}
- use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType, structureErrorCodeSuccess
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Integration , only : integrator
- implicit none
- class (darkMatterProfileGeneric ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- double precision , parameter :: radiusMaximumFactor=1.0d2
- type (integrator ), save :: integrator_
- logical , save :: initialized =.false.
- !$omp threadprivate(integrator_,initialized)
- double precision :: radiusMaximum
-
- if (present(status)) status=structureErrorCodeSuccess
- if (.not.initialized) then
- integrator_=integrator(integrandPotential,toleranceRelative=self%toleranceRelativePotential)
- initialized=.true.
- end if
- call self%solverSet (node)
- radiusMaximum = +radiusMaximumFactor &
- & *self%darkMatterHaloScale_%radiusVirial(node)
- if (radius < radiusMaximum) then
- genericPotentialNumerical = integrator_%integrate( &
- & radius , &
- & radiusMaximum &
- & )
- else
- ! Beyond some large radius approximate as a point mass.
- genericPotentialNumerical=+gravitationalConstantGalacticus &
- & *solvers(solversCount)%self%enclosedMass(solvers(solversCount)%node,radiusMaximum) &
- & *( &
- & +1.0d0/radiusMaximum &
- & -1.0d0/radius &
- & )
- end if
- call self%solverUnset( )
- return
- end function genericPotentialNumerical
-
- double precision function genericPotentialDifferenceNumerical(self,node,radiusLower,radiusUpper)
- !!{
- Returns the potential difference (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} between the
- given {\normalfont \ttfamily radiusLower} and {\normalfont \ttfamily radiusUpper} (given in units of Mpc) using a numerical
- calculation.
- !!}
- use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
- use :: Numerical_Integration , only : integrator
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), pointer :: node
- double precision , intent(in ) :: radiusLower , radiusUpper
- type (integrator ), save :: integrator_
- logical , save :: initialized=.false.
- !$omp threadprivate(integrator_,initialized)
-
- if (.not.initialized) then
- integrator_= integrator(integrandPotential,toleranceRelative=1.0d-6)
- initialized=.true.
- end if
- call self%solverSet (node)
- genericPotentialDifferenceNumerical=integrator_%integrate( &
- & radiusLower, &
- & radiusUpper &
- & )
- call self%solverUnset( )
- return
- end function genericPotentialDifferenceNumerical
-
- double precision function integrandPotential(radius)
- !!{
- Integrand for gravitational potential in a generic dark matter profile.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandPotential=-gravitationalConstantGalacticus &
- & *solvers(solversCount)%self%enclosedMass(solvers(solversCount)%node,radius) &
- & / radius **2
- else
- integrandPotential=0.0d0
- end if
- return
- end function integrandPotential
-
- double precision function genericCircularVelocityNumerical(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- genericCircularVelocityNumerical=sqrt( &
- & +gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & / radius &
- & )
- else
- genericCircularVelocityNumerical=0.0d0
- end if
- return
- end function genericCircularVelocityNumerical
-
- double precision function genericRadialVelocityDispersionNumerical(self,node,radius,radiusOuter)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Error , only : Error_Report , errorStatusSuccess
- use :: Numerical_Integration , only : integrator
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Table_Labels , only : extrapolationTypeFix
- use :: Numerical_Interpolation, only : gsl_interp_linear
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- double precision , intent(in ), optional :: radiusOuter
- double precision , parameter :: radiusTinyFactor =1.0d-9 , radiusLargeFactor=5.0d2
- double precision , parameter :: countPointsPerOctave =2.0d0
- double precision , parameter :: toleranceFactor =2.0d0
- double precision , dimension(:) , allocatable :: velocityDispersions , radii
- double precision :: radiusMinimum , radiusMaximum , &
- & radiusVirial , density , &
- & jeansIntegral , radiusOuter_ , &
- & radiusLower , radiusUpper , &
- & radiusLowerJeansEquation , radiusUpperJeansEquation, &
- & jeansIntegralPrevious , toleranceRelative
- integer (c_size_t ) :: countRadii , iMinimum , &
- & iMaximum , i
- integer :: status
- type (integrator ), save :: integrator_
- logical , save :: initialized =.false.
- logical :: remakeTable
- !$omp threadprivate(integrator_,initialized)
-
- ! Reset calculations if necessary.
- if (node%uniqueID() /= self%genericLastUniqueID) call self%calculationResetGeneric(node,node%uniqueID())
- ! Determine if the table must be rebuilt.
- remakeTable=.false.
- if (.not.allocated(self%genericVelocityDispersionRadialVelocity)) then
- remakeTable=.true.
- else
- remakeTable= radius < self%genericVelocityDispersionRadialRadiusMinimum &
- & .or. &
- & radius > self%genericVelocityDispersionRadialRadiusMaximum
- end if
- if (remakeTable) then
- ! Initialize integrator if necessary.
- if (.not.initialized) then
- integrator_=integrator(genericJeansEquationIntegrand_,toleranceRelative=self%toleranceRelativeVelocityDispersion)
- initialized=.true.
- end if
- ! Find the range of radii at which to compute the velocity dispersion, and construct the arrays.
- call self%solverSet (node)
- radiusVirial =self%darkMatterHaloScale_%radiusVirial(node)
- !! Set an initial range of radii that brackets the requested radius, but avoids tiny radii.
- radiusMinimum=max(0.5d0*radius,radiusTinyFactor*radiusVirial)
- radiusMaximum=max(2.0d0*radius, 2.0d0*radiusVirial)
- !! Round to the nearest factor of 2.
- radiusMinimum=2.0d0**floor (log(radiusMinimum)/log(2.0d0))
- radiusMaximum=2.0d0**ceiling(log(radiusMaximum)/log(2.0d0))
- !! Expand to encompass any pre-existing range.
- if (allocated(self%genericVelocityDispersionRadialRadius)) then
- radiusMinimum=min(radiusMinimum,self%genericVelocityDispersionRadialRadiusMinimum)
- radiusMaximum=max(radiusMaximum,self%genericVelocityDispersionRadialRadiusMaximum)
- end if
- !! Set a suitable outer radius for integration.
- if (present(radiusOuter)) then
- radiusOuter_=radiusOuter
- else
- radiusOuter_=max(10.0d0*radiusMaximum,radiusLargeFactor*radiusVirial)
- end if
- !! Construct arrays.
- countRadii=nint(log(radiusMaximum/radiusMinimum)/log(2.0d0)*countPointsPerOctave+1.0d0)
- allocate(radii (countRadii))
- allocate(velocityDispersions(countRadii))
- radii=Make_Range(radiusMinimum,radiusMaximum,int(countRadii),rangeTypeLogarithmic)
- ! Copy in any usable results from any previous solution.
- !! Assume by default that no previous solutions are usable.
- iMinimum=+huge(0_c_size_t)
- iMaximum=-huge(0_c_size_t)
- !! Check that a pre-existing solution exists.
- if (allocated(self%genericVelocityDispersionRadialRadius)) then
- !! Check that the outer radius for integration has not changed - if it has we need to recompute the full solution for
- !! consistency.
- if (radiusOuter_ == self%genericVelocityDispersionRadialRadiusOuter) then
- iMinimum=nint(log(self%genericVelocityDispersionRadialRadiusMinimum/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
- iMaximum=nint(log(self%genericVelocityDispersionRadialRadiusMaximum/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
- velocityDispersions(iMinimum:iMaximum)=self%genericVelocityDispersionRadialVelocity
- end if
- end if
- ! Solve for the velocity dispersion where old results were unavailable.
- jeansIntegralPrevious=0.0d0
- do i=countRadii,1,-1
- ! Skip cases for which we have a pre-existing solution.
- if (i >= iMinimum .and. i <= iMaximum) cycle
- ! Find the limits for the integral.
- if (i == countRadii) then
- radiusUpper=radiusOuter_
- else
- radiusUpper=radii(i+1)
- end if
- radiusLower =radii(i )
- ! Reset the accumulated Jeans integral if necessary.
- if (i == iMinimum-1) jeansIntegralPrevious=+ velocityDispersions( iMinimum )**2 &
- & *self%density (node,radii(iMinimum))
- ! If the interval is wholly outside of the outer radius, the integral is zero.
- if (radiusLower > radiusOuter_) then
- jeansIntegral =0.0d0
- velocityDispersions(i)=0.0d0
- else
- ! Evaluate the integral.
- density =self %density (node,radiusLower )
- radiusLowerJeansEquation=self %jeansEquationRadius(node,radiusLower )
- radiusUpperJeansEquation=self %jeansEquationRadius(node,radiusUpper )
- jeansIntegral =integrator_%integrate ( radiusLowerJeansEquation,radiusUpperJeansEquation,status)
- if (status /= errorStatusSuccess) then
- ! Integration failed.
- toleranceRelative=+ toleranceFactor &
- & *self%toleranceRelativeVelocityDispersion
- do while (toleranceRelative < self%toleranceRelativeVelocityDispersionMaximum)
- call integrator_%toleranceSet(toleranceRelative=toleranceRelative)
- jeansIntegral=integrator_%integrate(radiusLowerJeansEquation,radiusUpperJeansEquation,status)
- if (status == errorStatusSuccess) then
- exit
- else
- toleranceRelative=+toleranceFactor &
- & *toleranceRelative
- end if
- end do
- if (status /= errorStatusSuccess) call Error_Report('integration of Jeans equation failed'//{introspection:location})
- call integrator_%toleranceSet(toleranceRelative=self%toleranceRelativeVelocityDispersion)
- end if
- if (density <= 0.0d0) then
- ! Density is zero - the velocity dispersion is undefined. If the Jeans integral is also zero this is acceptable - we've
- ! been asked for the velocity dispersion in a region of zero density, so we simply return zero dispersion as it should have
- ! no consequence. If the Jeans integral is non-zero however, then something has gone wrong.
- velocityDispersions(i)=0.0d0
- if (jeansIntegral+jeansIntegralPrevious > 0.0d0) call Error_Report('undefined velocity dispersion'//{introspection:location})
- else
- velocityDispersions(i)=sqrt( &
- & +( &
- & +jeansIntegral &
- & +jeansIntegralPrevious &
- & ) &
- & /density &
- & )
- end if
- end if
- jeansIntegralPrevious=+jeansIntegralPrevious &
- & +jeansIntegral
- end do
- call self%solverUnset( )
- ! Build the interpolator.
- if (allocated(self%genericVelocityDispersionRadial)) deallocate(self%genericVelocityDispersionRadial)
- allocate(self%genericVelocityDispersionRadial)
- self%genericVelocityDispersionRadial=interpolator(log(radii),velocityDispersions,interpolationType=gsl_interp_linear,extrapolationType=extrapolationTypeFix)
- ! Store the current results for future re-use.
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- allocate(self%genericVelocityDispersionRadialRadius (countRadii))
- allocate(self%genericVelocityDispersionRadialVelocity(countRadii))
- self%genericVelocityDispersionRadialRadius =radii
- self%genericVelocityDispersionRadialVelocity =velocityDispersions
- self%genericVelocityDispersionRadialRadiusMinimum=radiusMinimum
- self%genericVelocityDispersionRadialRadiusMaximum=radiusMaximum
- self%genericVelocityDispersionRadialRadiusOuter =radiusOuter_
- end if
- ! Interpolate in the table to find the velocity dispersion.
- genericRadialVelocityDispersionNumerical=self%genericVelocityDispersionRadial%interpolate(log(radius))
- return
- end function genericRadialVelocityDispersionNumerical
-
- double precision function genericJeansEquationIntegrand_(radius)
- !!{
- Integrand for generic dark matter profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- genericJeansEquationIntegrand_=solvers(solversCount)%self%jeansEquationIntegrand(solvers(solversCount)%node,radius)
- return
- end function genericJeansEquationIntegrand_
-
- double precision function genericJeansEquationIntegrand(self,node,radius)
- !!{
- Integrand for generic dark matter profile Jeans equation.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- genericJeansEquationIntegrand=+gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & *self%density (node,radius) &
- & / radius **2
- else
- genericJeansEquationIntegrand=0.0d0
- end if
- return
- end function genericJeansEquationIntegrand
-
- double precision function genericJeansEquationRadius(self,node,radius)
- !!{
- Return the radius variable used in solving the Jeans equation that corresponds to a given physical radius.
- In some cases, it is easier to do the integration with respect to another variable which is a function of
- the physical radius.
- !!}
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node
-
- genericJeansEquationRadius=radius
- return
- end function genericJeansEquationRadius
-
- double precision function genericRadialMomentNumerical(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the radial moment of the density in the dark matter profile of {\normalfont \ttfamily node} between the given
- {\normalfont \ttfamily radiusMinimum} and {\normalfont \ttfamily radiusMaximum} (given in units of Mpc).
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- type (integrator ) :: integrator_
- double precision :: radiusMinimumActual, radiusMaximumActual
-
- radiusMinimumActual=0.0d0
- radiusMaximumActual=self%darkMatterHaloScale_%radiusVirial(node)
- if (present(radiusMinimum)) radiusMinimumActual=radiusMinimum
- if (present(radiusMaximum)) radiusMaximumActual=radiusMaximum
- integrator_=integrator(integrandRadialMoment,toleranceRelative=1.0d-3)
- genericRadialMomentNumerical=integrator_%integrate(radiusMinimumActual,radiusMaximumActual)
- return
-
- contains
-
- double precision function integrandRadialMoment(radius)
- !!{
- Integrand for radial moment in a generic dark matter profile.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandRadialMoment=+ radius **moment &
- & *self%density(node,radius)
- else
- integrandRadialMoment=0.0d0
- end if
- return
- end function integrandRadialMoment
-
- end function genericRadialMomentNumerical
-
- double precision function genericRotationNormalizationNumerical(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision :: radiusVirial
-
- radiusVirial =+self%darkMatterHaloScale_%radiusVirial ( &
- & node &
- & )
- genericRotationNormalizationNumerical=+self %enclosedMass ( &
- & node , &
- & radiusVirial &
- & ) &
- & /4.0d0 &
- & /Pi &
- & /self %radialMomentNumerical( &
- & node , &
- & moment =3.0d0 , &
- & radiusMinimum=0.0d0 , &
- & radiusMaximum=radiusVirial &
- & )
- return
- end function genericRotationNormalizationNumerical
-
- double precision function genericKSpaceNumerical(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the dark matter density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
- type (integrator ) :: integrator_
- double precision :: radiusVirial
-
- radiusVirial =+self %darkMatterHaloScale_%radiusVirial(node )
- integrator_ = integrator (integrandFourierTransform,toleranceRelative=1.0d-3)
- genericKSpaceNumerical=+integrator_%integrate (0.0d0 ,radiusVirial ) &
- & /self %enclosedMass(node ,radiusVirial )
- return
-
- contains
-
- double precision function integrandFourierTransform(radius)
- !!{
- Integrand for Fourier transform of the generic dark matter profile.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandFourierTransform=+4.0d0 &
- & *Pi &
- & * radius **2 &
- & *sin(wavenumber*radius) &
- & / (waveNumber*radius) &
- & *self%density(node,radius)
- else
- integrandFourierTransform=0.0d0
- end if
- return
- end function integrandFourierTransform
-
- end function genericKSpaceNumerical
-
- double precision function genericEnergyNumerical(self,node)
- !!{
- Return the energy of a generic dark matter density profile.
- !!}
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Integration , only : integrator
- implicit none
- class (darkMatterProfileGeneric ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , parameter :: multiplierRadius =100.0d0
- type (integrator ) :: integratorPotential , integratorKinetic, &
- & integratorPressure
- double precision :: radiusVirial , radiusLarge , &
- & energyPotential , energyKinetic , &
- & pseudoPressure
-
- integratorPotential=integrator(integrandEnergyPotential,toleranceRelative=1.0d-3)
- integratorKinetic =integrator(integrandEnergyKinetic ,toleranceRelative=1.0d-3)
- integratorPressure =integrator(integrandPseudoPressure ,toleranceRelative=1.0d-3)
- radiusVirial =+self%darkMatterHaloScale_%radiusVirial(node)
- radiusLarge =+multiplierRadius &
- & *radiusVirial
- energyPotential =+integratorPotential%integrate(0.0d0 ,radiusVirial)
- energyKinetic =+integratorKinetic %integrate(0.0d0 ,radiusVirial)
- pseudoPressure =+integratorPressure %integrate(radiusVirial,radiusLarge )
- genericEnergyNumerical=-0.5d0 &
- & *gravitationalConstantGalacticus &
- & *( &
- & +energyPotential &
- & +self%enclosedMass(node,radiusVirial)**2 &
- & / radiusVirial &
- & ) &
- & +2.0d0 &
- & *Pi &
- & *gravitationalConstantGalacticus &
- & *( &
- & +radiusVirial**3 &
- & *pseudoPressure &
- & +energyKinetic &
- & )
- return
-
- contains
-
- double precision function integrandEnergyPotential(radius)
- !!{
- Integrand for potential energy of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandEnergyPotential=( &
- & +self%enclosedMass(node,radius) &
- & / radius &
- & )**2
- else
- integrandEnergyPotential=0.0d0
- end if
- return
- end function integrandEnergyPotential
-
- double precision function integrandEnergyKinetic(radius)
- !!{
- Integrand for kinetic energy of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandEnergyKinetic=+self%enclosedMass(node,radius) &
- & *self%density (node,radius) &
- & * radius
- else
- integrandEnergyKinetic=0.0d0
- end if
- return
- end function integrandEnergyKinetic
-
- double precision function integrandPseudoPressure(radius)
- !!{
- Integrand for pseudo-pressure ($\rho(r) \sigma^2(r)$) of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandPseudoPressure=+self%enclosedMass(node,radius) &
- & *self%density (node,radius) &
- & / radius **2
- else
- integrandPseudoPressure=0.0d0
- end if
- return
- end function integrandPseudoPressure
-
- end function genericEnergyNumerical
-
- double precision function genericEnergyEvaluate(timeLogarithmic)
- !!{
- GSL-callable function to evaluate the energy of the dark matter profile.
- !!}
- use :: Functions_Global, only : Calculations_Reset_
- implicit none
- double precision, intent(in ), value :: timeLogarithmic
- double precision :: time
-
- time=exp(timeLogarithmic)
- call genericBasic %timeSet ( time )
- call genericBasic %timeLastIsolatedSet( time )
- call genericBasic %massSet (genericMass +genericMassGrowthRate *(time-genericTime))
- call genericDarkMatterProfile%scaleSet (genericScale+genericScaleGrowthRate*(time-genericTime))
- call genericDarkMatterProfile%shapeSet (genericShape+genericShapeGrowthRate*(time-genericTime))
- call Calculations_Reset_(solvers(solversCount)%node)
- genericEnergyEvaluate=solvers(solversCount)%self%energyNumerical(solvers(solversCount)%node)
- return
- end function genericEnergyEvaluate
-
- double precision function genericFreefallRadiusNumerical(self,node,time)
- !!{
- Returns the freefall radius in the adiabaticGnedin2004 density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
- type (rootFinder ) :: finder
-
- call self%solverSet (node)
- genericTime = time
- finder = rootFinder( &
- & rootFunction =rootRadiusFreefall , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
- & )
- genericFreefallRadiusNumerical=finder%find(rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- call self%solverUnset( )
- return
- end function genericFreefallRadiusNumerical
-
- double precision function rootRadiusFreefall(radiusFreefall)
- !!{
- Root function used in finding the radius corresponding to a given freefall time.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- double precision , intent(in ) :: radiusFreefall
- type (integrator) :: integrator_
-
- genericRadiusFreefall=+radiusFreefall
- integrator_ = integrator (integrandTimeFreefall,toleranceRelative=1.0d-3)
- rootRadiusFreefall =+integrator_ %integrate(0.0d0 ,radiusFreefall ) &
- & -genericTime
- return
- end function rootRadiusFreefall
-
- double precision function integrandTimeFreefall(radius)
- !!{
- Integrand for freefall time in the halo.
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- double precision, intent(in ) :: radius
- double precision :: potentialDifference
-
- potentialDifference=+solvers(solversCount)%self%potentialDifferenceNumerical(solvers(solversCount)%node,radius,genericRadiusFreefall)
- if (potentialDifference < 0.0d0) then
- integrandTimeFreefall=+Mpc_per_km_per_s_To_Gyr &
- & /sqrt( &
- & -2.0d0 &
- & *potentialDifference &
- & )
- else
- ! Avoid floating point errors arising from rounding errors.
- integrandTimeFreefall=0.0d0
- end if
- return
- end function integrandTimeFreefall
-
- double precision function genericFreefallRadiusIncreaseRateNumerical(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the dark matter density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- use :: Numerical_Differentiation, only : differentiator
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- double precision , parameter :: timeLogarithmicStep=0.1d0
- type (differentiator ) :: differentiator_
-
- call self%solverSet (node)
- differentiator_ = differentiator (genericFreefallRadiusEvaluate )
- genericFreefallRadiusIncreaseRateNumerical = +differentiator_%derivative(log(time) ,timeLogarithmicStep) &
- & / time
- call self%solverUnset( )
- return
- end function genericFreefallRadiusIncreaseRateNumerical
-
- double precision function genericFreefallRadiusEvaluate(timeLogarithmic)
- !!{
- GSL-callable function to evaluate the freefall radius of the dark matter profile.
- !!}
- implicit none
- double precision, intent(in ), value :: timeLogarithmic
-
- genericFreefallRadiusEvaluate=solvers(solversCount)%self%freefallRadiusNumerical(solvers(solversCount)%node,exp(timeLogarithmic))
- return
- end function genericFreefallRadiusEvaluate
-
- double precision function genericRadiusEnclosingDensityNumerical(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
- type (rootFinder ) :: finder
-
- call self%solverSet (node)
- genericDensity=density
- finder =rootFinder( &
- & rootFunction =rootDensity , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive &
- & )
- genericRadiusEnclosingDensityNumerical=finder%find(rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- call self%solverUnset( )
- return
- end function genericRadiusEnclosingDensityNumerical
-
- double precision function rootDensity(radius)
- !!{
- Root function used in finding the radius enclosing a given mean density.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- rootDensity=+3.0d0 &
- & *solvers(solversCount)%self%enclosedMass(solvers(solversCount)%node,radius) &
- & /4.0d0 &
- & /Pi &
- & / radius **3 &
- & -genericDensity
- return
- end function rootDensity
-
- double precision function genericRadiusEnclosingMassNumerical(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
- type (rootFinder ) :: finder
-
- call self%solverSet (node)
- genericMass = mass
- finder = rootFinder( &
- & rootFunction =rootMass , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
- & )
- genericRadiusEnclosingMassNumerical=finder%find(rootRange=[0.0d0,self%darkMatterHaloScale_%radiusVirial(node)])
- call self%solverUnset( )
- return
- end function genericRadiusEnclosingMassNumerical
-
- double precision function rootMass(radius)
- !!{
- Root function used in finding the radius enclosing a given mass.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- rootMass=+solvers(solversCount)%self%enclosedMass(solvers(solversCount)%node,radius) &
- & - genericMass
- return
- end function rootMass
-
- double precision function genericRadiusCircularVelocityMaximumNumerical(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic
- use :: Numerical_Comparison, only : Values_Agree
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-6
- class (nodeComponentBasic ), pointer :: basic
- type (rootFinder ) :: finder
-
- call self%solverSet (node)
- finder = rootFinder( &
- & rootFunction =rootCircularVelocityMaximum , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive &
- & )
- ! Isothermal profiles have dVc²/dr=0 everywhere. To handle these profiles, first test if the root function is sufficiently
- ! close to zero at a few points throughout the halo (which it will be for an isothermal profile), and return the circular
- ! velocity at the virial radius if so. Otherwise solve for the radius corresponding to the maximum circular velocity.
- basic => node%basic()
- if ( &
- & Values_Agree( &
- & +rootCircularVelocityMaximum(1.0d+0*self%darkMatterHaloScale_%radiusVirial(node)), &
- & +0.0d0 , &
- & absTol=+toleranceRelative &
- & *basic%mass ( ) &
- & ) &
- & .and. &
- & Values_Agree( &
- & +rootCircularVelocityMaximum(3.0d-1*self%darkMatterHaloScale_%radiusVirial(node)), &
- & +0.0d0 , &
- & absTol=+toleranceRelative &
- & *basic%mass ( ) &
- & ) &
- & .and. &
- & Values_Agree( &
- & +rootCircularVelocityMaximum(1.0d-1*self%darkMatterHaloScale_%radiusVirial(node)), &
- & +0.0d0 , &
- & absTol=+toleranceRelative &
- & *basic%mass ( ) &
- & ) &
- & .and. &
- & Values_Agree( &
- & +rootCircularVelocityMaximum(3.0d-2*self%darkMatterHaloScale_%radiusVirial(node)), &
- & +0.0d0 , &
- & absTol=+toleranceRelative &
- & *basic%mass ( ) &
- & ) &
- & ) then
- genericRadiusCircularVelocityMaximumNumerical= self%darkMatterHaloScale_%radiusVirial(node)
- else
- genericRadiusCircularVelocityMaximumNumerical=finder%find(rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- end if
- call self%solverUnset( )
- return
- end function genericRadiusCircularVelocityMaximumNumerical
-
- double precision function genericCircularVelocityMaximumNumerical(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- genericCircularVelocityMaximumNumerical=self%circularVelocityNumerical(node,self%radiusCircularVelocityMaximumNumerical(node))
- return
- end function genericCircularVelocityMaximumNumerical
-
- double precision function rootCircularVelocityMaximum(radius)
- !!{
- Root function used in finding the radius at which the maximum circular velocity occurs. Since for a spherical profile $V_\mathrm{c}^2(r)=\mathrm{G}M(r)/r$, then
- \begin{equation}
- {\mathrm{d} V_\mathrm{c}^2 \over \mathrm{d} r} = - {\mathrm{G} M(r) \over r^2} + 4 \pi \mathrm{G} \rho(r) r.
- \end{equation}
- Therefore, the peak of the rotation curve satisfies $4 \pi r^3 \rho(r) - M(r)=0$.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- rootCircularVelocityMaximum=+4.0d0 &
- & *Pi &
- & * radius **3 &
- & *solvers(solversCount)%self%density (solvers(solversCount)%node,radius) &
- & -solvers(solversCount)%self%enclosedMass(solvers(solversCount)%node,radius)
- return
- end function rootCircularVelocityMaximum
-
- double precision function genericRadiusFromSpecificAngularMomentumNumerical(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: specificAngularMomentum
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
- type (rootFinder ) :: finder
-
- call self%solverSet (node)
- genericSpecificAngularMomentum = specificAngularMomentum
- finder = rootFinder( &
- & rootFunction =rootSpecificAngularMomentum , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
- & )
- genericRadiusFromSpecificAngularMomentumNumerical=finder%find(rootRange=[0.0d0,self%darkMatterHaloScale_%radiusVirial(node)])
- call self%solverUnset( )
- return
- end function genericRadiusFromSpecificAngularMomentumNumerical
-
- double precision function rootSpecificAngularMomentum(radius)
- !!{
- Root function used in finding the radius enclosing a given specific angular momentum.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- rootSpecificAngularMomentum=+solvers(solversCount)%self%circularVelocityNumerical(solvers(solversCount)%node,radius) &
- & * radius &
- & -genericSpecificAngularMomentum
- return
- end function rootSpecificAngularMomentum
-
- double precision function genericDensityLogSlopeNumerical(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Differentiation, only : differentiator
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusLogarithmicStep=0.1d0
- type (differentiator ) :: differentiator_
-
- call self%solverSet (node)
- differentiator_ = differentiator (genericDensityEvaluate )
- genericDensityLogSlopeNumerical= +differentiator_ %derivative(log(radius) ,radiusLogarithmicStep) &
- & /genericDensityEvaluate (log(radius) )
- call self%solverUnset( )
- return
- end function genericDensityLogSlopeNumerical
-
- double precision function genericDensityEvaluate(radiusLogarithmic)
- !!{
- GSL-callable function to evaluate the density of the dark matter profile.
- !!}
- implicit none
- double precision, intent(in ), value :: radiusLogarithmic
-
- genericDensityEvaluate=solvers(solversCount)%self%density(solvers(solversCount)%node,exp(radiusLogarithmic))
- return
- end function genericDensityEvaluate
-
- subroutine genericSolverSet(self,node)
- !!{
- Set a sub-module scope pointers on a stack to allow recursive calls to functions.
- !!}
- implicit none
- class (darkMatterProfileGeneric), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- type (genericSolver ), allocatable , dimension(:) :: solvers_
- integer :: i
-
- ! Increment the state counter. This is necessary to ensure that this function can be called recursively.
- if (allocated(solvers)) then
- if (solversCount == size(solvers)) then
- call move_alloc(solvers,solvers_)
- allocate(solvers(size(solvers_)+solversIncrement))
- solvers(1:size(solvers_))=solvers_
- do i=1,size(solvers_)
- nullify(solvers_(i)%self)
- nullify(solvers_(i)%node)
- end do
- deallocate(solvers_)
- end if
- else
- allocate(solvers(solversIncrement))
- end if
- solversCount=solversCount+1
- solvers(solversCount)%self => self
- solvers(solversCount)%node => node
- return
- end subroutine genericSolverSet
-
- subroutine genericSolverUnset()
- !!{
- Unset a sub-module scope pointers on the stack.
- !!}
- implicit none
-
- solvers(solversCount)%self => null()
- solvers(solversCount)%node => null()
- solversCount=solversCount-1
- return
- end subroutine genericSolverUnset
-
- subroutine genericCalculationResetGeneric(self,node,uniqueID)
- !!{
- Reset generic profile memoized data.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileGeneric), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%genericLastUniqueID=uniqueID
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine genericCalculationResetGeneric
-
-end module Dark_Matter_Profiles_Generic
diff --git a/source/dark_matter_profiles.heating.impulsive_outflow.F90 b/source/dark_matter_profiles.heating.impulsive_outflow.F90
index 494a0a479d..5c3ce54377 100644
--- a/source/dark_matter_profiles.heating.impulsive_outflow.F90
+++ b/source/dark_matter_profiles.heating.impulsive_outflow.F90
@@ -24,19 +24,12 @@
!![
- A dark matter profile heating model which accounts for heating due to impulsive outflows---i.e. outflows occurring on
- timescales that are small relative to the dynamical time of the halo. The model assumed is that the energy injection is given by
+ A dark matter profile heating model which accounts for heating due to impulsive outflows. The quantity
\begin{equation}
- \dot{\epsilon}(r) = \alpha \frac{\mathrm{G} \dot{M}_\mathrm{outflow}(r)}{r} f\left( \frac{t_\phi}{t_\mathrm{dyn}} \right),
- \end{equation}
- where $\alpha$ is a normalization factor, $t_\phi = M_\mathrm{gas}/\dot{M}_\mathrm{outflow}$ is the timescale for the
- outflow, and $t_\mathrm{dyn} = r_{1/2}/v_{1/2}$ is the dynamical time at the half-mass radius.
-
- In practice, the quantity
- \begin{equation}
- \dot{\epsilon}^\prime = \dot{M}_\mathrm{outflow} f\left( \frac{t_\phi}{t_\mathrm{dyn}} \right),
- \end{equation}
- has been accumulated by the \refClass{nodeOperatorImpulsiveOutflowEnergy} object---radially-dependent factors are then applied here.
+ \dot{\epsilon}^\prime = \dot{M}_\mathrm{outflow} f\left( \frac{t_\phi}{t_\mathrm{dyn}} \right),
+ \end{equation}
+ has been accumulated by the \refClass{nodeOperatorImpulsiveOutflowEnergy} object---radially-dependent factors are then applied
+ in the \refClass{massDistributionHeatingImpulsiveOutflow} object returned from our factory.
!!]
@@ -45,17 +38,10 @@
A dark matter profile heating class which accounts for heating arising from impulsive outflows.
!!}
private
- integer :: energyImpulsiveOutflowDiskID , energyImpulsiveOutflowSpheroidID
- double precision :: impulsiveEnergyFactor
- class (*), pointer :: galacticStructure_ => null()
+ integer :: energyImpulsiveOutflowDiskID, energyImpulsiveOutflowSpheroidID
+ double precision :: impulsiveEnergyFactor
contains
- final :: impulsiveOutflowDestructor
- procedure :: specificEnergy => impulsiveOutflowSpecificEnergy
- procedure :: specificEnergyGradient => impulsiveOutflowSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => impulsiveOutflowSpecificEnergyIsEverywhereZero
- procedure :: deepCopyReset => impulsiveOutflowDeepCopyReset
- procedure :: deepCopy => impulsiveOutflowDeepCopy
- procedure :: deepCopyFinalize => impulsiveOutflowDeepCopyFinalize
+ procedure :: get => impulsiveOutflowGet
end type darkMatterProfileHeatingImpulsiveOutflow
interface darkMatterProfileHeatingImpulsiveOutflow
@@ -72,13 +58,12 @@ function impulsiveOutflowConstructorParameters(parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily impulsiveOutflow} dark matter profile heating scales class which takes a parameter set as input.
!!}
- use :: Functions_Global, only : galacticStructureConstruct_, galacticStructureDestruct_
use :: Input_Parameters, only : inputParameters
implicit none
type (darkMatterProfileHeatingImpulsiveOutflow), target :: self
type (inputParameters ), intent(inout) :: parameters
- class (* ), pointer :: galacticStructure_
double precision :: impulsiveEnergyFactor
+
!![
impulsiveEnergyFactor
@@ -87,25 +72,22 @@ function impulsiveOutflowConstructorParameters(parameters) result(self)
!!]
- call galacticStructureConstruct_(parameters,galacticStructure_)
- self=darkMatterProfileHeatingImpulsiveOutflow(impulsiveEnergyFactor,galacticStructure_)
+ self=darkMatterProfileHeatingImpulsiveOutflow(impulsiveEnergyFactor)
!![
-
+
!!]
- call galacticStructureDestruct_(galacticStructure_)
return
end function impulsiveOutflowConstructorParameters
- function impulsiveOutflowConstructorInternal(impulsiveEnergyFactor,galacticStructure_) result(self)
+ function impulsiveOutflowConstructorInternal(impulsiveEnergyFactor) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily impulsiveOutflow} dark matter profile heating scales class.
!!}
implicit none
- type (darkMatterProfileHeatingImpulsiveOutflow) :: self
- class (* ), intent(in ), target :: galacticStructure_
- double precision , intent(in ) :: impulsiveEnergyFactor
+ type (darkMatterProfileHeatingImpulsiveOutflow) :: self
+ double precision , intent(in ) :: impulsiveEnergyFactor
!![
-
+
!!]
!![
@@ -115,204 +97,34 @@ function impulsiveOutflowConstructorInternal(impulsiveEnergyFactor,galacticStruc
return
end function impulsiveOutflowConstructorInternal
- subroutine impulsiveOutflowDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily impulsiveOutflow} node operator class.
- !!}
- use :: Functions_Global, only : galacticStructureDestruct_
- implicit none
- type(darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
-
- if (associated(self%galacticStructure_)) call galacticStructureDestruct_(self%galacticStructure_)
- return
- end subroutine impulsiveOutflowDestructor
-
- double precision function impulsiveOutflowSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
- use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid, radiusLarge,massTypeDark
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Functions_Global , only : galacticStructureMassEnclosed_
- implicit none
- class (darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: massTotalDisk , massTotalSpheroid , &
- & fractionMassDisk , fractionMassSpheroid
-
- darkMatterProfile => node%darkMatterProfile ( )
- massTotalDisk = galacticStructureMassEnclosed_(self%galacticStructure_,node,radiusLarge,componentType=componentTypeDisk )
- massTotalSpheroid = galacticStructureMassEnclosed_(self%galacticStructure_,node,radiusLarge,componentType=componentTypeSpheroid)
- if (massTotalDisk > 0.0d0) then
- fractionMassDisk =+galacticStructureMassEnclosed_(self%galacticStructure_,node,radius,componentType=componentTypeDisk ) &
- & /massTotalDisk
- else
- fractionMassDisk =+0.0d0
- end if
- if (massTotalSpheroid > 0.0d0) then
- fractionMassSpheroid=+galacticStructureMassEnclosed_(self%galacticStructure_,node,radius,componentType=componentTypeSpheroid) &
- & /massTotalSpheroid
- else
- fractionMassSpheroid=+0.0d0
- end if
- impulsiveOutflowSpecificEnergy = +self %impulsiveEnergyFactor &
- & *gravitationalConstantGalacticus &
- & *( &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowDiskID ) &
- & *fractionMassDisk &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowSpheroidID) &
- & *fractionMassSpheroid &
- & ) &
- & /radius
- return
- end function impulsiveOutflowSpecificEnergy
-
- double precision function impulsiveOutflowSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
- use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid, radiusLarge, coordinateSystemSpherical
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Math , only : Pi
- use :: Functions_Global , only : galacticStructureMassEnclosed_ , galacticStructureDensity_
- implicit none
- class (darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: massTotalDisk , massTotalSpheroid , &
- & fractionMassDisk , fractionMassSpheroid , &
- & fractionDensityDisk , fractionDensitySpheroid
-
- darkMatterProfile => node%darkMatterProfile ( )
- massTotalDisk = galacticStructureMassEnclosed_(self%galacticStructure_,node,radiusLarge,componentType=componentTypeDisk )
- massTotalSpheroid = galacticStructureMassEnclosed_(self%galacticStructure_,node,radiusLarge,componentType=componentTypeSpheroid)
- if (massTotalDisk > 0.0d0) then
- fractionMassDisk =+galacticStructureMassEnclosed_(self%galacticStructure_,node, radius ,componentType=componentTypeDisk ) &
- & /massTotalDisk
- fractionDensityDisk =+galacticStructureDensity_ (self%galacticStructure_,node,[radius,0.0d0,0.0d0],componentType=componentTypeDisk ,coordinateSystem=coordinateSystemSpherical) &
- & /massTotalDisk
- else
- fractionMassDisk =+0.0d0
- fractionDensityDisk =+0.0d0
- end if
- if (massTotalSpheroid > 0.0d0) then
- fractionMassSpheroid =+galacticStructureMassEnclosed_(self%galacticStructure_,node, radius ,componentType=componentTypeSpheroid ) &
- & /massTotalSpheroid
- fractionDensitySpheroid=+galacticStructureDensity_ (self%galacticStructure_,node,[radius,0.0d0,0.0d0],componentType=componentTypeSpheroid,coordinateSystem=coordinateSystemSpherical) &
- & /massTotalSpheroid
- else
- fractionMassSpheroid =+0.0d0
- fractionDensitySpheroid=+0.0d0
- end if
- impulsiveOutflowSpecificEnergyGradient = +self%impulsiveEnergyFactor &
- & *gravitationalConstantGalacticus &
- & *( &
- & +( &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowDiskID ) &
- & *fractionDensityDisk &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowSpheroidID) &
- & *fractionDensitySpheroid &
- & ) &
- & *4.0d0 &
- & *Pi &
- & *radius &
- & -( &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowDiskID ) &
- & *fractionMassDisk &
- & +darkMatterProfile %floatRank0MetaPropertyGet(self%energyImpulsiveOutflowSpheroidID) &
- & *fractionMassSpheroid &
- & ) &
- & /radius**2 &
- & )
- return
- end function impulsiveOutflowSpecificEnergyGradient
-
- logical function impulsiveOutflowSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
+ function impulsiveOutflowGet(self,node) result(massDistributionHeating_)
!!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile
+ use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
+ use :: Mass_Distributions, only : massDistributionHeatingImpulsiveOutflow
implicit none
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeating_
class(darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
class(nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- !$GLC attributes unused :: darkMatterProfileDMO_
-
- darkMatterProfile => node %darkMatterProfile ( )
- impulsiveOutflowSpecificEnergyIsEverywhereZero = darkMatterProfile%floatRank0MetaPropertyGet(self%energyImpulsiveOutflowDiskID ) <= 0.0d0 &
- & .and. &
- & darkMatterProfile%floatRank0MetaPropertyGet(self%energyImpulsiveOutflowSpheroidID) <= 0.0d0
-
- return
- end function impulsiveOutflowSpecificEnergyIsEverywhereZero
-
- subroutine impulsiveOutflowDeepCopyReset(self)
- !!{
- Perform a deep copy reset of the object.
- !!}
- use :: Functions_Global, only : galacticStructureDeepCopyReset_
- implicit none
- class(darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
-
- self%copiedSelf => null()
- if (associated(self%galacticStructure_)) call galacticStructureDeepCopyReset_(self%galacticStructure_)
- return
- end subroutine impulsiveOutflowDeepCopyReset
-
- subroutine impulsiveOutflowDeepCopyFinalize(self)
- !!{
- Finalize a deep reset of the object.
- !!}
- use :: Functions_Global, only : galacticStructureDeepCopyFinalize_
- implicit none
- class(darkMatterProfileHeatingImpulsiveOutflow), intent(inout) :: self
-
- if (associated(self%galacticStructure_)) call galacticStructureDeepCopyFinalize_(self%galacticStructure_)
- return
- end subroutine impulsiveOutflowDeepCopyFinalize
-
- subroutine impulsiveOutflowDeepCopy(self,destination)
- !!{
- Perform a deep copy of the object.
- !!}
- use :: Error , only : Error_Report
- use :: Functions_Global , only : galacticStructureDeepCopy_
-#ifdef OBJECTDEBUG
- use :: Display , only : displayMessage , verbosityLevelSilent
- use :: MPI_Utilities , only : mpiSelf
- use :: Function_Classes , only : debugReporting
- use :: ISO_Varying_String, only : operator(//) , var_str
- use :: String_Handling , only : operator(//)
-#endif
- implicit none
- class(darkMatterProfileHeatingImpulsiveOutflow), intent(inout), target :: self
- class(darkMatterProfileHeatingClass ), intent(inout) :: destination
-
- call self%darkMatterProfileHeatingClass%deepCopy(destination)
- select type (destination)
- type is (darkMatterProfileHeatingImpulsiveOutflow)
- destination%energyImpulsiveOutflowDiskID =self%energyImpulsiveOutflowDiskID
- destination%energyImpulsiveOutflowSpheroidID=self%energyImpulsiveOutflowSpheroidID
- destination%impulsiveEnergyFactor =self%impulsiveEnergyFactor
- nullify(destination%galacticStructure_)
- if (associated(self%galacticStructure_)) then
- allocate(destination%galacticStructure_,mold=self%galacticStructure_)
- call galacticStructureDeepCopy_(self%galacticStructure_,destination%galacticStructure_)
-#ifdef OBJECTDEBUG
- if (debugReporting.and.mpiSelf%isMaster()) call displayMessage(var_str('functionClass[own] (class : ownerName : ownerLoc : objectLoc : sourceLoc): galacticstructure : [destination] : ')//loc(destination)//' : '//loc(destination%galacticStructure_)//' : '//{introspection:location:compact},verbosityLevelSilent)
-#endif
- destination%referenceCount=1
- end if
- class default
- call Error_Report('destination and source types do not match'//{introspection:location})
+
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingImpulsiveOutflow :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingImpulsiveOutflow)
+ darkMatterProfile => node%darkMatterProfile()
+ !![
+
+
+ massDistributionHeatingImpulsiveOutflow( &
+ & energyImpulsiveOutflowDisk =darkMatterProfile%floatRank0MetaPropertyGet(self%energyImpulsiveOutflowDiskID ), &
+ & energyImpulsiveOutflowSpheroid=darkMatterProfile%floatRank0MetaPropertyGet(self%energyImpulsiveOutflowSpheroidID), &
+ & impulsiveEnergyFactor =self %impulsiveEnergyFactor &
+ & )
+
+
+ !!]
end select
return
- end subroutine impulsiveOutflowDeepCopy
+ end function impulsiveOutflowGet
diff --git a/source/dark_matter_profiles.heating.monotonic.F90 b/source/dark_matter_profiles.heating.monotonic.F90
index d242ac98b9..ca43778743 100644
--- a/source/dark_matter_profiles.heating.monotonic.F90
+++ b/source/dark_matter_profiles.heating.monotonic.F90
@@ -23,39 +23,23 @@
A dark matter halo profile heating class which takes another heating source and enforces monotonic heating energy perturbation.
!!}
- use :: Root_Finder, only : rootFinder
-
!![
- A dark matter profile heating model which takes another heating source and enforces monotonic heating energy perturbation.
+
+ A dark matter profile heating model builds \refClass{massDistributionHeatingMonotonic} objects to enforce monotonic heating
+ energy perturbations.
+
!!]
-
type, extends(darkMatterProfileHeatingClass) :: darkMatterProfileHeatingMonotonic
!!{
A dark matter profile heating class which takes another heating source and enforces monotonic heating energy perturbation.
!!}
private
- class (darkMatterProfileHeatingClass), pointer :: darkMatterProfileHeating_ => null()
- type (rootFinder ) :: finder
- double precision :: radiusShellCrossing , energyPerturbationShellCrossing
- integer (kind_int8 ) :: lastUniqueID
+ class(darkMatterProfileHeatingClass), pointer :: darkMatterProfileHeating_ => null()
contains
- !![
-
-
-
-
-
- !!]
- final :: monotonicDestructor
- procedure :: autoHook => monotonicAutoHook
- procedure :: calculationReset => monotonicCalculationReset
- procedure :: specificEnergy => monotonicSpecificEnergy
- procedure :: specificEnergyGradient => monotonicSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => monotonicSpecificEnergyIsEverywhereZero
- procedure :: noShellCrossingIsValid => monotonicNoShellCrossingIsValid
- procedure :: computeRadiusShellCrossing => monotonicComputeRadiusShellCrossing
+ final :: monotonicDestructor
+ procedure :: get => monotonicGet
end type darkMatterProfileHeatingMonotonic
interface darkMatterProfileHeatingMonotonic
@@ -66,12 +50,6 @@
module procedure monotonicConstructorInternal
end interface darkMatterProfileHeatingMonotonic
- ! Global variables used in root solving.
- type (treeNode ), pointer :: node_
- type (darkMatterProfileHeatingMonotonic), pointer :: self_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO__
- !$omp threadprivate(node_,self_,darkMatterProfileDMO__)
-
contains
function monotonicConstructorParameters(parameters) result(self)
@@ -80,9 +58,9 @@ function monotonicConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (darkMatterProfileHeatingMonotonic), target :: self
- type (inputParameters ), intent(inout) :: parameters
- class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_
+ type (darkMatterProfileHeatingMonotonic), target :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_
!![
@@ -100,276 +78,54 @@ function monotonicConstructorInternal(darkMatterProfileHeating_) result(self)
Internal constructor for the ``monotonic'' dark matter profile heating class.
!!}
implicit none
- type (darkMatterProfileHeatingMonotonic) :: self
- class (darkMatterProfileHeatingClass ), target, intent(in ) :: darkMatterProfileHeating_
- double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
+ type (darkMatterProfileHeatingMonotonic) :: self
+ class(darkMatterProfileHeatingClass ), target, intent(in ) :: darkMatterProfileHeating_
!![
!!]
- self%radiusShellCrossing =-1.0d0
- self%energyPerturbationShellCrossing=-1.0d0
- self%lastUniqueID =-1_kind_int8
- self%finder =rootFinder( &
- & rootFunction =monotonicRadiusShellCrossingRoot, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
return
end function monotonicConstructorInternal
- subroutine monotonicAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileHeatingMonotonic), intent(inout) :: self
-
- call calculationResetEvent%attach(self,monotonicCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileHeatingMonotonic')
- return
- end subroutine monotonicAutoHook
-
- subroutine monotonicCalculationReset(self,node,uniqueID)
- !!{
- Reset the stored shell crossing radius.
- !!}
- implicit none
- class (darkMatterProfileHeatingMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
-
- self%radiusShellCrossing =-1.0d0
- self%energyPerturbationShellCrossing=-1.0d0
- self%lastUniqueID =uniqueID
- return
- end subroutine monotonicCalculationReset
-
subroutine monotonicDestructor(self)
!!{
- Destructor for the ``monotonic'' dark matter profile heating class.
+ Destructor for the {\normalfont \ttfamily monotonic} dark matter profile heating class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileHeatingMonotonic), intent(inout) :: self
!![
!!]
- if (calculationResetEvent%isAttached(self,monotonicCalculationReset)) call calculationResetEvent%detach(self,monotonicCalculationReset)
return
end subroutine monotonicDestructor
- double precision function monotonicSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
+ function monotonicGet(self,node) result(massDistributionHeating_)
!!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileHeatingMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- integer (kind_int8 ) :: uniqueID
-
- if (self%noShellCrossingIsValid(node,radius,darkMatterProfileDMO_)) then
- monotonicSpecificEnergy=self%darkMatterProfileHeating_%specificEnergy( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- else
- uniqueID=node%uniqueID()
- if (uniqueID /= self%lastUniqueID) call self%calculationReset(node,uniqueID)
- if (self%energyPerturbationShellCrossing < 0.0d0) then
- call self%computeRadiusShellCrossing ( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- end if
- monotonicSpecificEnergy=+self%energyPerturbationShellCrossing &
- & *0.5d0 &
- & *gravitationalConstantGalacticus &
- & *darkMatterProfileDMO_ %enclosedMass(node,radius) &
- & /radius
- end if
- return
- end function monotonicSpecificEnergy
-
- double precision function monotonicSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- class (darkMatterProfileHeatingMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- integer (kind_int8 ) :: uniqueID
-
- if (self%noShellCrossingIsValid(node,radius,darkMatterProfileDMO_)) then
- monotonicSpecificEnergyGradient=self%darkMatterProfileHeating_%specificEnergyGradient( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- else
- uniqueID=node%uniqueID()
- if (uniqueID /= self%lastUniqueID) call self%calculationReset(node,uniqueID)
- if (self%energyPerturbationShellCrossing < 0.0d0) then
- call self%computeRadiusShellCrossing ( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- end if
- monotonicSpecificEnergyGradient=+self%energyPerturbationShellCrossing &
- & *0.5d0 &
- & *gravitationalConstantGalacticus &
- & *( &
- & +4.0d0 &
- & *Pi &
- & * radius &
- & *darkMatterProfileDMO_%density (node,radius) &
- & -darkMatterProfileDMO_%enclosedMass(node,radius) &
- & /radius**2 &
- & )
- end if
- return
- end function monotonicSpecificEnergyGradient
-
- logical function monotonicSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
- !!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
+ use :: Mass_Distributions, only : massDistributionHeatingMonotonic
implicit none
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeating_
class(darkMatterProfileHeatingMonotonic), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
-
- monotonicSpecificEnergyIsEverywhereZero=self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,darkMatterProfileDMO_)
- return
- end function monotonicSpecificEnergyIsEverywhereZero
-
- logical function monotonicNoShellCrossingIsValid(self,node,radius,darkMatterProfileDMO_)
- !!{
- Determines if the no shell crossing assumption is valid.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileHeatingMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- double precision :: massEnclosed
-
- massEnclosed = darkMatterProfileDMO_%enclosedMass(node,radius)
- if (massEnclosed > 0.0d0) then
- monotonicNoShellCrossingIsValid=+self%darkMatterProfileHeating_%specificEnergyGradient( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & ) &
- & * radius &
- & +self%darkMatterProfileHeating_%specificEnergy ( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & ) &
- & *( &
- & +1.0d0 &
- & -4.0d0 &
- & *Pi &
- & * radius**3 &
- & *darkMatterProfileDMO_ %density ( &
- & node , &
- & radius &
- & ) &
- & /massEnclosed &
- & ) &
- & >=0.0d0
- else
- monotonicNoShellCrossingIsValid=.true.
- end if
- return
- end function monotonicNoShellCrossingIsValid
-
- subroutine monotonicComputeRadiusShellCrossing(self,node,radius,darkMatterProfileDMO_)
- !!{
- Determines if the no shell crossing assumption is valid.
- !!}
- use :: Root_Finder , only : rangeExpandMultiplicative , rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileHeatingMonotonic), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- class (darkMatterProfileDMOClass ), intent(inout), target :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- integer (kind_int8 ) :: uniqueID
-
- uniqueID=node%uniqueID()
- if (uniqueID /= self%lastUniqueID) call self%calculationReset(node,uniqueID)
- if (self%energyPerturbationShellCrossing < 0.0d0) then
- self_ => self
- node_ => node
- darkMatterProfileDMO__ => darkMatterProfileDMO_
- call self%finder%rangeExpand( &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =1.0d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- self%radiusShellCrossing =+self%finder%find(rootGuess=radius)
- self%energyPerturbationShellCrossing =+self%darkMatterProfileHeating_%specificEnergy(node,self%radiusShellCrossing,darkMatterProfileDMO_) &
- & /( &
- & +0.5d0 &
- & *gravitationalConstantGalacticus &
- & *darkMatterProfileDMO_ %enclosedMass (node,self%radiusShellCrossing ) &
- & / self%radiusShellCrossing &
- & )
- end if
- return
- end subroutine monotonicComputeRadiusShellCrossing
-
- double precision function monotonicRadiusShellCrossingRoot(radius)
- !!{
- Root function used in finding the radius where shell crossing happens.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
- double precision :: massEnclosed
-
- massEnclosed = darkMatterProfileDMO__%enclosedMass(node_,radius)
- if (massEnclosed > 0.0d0) then
- monotonicRadiusShellCrossingRoot=+self_%darkMatterProfileHeating_%specificEnergyGradient( &
- & node_ , &
- & radius , &
- & darkMatterProfileDMO__ &
- & ) &
- & * radius &
- & +self_%darkMatterProfileHeating_%specificEnergy ( &
- & node_ , &
- & radius , &
- & darkMatterProfileDMO__ &
- & ) &
- & *( &
- & +1.0d0 &
- & -4.0d0 &
- & *Pi &
- & * radius**3 &
- & *darkMatterProfileDMO__ %density ( &
- & node_ , &
- & radius &
- & ) &
- & /massEnclosed &
- & )
- else
- monotonicRadiusShellCrossingRoot=0.0d0
- end if
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeatingDecorated
+
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingMonotonic :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingMonotonic)
+ massDistributionHeatingDecorated => self%darkMatterProfileHeating_%get(node)
+ !![
+
+
+ massDistributionHeatingMonotonic( &
+ & massDistributionHeating_=massDistributionHeatingDecorated &
+ & )
+
+
+
+ !!]
+ end select
return
- end function monotonicRadiusShellCrossingRoot
+ end function monotonicGet
diff --git a/source/dark_matter_profiles.heating.null.F90 b/source/dark_matter_profiles.heating.null.F90
index 7e1fcce015..fa651c5cce 100644
--- a/source/dark_matter_profiles.heating.null.F90
+++ b/source/dark_matter_profiles.heating.null.F90
@@ -23,7 +23,9 @@
!![
- A dark matter profile heating model in which the heating is always zero.
+
+ A dark matter profile heating model which constructs \refClass{massDistributionHeatingNull} objects to provide zero heating.
+
!!]
@@ -33,9 +35,7 @@
!!}
private
contains
- procedure :: specificEnergy => nullSpecificEnergy
- procedure :: specificEnergyGradient => nullSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => nullSpecificEnergyIsEverywhereZero
+ procedure :: get => nullGet
end type darkMatterProfileHeatingNull
interface darkMatterProfileHeatingNull
@@ -63,46 +63,28 @@ function nullConstructorParameters(parameters) result(self)
return
end function nullConstructorParameters
- double precision function nullSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
+ function nullGet(self,node) result(massDistributionHeating_)
!!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class (darkMatterProfileHeatingNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius, darkMatterProfileDMO_
-
- nullSpecificEnergy=0.0d0
- return
- end function nullSpecificEnergy
-
- double precision function nullSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class (darkMatterProfileHeatingNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, darkMatterProfileDMO_, radius
-
- nullSpecificEnergyGradient=0.0d0
- return
- end function nullSpecificEnergyGradient
-
- logical function nullSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
- !!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
+ use :: Mass_Distributions, only : massDistributionHeatingNull
implicit none
+ class(massDistributionHeatingClass), pointer :: massDistributionHeating_
class(darkMatterProfileHeatingNull), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- !$GLC attributes unused :: self, node, darkMatterProfileDMO_
-
- nullSpecificEnergyIsEverywhereZero=.true.
+
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingNull :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingNull)
+ !![
+
+
+ massDistributionHeatingNull()
+
+
+ !!]
+ end select
return
- end function nullSpecificEnergyIsEverywhereZero
+ end function nullGet
+
diff --git a/source/dark_matter_profiles.heating.summation.F90 b/source/dark_matter_profiles.heating.summation.F90
index 7d87090bf0..c34a66daa1 100644
--- a/source/dark_matter_profiles.heating.summation.F90
+++ b/source/dark_matter_profiles.heating.summation.F90
@@ -40,10 +40,8 @@
private
type(heatSourceList), pointer :: heatSources => null()
contains
- final :: summationDestructor
- procedure :: specificEnergy => summationSpecificEnergy
- procedure :: specificEnergyGradient => summationSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => summationSpecificEnergyIsEverywhereZero
+ final :: summationDestructor
+ procedure :: get => summationGet
end type darkMatterProfileHeatingSummation
interface darkMatterProfileHeatingSummation
@@ -126,74 +124,45 @@ subroutine summationDestructor(self)
return
end subroutine summationDestructor
- double precision function summationSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
+ function summationGet(self,node) result(massDistributionHeating_)
!!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class (darkMatterProfileHeatingSummation), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- type (heatSourceList ), pointer :: heatSource
-
- summationSpecificEnergy = 0.0d0
- heatSource => self%heatSources
- do while (associated(heatSource))
- summationSpecificEnergy=+summationSpecificEnergy &
- & +heatSource%heatSource%specificEnergy( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- heatSource => heatSource%next
- end do
- return
- end function summationSpecificEnergy
-
- double precision function summationSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class (darkMatterProfileHeatingSummation), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- type (heatSourceList ), pointer :: heatSource
-
- summationSpecificEnergyGradient = 0.0d0
- heatSource => self%heatSources
- do while (associated(heatSource))
- summationSpecificEnergyGradient=+summationSpecificEnergyGradient &
- & +heatSource%heatSource%specificEnergyGradient( &
- & node , &
- & radius , &
- & darkMatterProfileDMO_ &
- & )
- heatSource => heatSource%next
- end do
- return
- end function summationSpecificEnergyGradient
-
- logical function summationSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
- !!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
+ use :: Mass_Distributions, only : massDistributionHeatingSummation, massDistributionHeatingList
implicit none
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeating_
class(darkMatterProfileHeatingSummation), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
+ type (massDistributionHeatingList ), pointer :: massDistributionHeatings, massDistributionHeating__
type (heatSourceList ), pointer :: heatSource
- summationSpecificEnergyIsEverywhereZero = .true.
- heatSource => self%heatSources
- do while (associated(heatSource))
- if (.not.heatSource%heatSource%specificEnergyIsEverywhereZero(node,darkMatterProfileDMO_)) then
- summationSpecificEnergyIsEverywhereZero=.false.
- exit
- end if
- heatSource => heatSource%next
- end do
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingSummation :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingSummation)
+ heatSource => self%heatSources
+ massDistributionHeating__ => null()
+ massDistributionHeatings => null()
+ do while (associated(heatSource))
+ if (associated(massDistributionHeatings)) then
+ allocate(massDistributionHeating__%next)
+ massDistributionHeating__ => massDistributionHeating__%next
+ else
+ allocate(massDistributionHeatings )
+ massDistributionHeating__ => massDistributionHeatings
+ end if
+ massDistributionHeating__%massDistributionHeating_ => heatSource%heatSource%get(node)
+ heatSource => heatSource%next
+ end do
+ !![
+
+
+ massDistributionHeatingSummation( &
+ & massDistributionHeatings=massDistributionHeatings &
+ & )
+
+
+ !!]
+ end select
return
- end function summationSpecificEnergyIsEverywhereZero
+ end function summationGet
diff --git a/source/dark_matter_profiles.heating.tidal.F90 b/source/dark_matter_profiles.heating.tidal.F90
index 6cf02e7c48..a6c5d4a7f0 100644
--- a/source/dark_matter_profiles.heating.tidal.F90
+++ b/source/dark_matter_profiles.heating.tidal.F90
@@ -21,25 +21,11 @@
A dark matter halo profile heating class which accounts for heating from tidal shocking.
!!}
- use :: Kind_Numbers, only : kind_int8
-
!![
- A dark matter profile heating model which accounts for heating due to tidal shocking. The model follows the general
- approach of \cite{gnedin_tidal_1999}. The change in the specific energy of particles at radius $r$ in a halo is given by
- $\Delta \epsilon = \Delta \epsilon_1 + \Delta \epsilon_2$, where $\Delta \epsilon_1$, and $\Delta \epsilon_2$ are the first
- and second order perturbations respectively. The first order term is given by $\Delta \epsilon_1 = Q r^2$ where $Q$ is the
- tidal tensor integrated along the orbital path (see, for example, \citealt{taylor_dynamics_2001}), while the second order
- term is given by $\Delta \epsilon_2 = (2/3) f \sigma_\mathrm{rms} (1+\chi_\mathrm{r,v}) \sqrt{\Delta \epsilon_1}$
- \citep[][eqn.~20, see also \protect\citealt{gnedin_self-consistent_1999}; eqn.~18a,b]{gnedin_tidal_1999}. For the particle
- velocity dispersion, $v_\mathrm{rms}$, we use $\sqrt{3} \sigma_\mathrm{r}(r)$, the radial velocity dispersion in the dark
- matter profile scaled to the total velocity dispersion assuming an isotropic velocity distribution. The position-velocity
- correlation function, $\chi_\mathrm{r,v}$, is taken to be a constant given by the parameter {\normalfont \ttfamily
- [correlationVelocityRadius]}. The coefficient, $f=${\normalfont \ttfamily [coefficientSecondOrder]} is introduced to allow
- some freedom to adjust the contribution of the second order term. It is degenerate with the value of $\chi_\mathrm{r,v}$
- but is introduced to allow for possible future promotion of $\chi_\mathrm{r,v}$ from a constant to a function of the dark
- matter profile potential \citep[see, for example,][appendix~B]{gnedin_self-consistent_1999}.
+ A dark matter profile heating class that constructs \refClass{massDistributionHeatingTidal} objects to compute heating due to
+ tidal shocks.
!!]
@@ -48,26 +34,10 @@ but is introduced to allow for possible future promotion of $\chi_\mathrm{r,v}$
A dark matter profile heating class which accounts for heating due to tidal shocking.
!!}
private
- double precision :: specificEnergyOverRadiusSquared_, specificEnergyOverRadiusSquaredParent_, &
- & correlationVelocityRadius , coefficientSecondOrder0 , &
- & coefficientSecondOrder1 , coefficientSecondOrder2
- integer (kind_int8) :: lastUniqueID , parentUniqueID
- contains
- !![
-
-
-
-
-
- !!]
- final :: tidalDestructor
- procedure :: autoHook => tidalAutoHook
- procedure :: calculationReset => tidalCalculationReset
- procedure :: specificEnergy => tidalSpecificEnergy
- procedure :: specificEnergyGradient => tidalSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => tidalSpecificEnergyIsEverywhereZero
- procedure :: specificEnergyOverRadiusSquared => tidalSpecificEnergyOverRadiusSquared
- procedure :: specificEnergyTerms => tidalSpecificEnergyTerms
+ double precision :: correlationVelocityRadius, coefficientSecondOrder0, &
+ & coefficientSecondOrder1 , coefficientSecondOrder2
+ contains
+ procedure :: get => tidalGet
end type darkMatterProfileHeatingTidal
interface darkMatterProfileHeatingTidal
@@ -136,205 +106,45 @@ function tidalConstructorInternal(coefficientSecondOrder0,coefficientSecondOrder
!!]
- self%specificEnergyOverRadiusSquared_ =-1.0d0
- self%specificEnergyOverRadiusSquaredParent_=-1.0d0
- self%lastUniqueID =-1_kind_int8
- self%parentUniqueID =-1_kind_int8
return
end function tidalConstructorInternal
- subroutine tidalAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileHeatingTidal), intent(inout) :: self
-
- call calculationResetEvent%attach(self,tidalCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileHeatingTidal')
- return
- end subroutine tidalAutoHook
-
- subroutine tidalCalculationReset(self,node,uniqueID)
- !!{
- Reset the stored tidal radii.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileHeatingTidal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
-
- self %specificEnergyOverRadiusSquared_ =-1.0d0
- self %specificEnergyOverRadiusSquaredParent_=-1.0d0
- self %lastUniqueID = uniqueID
- if (associated(node%parent)) then
- self%parentUniqueID =node%parent%uniqueID()
- else
- self%parentUniqueID =-1_kind_int8
- end if
- return
- end subroutine tidalCalculationReset
-
- subroutine tidalDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily tidal} dark matter profile heating class.
- !!}
- use :: Events_Hooks, only : calculationResetEvent
- implicit none
- type(darkMatterProfileHeatingTidal), intent(inout) :: self
-
- if (calculationResetEvent%isAttached(self,tidalCalculationReset)) call calculationResetEvent%detach(self,tidalCalculationReset)
- return
- end subroutine tidalDestructor
-
- double precision function tidalSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class (darkMatterProfileHeatingTidal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
-
- call self%specificEnergyTerms(node,radius,energyPerturbationFirstOrder,energyPerturbationSecondOrder,darkMatterProfileDMO_)
- tidalSpecificEnergy=+energyPerturbationFirstOrder &
- & +energyPerturbationSecondOrder
- return
- end function tidalSpecificEnergy
-
- double precision function tidalSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileHeatingTidal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
-
-
- if (radius > 0.0d0) then
- call self%specificEnergyTerms(node,radius,energyPerturbationFirstOrder,energyPerturbationSecondOrder,darkMatterProfileDMO_)
- if (energyPerturbationSecondOrder > 0.0d0) then
- tidalSpecificEnergyGradient=+( &
- & +energyPerturbationFirstOrder * 2.0d0 & ! dlog[r² ]/dlog(r) term
- & +energyPerturbationSecondOrder*( &
- & -0.5d0 & ! ⎧ dlog[σ_r(r)]/dlog[r] term
- & *darkMatterProfileDMO_%densityLogSlope (node,radius) & ! ⎥
- & -0.5d0 & ! ⎥ Assumes the Jeans equation in
- & *gravitationalConstantGalacticus & ! ⎥ spherical symmetry with anisotropy
- & *darkMatterProfileDMO_%enclosedMass (node,radius) & ! ⎥ parameter β=0. Would be better to
- & / radius & ! ⎥ have this provided by the
- & /darkMatterProfileDMO_%radialVelocityDispersion(node,radius)**2 & ! ⎩ darkMatterProfileDMO class.
- & +1.0d0 & ! dlog[r ]/dlog(r) term
- & ) &
- & ) &
- & /radius
- else
- tidalSpecificEnergyGradient=+ energyPerturbationFirstOrder * 2.0d0 & ! dlog[r² ]/dlog(r) term
- & /radius
- end if
- else
- tidalSpecificEnergyGradient=+0.0d0
- end if
- return
- end function tidalSpecificEnergyGradient
-
- subroutine tidalSpecificEnergyTerms(self,node,radius,energyPerturbationFirstOrder,energyPerturbationSecondOrder,darkMatterProfileDMO_)
+ function tidalGet(self,node) result(massDistributionHeating_)
!!{
- Compute the first and second order perturbations to the energy.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galacticus_Nodes , only : nodeComponentSatellite
+ use :: Mass_Distributions, only : massDistributionHeatingTidal
implicit none
+ class (massDistributionHeatingClass ), pointer :: massDistributionHeating_
class (darkMatterProfileHeatingTidal), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , intent( out) :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision :: coefficientSecondOrder, densityLogSlope
-
- energyPerturbationFirstOrder=+self%specificEnergyOverRadiusSquared(node) &
- & *radius **2
- if ( &
- & self%coefficientSecondOrder0 /= 0.0d0 &
- & .or. &
- & self%coefficientSecondOrder1 /= 0.0d0 &
- & .or. &
- & self%coefficientSecondOrder2 /= 0.0d0 &
- & ) then
- ! Compute the coefficient for the second order term.
- densityLogSlope =+darkMatterProfileDMO_%densityLogSlope(node,radius)
- coefficientSecondOrder=+self%coefficientSecondOrder0 &
- & +self%coefficientSecondOrder1*densityLogSlope &
- & +self%coefficientSecondOrder2*densityLogSlope**2
- ! Compute the second order energy perturbation.
- energyPerturbationSecondOrder=+sqrt(2.0d0) &
- & *coefficientSecondOrder &
- & *( &
- & +1.0d0 &
- & +self%correlationVelocityRadius &
- & ) &
- & *sqrt(energyPerturbationFirstOrder) &
- & *darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else
- energyPerturbationSecondOrder=+0.0d0
- end if
- return
- end subroutine tidalSpecificEnergyTerms
-
- logical function tidalSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
- !!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileHeatingTidal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- !$GLC attributes unused :: darkMatterProfileDMO_
-
- tidalSpecificEnergyIsEverywhereZero=self%specificEnergyOverRadiusSquared(node) <= 0.0d0
+ class (nodeComponentSatellite ), pointer :: satellite
+ double precision :: heatSpecificNormalized
+
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingTidal :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingTidal)
+ satellite => node %satellite ()
+ heatSpecificNormalized = max( &
+ & +0.0d0 , &
+ & +satellite%tidalHeatingNormalized() &
+ & )
+ !![
+
+
+ massDistributionHeatingTidal( &
+ & heatSpecificNormalized =heatSpecificNormalized , &
+ & coefficientSecondOrder0 =self%coefficientSecondOrder0 , &
+ & coefficientSecondOrder1 =self%coefficientSecondOrder1 , &
+ & coefficientSecondOrder2 =self%coefficientSecondOrder2 , &
+ & correlationVelocityRadius=self%correlationVelocityRadius &
+ & )
+
+
+ !!]
+ end select
return
- end function tidalSpecificEnergyIsEverywhereZero
-
- double precision function tidalSpecificEnergyOverRadiusSquared(self,node)
- !!{
- Compute $Q = E / r^2$.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentSatellite, treeNode
- implicit none
- class (darkMatterProfileHeatingTidal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentSatellite ), pointer :: satellite
- integer(kind_int8 ) :: uniqueID
+ end function tidalGet
- uniqueID=node%uniqueID()
- if ( &
- & uniqueID /= self%parentUniqueID &
- & .and. &
- & uniqueID /= self%lastUniqueID &
- & ) call self%calculationReset(node,uniqueID)
- if (uniqueID == self%parentUniqueID) then
- if (self%specificEnergyOverRadiusSquaredParent_ < 0.0d0) then
- satellite => node %satellite ()
- self%specificEnergyOverRadiusSquaredParent_ = max( &
- & +0.0d0 , &
- & +satellite%tidalHeatingNormalized() &
- & )
- end if
- tidalSpecificEnergyOverRadiusSquared=self%specificEnergyOverRadiusSquaredParent_
- else
- if (self%specificEnergyOverRadiusSquared_ < 0.0d0) then
- satellite => node %satellite ()
- self%specificEnergyOverRadiusSquared_ = max( &
- & +0.0d0 , &
- & +satellite%tidalHeatingNormalized() &
- & )
- end if
- tidalSpecificEnergyOverRadiusSquared=self%specificEnergyOverRadiusSquared_
- end if
- return
- end function tidalSpecificEnergyOverRadiusSquared
diff --git a/source/dark_matter_profiles.heating.two_body_relaxation.F90 b/source/dark_matter_profiles.heating.two_body_relaxation.F90
index c0673ea864..839d5cf187 100644
--- a/source/dark_matter_profiles.heating.two_body_relaxation.F90
+++ b/source/dark_matter_profiles.heating.two_body_relaxation.F90
@@ -23,7 +23,10 @@
!![
- A dark matter profile heating model which computes heating due to two-body relaxation.
+
+ A dark matter profile heating class which returns a \refClass{massDistributionHeatingTwoBodyRelaxation} objects to compute
+ heating due to two-body relaxation.
+
!!]
@@ -35,9 +38,7 @@
double precision :: massParticle, lengthSoftening, &
& timeStart , efficiency
contains
- procedure :: specificEnergy => twoBodyRelaxationSpecificEnergy
- procedure :: specificEnergyGradient => twoBodyRelaxationSpecificEnergyGradient
- procedure :: specificEnergyIsEverywhereZero => twoBodyRelaxationSpecificEnergyIsEverywhereZero
+ procedure :: get => twoBodyRelaxationGet
end type darkMatterProfileHeatingTwoBodyRelaxation
interface darkMatterProfileHeatingTwoBodyRelaxation
@@ -105,154 +106,36 @@ function twoBodyRelaxationConstructorInternal(massParticle,lengthSoftening,timeS
return
end function twoBodyRelaxationConstructorInternal
- double precision function twoBodyRelaxationSpecificEnergy(self,node,radius,darkMatterProfileDMO_)
+ function twoBodyRelaxationGet(self,node) result(massDistributionHeating_)
!!{
- Returns the specific energy of heating in the given {\normalfont \ttfamily node}. The assumption here is that the mean
- fractional change in energy for a particle per crossing time is $8 \log \Lambda / N$ where $N$ is the number of particles
- within radius $r=${\normalfont \ttfamily radius}. The crossing time is approximated by $r/V(r)$ where $V(r)$ is the
- circular velocity at $r$. The Coulomb logarithm is given by $\log\Lambda=\hbox{max}(\epsilon,b_{90})$ where $\epsilon$ is
- the softening length, $b_{90}=2\mathrm{G}m_\mathrm{p}/V^2(r)$, and $m_\mathrm{p}$ is the particle mass. Finally, the
- specific energy is assumed to be $\sigma^2(r)/2\approx V^2(r)/4$.
+ Return the dark matter mass distribution heating for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec, gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions, only : massDistributionHeatingTwoBodyRelaxation
implicit none
+ class (massDistributionHeatingClass ), pointer :: massDistributionHeating_
class (darkMatterProfileHeatingTwoBodyRelaxation), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
class (nodeComponentBasic ), pointer :: basic
- double precision :: particleCount , velocity , &
- & logarithmCoulomb , impactParameterCritical
-
- basic => node%basic()
- if (basic%time() > self%timeStart) then
- velocity =+darkMatterProfileDMO_ %circularVelocity(node,radius)
- impactParameterCritical =+2.0d0 &
- & *gravitationalConstantGalacticus &
- & *self %massParticle &
- & /velocity **2
- logarithmCoulomb =+0.5d0 &
- & *log( &
- & +1.0d0 &
- & +( &
- & +radius &
- & /max( &
- & self %lengthSoftening , &
- & impactParameterCritical &
- & ) &
- & ) **2 &
- & )
- particleCount =+darkMatterProfileDMO_ %enclosedMass (node,radius) &
- & /self %massParticle
- twoBodyRelaxationSpecificEnergy=+2.0d0 &
- & *self %efficiency &
- & *logarithmCoulomb &
- & *( &
- & +basic %time ( ) &
- & -self %timeStart &
- & ) &
- & *velocity **3 &
- & /radius &
- & /particleCount &
- & *kilo &
- & *gigaYear &
- & /megaParsec
- else
- twoBodyRelaxationSpecificEnergy=+0.0d0
- end if
- return
- end function twoBodyRelaxationSpecificEnergy
-
- double precision function twoBodyRelaxationSpecificEnergyGradient(self,node,radius,darkMatterProfileDMO_)
- !!{
- Returns the gradient of the specific energy of heating in the given {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileHeatingTwoBodyRelaxation), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- double precision :: particleCount , velocity , &
- & logarithmCoulomb , impactParameterCritical, &
- & gradientCoulomb
-
- basic => node%basic()
- if (basic%time() > self%timeStart) then
- velocity =+darkMatterProfileDMO_ %circularVelocity(node,radius)
- impactParameterCritical =+2.0d0 &
- & *gravitationalConstantGalacticus &
- & *self %massParticle &
- & /velocity **2
- logarithmCoulomb =+0.5d0 &
- & *log( &
- & +1.0d0 &
- & +( &
- & +radius &
- & /max( &
- & self %lengthSoftening , &
- & impactParameterCritical &
- & ) &
- & ) **2 &
- & )
- particleCount =+darkMatterProfileDMO_ %enclosedMass (node,radius) &
- & /self %massParticle
- if (self%lengthSoftening > impactParameterCritical) then
- gradientCoulomb=+radius &
- & /self %lengthSoftening
- else
- gradientCoulomb=+2.0d0 &
- & *radius &
- & /impactParameterCritical &
- & *( &
- & -1.0d0 &
- & +8.0d0 &
- & *Pi &
- & *gravitationalConstantGalacticus &
- & *radius **2 &
- & *darkMatterProfileDMO_ %density (node,radius) &
- & /velocity **2 &
- & )
- end if
- twoBodyRelaxationSpecificEnergyGradient=+self %specificEnergy(node,radius,darkMatterProfileDMO_) &
- & / radius &
- & *( &
- & -2.5d0 &
- & +6.0d0 &
- & *Pi &
- & *gravitationalConstantGalacticus &
- & *darkMatterProfileDMO_ %density (node,radius ) &
- & *radius **2 &
- & /velocity **2 &
- & - radius &
- & * gradientCoulomb &
- & / logarithmCoulomb &
- & *sqrt(exp(2.0d0*logarithmCoulomb)-1.0d0) &
- & / exp(2.0d0*logarithmCoulomb) &
- & )
- else
- twoBodyRelaxationSpecificEnergyGradient=0.0d0
- end if
- return
- end function twoBodyRelaxationSpecificEnergyGradient
-
- logical function twoBodyRelaxationSpecificEnergyIsEverywhereZero(self,node,darkMatterProfileDMO_)
- !!{
- Returns true if the specific energy is everywhere zero in the given {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, treeNode
- implicit none
- class(darkMatterProfileHeatingTwoBodyRelaxation), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass ), intent(inout) :: darkMatterProfileDMO_
- class(nodeComponentBasic ), pointer :: basic
- !$GLC attributes unused :: self, darkMatterProfileDMO_
-
- basic => node %basic()
- twoBodyRelaxationSpecificEnergyIsEverywhereZero = basic%time () <= self%timeStart
+
+ ! Create the mass distribution.
+ allocate(massDistributionHeatingTwoBodyRelaxation :: massDistributionHeating_)
+ select type(massDistributionHeating_)
+ type is (massDistributionHeatingTwoBodyRelaxation)
+ basic => node%basic()
+ !![
+
+
+ massDistributionHeatingTwoBodyRelaxation( &
+ & massParticle =+self %massParticle , &
+ & lengthSoftening=+self %lengthSoftening , &
+ & timeRelaxing =+basic%time () &
+ & -self %timeStart , &
+ & efficiency =+self %efficiency &
+ & )
+
+
+ !!]
+ end select
return
- end function twoBodyRelaxationSpecificEnergyIsEverywhereZero
+ end function twoBodyRelaxationGet
diff --git a/source/dark_matter_profiles.structure.concentration.Bullock2001.F90 b/source/dark_matter_profiles.structure.concentration.Bullock2001.F90
index 21245b6d19..4a74e5a808 100644
--- a/source/dark_matter_profiles.structure.concentration.Bullock2001.F90
+++ b/source/dark_matter_profiles.structure.concentration.Bullock2001.F90
@@ -24,7 +24,6 @@
use :: Cosmological_Density_Field, only : cosmologicalMassVarianceClass, criticalOverdensityClass
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass , darkMatterProfileDMONFW
use :: Virial_Density_Contrast , only : virialDensityContrastClass , virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
!![
@@ -48,7 +47,6 @@
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
type (virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt), pointer :: virialDensityContrastDefinition_ => null()
type (darkMatterProfileDMONFW ), pointer :: darkMatterProfileDMODefinition_ => null()
double precision :: F , K
@@ -83,7 +81,6 @@ function bullock2001ConstructorParameters(parameters) result(self)
class (criticalOverdensityClass ), pointer :: criticalOverdensity_
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
double precision :: F , K
! Check and read parameters.
@@ -107,9 +104,8 @@ function bullock2001ConstructorParameters(parameters) result(self)
-
!!]
- self=darkMatterProfileConcentrationBullock2001(F,K,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_,darkMatterProfileDMO_)
+ self=darkMatterProfileConcentrationBullock2001(F,K,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_)
!![
@@ -117,29 +113,27 @@ function bullock2001ConstructorParameters(parameters) result(self)
-
!!]
return
end function bullock2001ConstructorParameters
- function bullock2001ConstructorInternal(F,K,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_,darkMatterProfileDMO_) result(self)
+ function bullock2001ConstructorInternal(F,K,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_) result(self)
!!{
Constructor for the {\normalfont \ttfamily bullock2001} dark matter halo profile
concentration class.
!!}
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleVirialDensityContrastDefinition
implicit none
- type (darkMatterProfileConcentrationBullock2001 ) :: self
- double precision , intent(in ) :: F , K
- class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
- class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
- class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ type (darkMatterProfileConcentrationBullock2001 ) :: self
+ double precision , intent(in ) :: F , K
+ class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
+ class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
type (darkMatterHaloScaleVirialDensityContrastDefinition) , pointer :: darkMatterHaloScaleDefinition_
!![
-
+
!!]
allocate(self%darkMatterProfileDMODefinition_ )
@@ -189,7 +183,6 @@ subroutine bullock2001Destructor(self)
-
!!]
@@ -226,7 +219,6 @@ double precision function bullock2001Concentration(self,node)
& densityContrast , &
& cosmologyParameters_ =self %cosmologyParameters_ , &
& cosmologyFunctions_ =self %cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self %darkMatterProfileDMO_ , &
& virialDensityContrast_=self %virialDensityContrast_ &
& )
massHaloFormation = +self%F*massHalo
diff --git a/source/dark_matter_profiles.structure.mass_definitions.F90 b/source/dark_matter_profiles.structure.mass_definitions.F90
index 4e73aeac79..3213c802ba 100644
--- a/source/dark_matter_profiles.structure.mass_definitions.F90
+++ b/source/dark_matter_profiles.structure.mass_definitions.F90
@@ -30,19 +30,20 @@ module Dark_Matter_Profile_Mass_Definitions
contains
- function Dark_Matter_Profile_Mass_Definition(node,densityContrast,radius,velocity,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,useLastIsolatedTime) result(massHalo)
+ function Dark_Matter_Profile_Mass_Definition(node,densityContrast,radius,velocity,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,useLastIsolatedTime) result(massHalo)
!!{
Compute the mass of {\normalfont \ttfamily node} under the given density contrast definition.
!!}
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Math_Exponentiation , only : cubeRoot
use :: Numerical_Comparison , only : Values_Agree
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly , massTypeDark
implicit none
double precision :: massHalo
type (treeNode ) , intent(inout) :: node
@@ -51,8 +52,8 @@ function Dark_Matter_Profile_Mass_Definition(node,densityContrast,radius,velocit
logical , optional, intent(in ) :: useLastIsolatedTime
class (cosmologyParametersClass ) , intent(inout) :: cosmologyParameters_
class (cosmologyFunctionsClass ) , intent(inout) :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ) , intent(inout) :: darkMatterProfileDMO_
class (virialDensityContrastClass) , intent(inout) :: virialDensityContrast_
+ class (massDistributionClass ), pointer :: massDistribution_
class (nodeComponentBasic ), pointer :: basic
double precision :: radiusHalo , density , &
& time
@@ -94,7 +95,11 @@ function Dark_Matter_Profile_Mass_Definition(node,densityContrast,radius,velocit
else
! Mismatched density contrast definitions - compute the mass directly.
! Get the radius in the halo enclosing this density.
- radiusHalo=+darkMatterProfileDMO_%radiusEnclosingDensity(node,density)
+ massDistribution_ => node %massDistribution (componentTypeDarkMatterOnly,massTypeDark)
+ radiusHalo = massDistribution_%radiusEnclosingDensity(density )
+ !![
+
+ !!]
! Find the mass within that radius - this is computable directly from the mean density and the radius enclosing that mean
! density.
massHalo =+4.0d0 &
diff --git a/source/dark_matter_profiles.structure.scale.Johnson2021.F90 b/source/dark_matter_profiles.structure.scale.Johnson2021.F90
index 296dc2ea8d..0882cd009f 100644
--- a/source/dark_matter_profiles.structure.scale.Johnson2021.F90
+++ b/source/dark_matter_profiles.structure.scale.Johnson2021.F90
@@ -21,7 +21,6 @@
Implements a dark matter profile scale radius class using the energy-based model of \cite{johnson_random_2021}.
!!}
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
use :: Virial_Orbits , only : virialOrbitClass
@@ -42,7 +41,6 @@
!!}
private
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (virialOrbitClass ), pointer :: virialOrbit_ => null()
class (mergerTreeMassResolutionClass ), pointer :: mergerTreeMassResolution_ => null()
@@ -80,7 +78,6 @@ function darkMatterProfileScaleJohnson2021ConstructorParameters(parameters) resu
type (darkMatterProfileScaleRadiusJohnson2021) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (virialOrbitClass ), pointer :: virialOrbit_
class (mergerTreeMassResolutionClass ), pointer :: mergerTreeMassResolution_
@@ -110,16 +107,14 @@ function darkMatterProfileScaleJohnson2021ConstructorParameters(parameters) resu
Factor multiplying the estimate of the internal energy of unresolved accretion.
-
!!]
- self=darkMatterProfileScaleRadiusJohnson2021(massExponent,energyBoost,unresolvedEnergy,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_)
+ self=darkMatterProfileScaleRadiusJohnson2021(massExponent,energyBoost,unresolvedEnergy,darkMatterProfileScaleRadius_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_)
!![
-
@@ -127,21 +122,20 @@ function darkMatterProfileScaleJohnson2021ConstructorParameters(parameters) resu
return
end function darkMatterProfileScaleJohnson2021ConstructorParameters
- function darkMatterProfileScaleJohnson2021ConstructorInternal(massExponent,energyBoost,unresolvedEnergy,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_) result(self)
+ function darkMatterProfileScaleJohnson2021ConstructorInternal(massExponent,energyBoost,unresolvedEnergy,darkMatterProfileScaleRadius_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily randomWalk} dark matter profile scale radius class.
!!}
implicit none
type (darkMatterProfileScaleRadiusJohnson2021) :: self
class (darkMatterProfileScaleRadiusClass ), intent(in ), target :: darkMatterProfileScaleRadius_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (virialOrbitClass ), intent(in ), target :: virialOrbit_
class (mergerTreeMassResolutionClass ), intent(in ), target :: mergerTreeMassResolution_
double precision , intent(in ) :: massExponent , energyBoost, &
& unresolvedEnergy
!![
-
+
!!]
return
@@ -156,7 +150,6 @@ subroutine darkMatterProfileScaleJohnson2021Destructor(self)
!![
-
@@ -174,7 +167,9 @@ double precision function darkMatterProfileScaleJohnson2021Radius(self,node) res
use :: Kepler_Orbits , only : keplerOrbit
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Beta_Functions , only : Beta_Function , Beta_Function_Incomplete_Normalized
- use :: Hypergeometric_Functions
+ use :: Hypergeometric_Functions , only : Hypergeometric_2F1
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly , massTypeDark
implicit none
class (darkMatterProfileScaleRadiusJohnson2021), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
@@ -185,6 +180,7 @@ double precision function darkMatterProfileScaleJohnson2021Radius(self,node) res
class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile , darkMatterProfileSibling , &
& darkMatterProfileChild , darkMatterProfileUnresolved
class (nodeComponentSatellite ) , pointer :: satelliteSibling
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , parameter :: massFunctionSlopeLogarithmic =1.90d0
double precision , parameter :: energyInternalFormFactorSlopeLogarithmic=0.03d0
double precision :: energyOrbital , massRatio , &
@@ -193,7 +189,8 @@ double precision function darkMatterProfileScaleJohnson2021Radius(self,node) res
& radiusScaleUnresolved , massResolution , &
& energyPotentialSubresolutionFactor , energyKineticSubresolutionFactor , &
& a , b , &
- & energyPotential , energyKinetic
+ & energyPotential , energyKinetic , &
+ & radiusVirial
type (rootFinder ) :: finder
type (keplerOrbit ) :: orbit
@@ -224,42 +221,52 @@ double precision function darkMatterProfileScaleJohnson2021Radius(self,node) res
massUnresolved = +basic %mass () &
& -basicChild %mass ()
! Iterate over progenitors and sum their energies.
- nodeSibling => nodeChild
- energyTotal = self%darkMatterProfileDMO_%energy(nodeSibling)
+ nodeSibling => nodeChild
+ massDistribution_ => nodeSibling %massDistribution (componentTypeDarkMatterOnly,massTypeDark )
+ radiusVirial = self %darkMatterHaloScale_ %radiusVirial(nodeSibling )
+ energyTotal = massDistribution_%energy (radiusVirial ,massDistribution_)
+ !![
+
+ !!]
do while (associated(nodeSibling%sibling))
nodeSibling => nodeSibling %sibling
- basicSibling => nodeSibling %basic ( )
- darkMatterProfileSibling => nodeSibling %darkMatterProfile( )
- satelliteSibling => nodeSibling %satellite (autoCreate=.true. )
- orbit = satelliteSibling %virialOrbit ( )
- massRatio = +basicSibling %mass ( ) &
- & /basicChild %mass ( )
- energyOrbital = +orbit %energy ( ) &
- & *basicSibling %mass ( ) &
- & /( &
- & +1.0d0 &
- & +massRatio &
+ basicSibling => nodeSibling %basic ( )
+ darkMatterProfileSibling => nodeSibling %darkMatterProfile ( )
+ satelliteSibling => nodeSibling %satellite (autoCreate=.true. )
+ massDistribution_ => nodeSibling %massDistribution (componentTypeDarkMatterOnly,massTypeDark)
+ radiusVirial = self %darkMatterHaloScale_ %radiusVirial(nodeSibling )
+ orbit = satelliteSibling %virialOrbit ( )
+ massRatio = +basicSibling %mass ( ) &
+ & /basicChild %mass ( )
+ energyOrbital = +orbit %energy ( ) &
+ & *basicSibling %mass ( ) &
+ & /( &
+ & +1.0d0 &
+ & +massRatio &
& )**self%massExponent
- massUnresolved = +massUnresolved &
- & -basicSibling %mass ( )
+ massUnresolved = +massUnresolved &
+ & -basicSibling %mass ( )
! Add orbital energy of this sibling.
- energyTotal = +energyTotal &
- & +energyOrbital &
- & *( &
- & +1.0d0 &
- & +self%energyBoost &
+ energyTotal = +energyTotal &
+ & +energyOrbital &
+ & *( &
+ & +1.0d0 &
+ & +self%energyBoost &
& )
! Add the internal energy of the sibling.
- energyTotal = +energyTotal &
- & +self%darkMatterProfileDMO_%energy ( nodeSibling) &
- & /( &
- & +1.0d0 &
- & +massRatio &
- & )**self%massExponent &
- & *( &
- & +1.0d0 &
- & +self%energyBoost &
+ energyTotal = +energyTotal &
+ & +massDistribution_ %energy (radiusVirial,massDistribution_ ) &
+ & /( &
+ & +1.0d0 &
+ & +massRatio &
+ & )**self%massExponent &
+ & *( &
+ & +1.0d0 &
+ & +self%energyBoost &
& )
+ !![
+
+ !!]
end do
! Account for unresolved accretion. We assume that unresolved halos are accreted with the mean orbital energy of
! the virial orbital parameter distribution, plus an internal energy corresponding to that of a halo with mass
@@ -316,30 +323,35 @@ double precision function darkMatterProfileScaleJohnson2021Radius(self,node) res
& 8.0d0/3.0d0-(massFunctionSlopeLogarithmic+energyInternalFormFactorSlopeLogarithmic) &
& )
! Determine the orbital and internal energies.
- energyKinetic =+0.5d0 &
- & *self%virialOrbit_%velocityTotalRootMeanSquared(nodeUnresolved,nodeChild)**2 &
- & /(1.0d0+massRatio)
- energyPotential=+self%virialOrbit_%energyMean (nodeUnresolved,nodeChild) &
- & - energyKinetic
- energyOrbital =+energyPotential &
- & *energyPotentialSubresolutionFactor &
- & +energyKinetic &
- & *energyKineticSubresolutionFactor
- energyTotal =+energyTotal &
- & +massUnresolved &
- & *self%unresolvedEnergy &
- & *( &
- & +energyOrbital &
- & +energyInternalSubresolutionFactor &
- & *self%darkMatterProfileDMO_%energy(nodeUnresolved) &
- & /massResolution &
- & ) &
- & *( &
- & +1.0d0 &
- & +self%energyBoost &
- & )
+ massDistribution_ => nodeUnresolved%massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial(nodeUnresolved)
+ energyKinetic = +0.5d0 &
+ & *self%virialOrbit_%velocityTotalRootMeanSquared(nodeUnresolved,nodeChild)**2 &
+ & /(1.0d0+massRatio)
+ energyPotential = +self%virialOrbit_%energyMean (nodeUnresolved,nodeChild) &
+ & - energyKinetic
+ energyOrbital = +energyPotential &
+ & *energyPotentialSubresolutionFactor &
+ & +energyKinetic &
+ & *energyKineticSubresolutionFactor
+ energyTotal = +energyTotal &
+ & +massUnresolved &
+ & *self%unresolvedEnergy &
+ & *( &
+ & +energyOrbital &
+ & +energyInternalSubresolutionFactor &
+ & *massDistribution_%energy(radiusVirial,massDistribution_) &
+ & /massResolution &
+ & ) &
+ & *( &
+ & +1.0d0 &
+ & +self%energyBoost &
+ & )
call nodeUnresolved%destroy()
deallocate(nodeUnresolved)
+ !![
+
+ !!]
end if
! Add mutual gravitational binding energy of any sibling halo and any unresolved mass.
if (associated(nodeChild%sibling)) &
@@ -370,13 +382,20 @@ double precision function radiusScaleRoot(radiusScale)
!!{
Function used in root-finding to compute the scale radius of a dark matter profile as a given energy.
!!}
- use :: Calculations_Resets, only : Calculations_Reset
+ use :: Calculations_Resets , only : Calculations_Reset
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeDarkMatterOnly, massTypeDark
implicit none
- double precision, intent(in ) :: radiusScale
-
+ double precision , intent(in ) :: radiusScale
+ class (massDistributionClass), pointer :: massDistribution_
+
call darkMatterProfile_%scaleSet(radiusScale)
call Calculations_Reset(node_)
- radiusScaleRoot=+ energyTotal &
- & -self_%darkMatterProfileDMO_%energy (node_)
+ massDistribution_ => node_ %massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ radiusScaleRoot = + energyTotal &
+ & -massDistribution_%energy (self_%darkMatterHaloScale_%radiusVirial(node_),massDistribution_)
+ !![
+
+ !!]
return
end function radiusScaleRoot
diff --git a/source/dark_matter_profiles.structure.scale.Ludlow2014.F90 b/source/dark_matter_profiles.structure.scale.Ludlow2014.F90
index 5f6389778c..ee460373c7 100644
--- a/source/dark_matter_profiles.structure.scale.Ludlow2014.F90
+++ b/source/dark_matter_profiles.structure.scale.Ludlow2014.F90
@@ -69,7 +69,7 @@ function ludlow2014ConstructorParameters(parameters) result(self)
return
end function ludlow2014ConstructorParameters
- function ludlow2014ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function ludlow2014ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,virialDensityContrast_) result(self)
!!{
Constructor for the {\normalfont \ttfamily ludlow2014} dark matter halo profile concentration class.
!!}
@@ -80,10 +80,9 @@ function ludlow2014ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunct
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterProfileScaleRadiusClass ), intent(in ), target :: darkMatterProfileScaleRadius_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- self%darkMatterProfileScaleRadiusLudlow2016=darkMatterProfileScaleRadiusLudlow2016(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,virialDensityContrast_)
+ self%darkMatterProfileScaleRadiusLudlow2016=darkMatterProfileScaleRadiusLudlow2016(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,virialDensityContrast_)
return
end function ludlow2014ConstructorInternal
@@ -146,7 +145,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& * states(stateCount)%cosmologyFunctions_%expansionFactor ( basicSibling%time())**3, &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& )
nodeSibling => nodeSibling %sibling
@@ -162,7 +160,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& * states(stateCount)%cosmologyFunctions_%expansionFactor ( basicBranch%time())**3, &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& ) &
& -massSiblings &
@@ -181,7 +178,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& * states(stateCount)%cosmologyFunctions_%expansionFactor ( basicChild%time())**3, &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& ) &
& +massAccretionRate &
@@ -206,7 +202,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& * states(stateCount)%cosmologyFunctions_%expansionFactor ( basicBranch%time())**3, &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& )
if (massProgenitor >= states(stateCount)%massLimit) &
diff --git a/source/dark_matter_profiles.structure.scale.Ludlow2016.F90 b/source/dark_matter_profiles.structure.scale.Ludlow2016.F90
index 4967d9e179..1d50e60e96 100644
--- a/source/dark_matter_profiles.structure.scale.Ludlow2016.F90
+++ b/source/dark_matter_profiles.structure.scale.Ludlow2016.F90
@@ -22,11 +22,10 @@
algorithm.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
- use :: Root_Finder , only : rootFinder
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
+ use :: Root_Finder , only : rootFinder
!![
@@ -48,7 +47,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
double precision :: C , f , &
& timeFormationSeekDelta , densityContrast
@@ -102,7 +100,6 @@ function ludlow2016ConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
double precision :: C , f, &
& timeFormationSeekDelta
@@ -130,22 +127,20 @@ function ludlow2016ConstructorParameters(parameters) result(self)
-
!!]
- self=darkMatterProfileScaleRadiusLudlow2016(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=darkMatterProfileScaleRadiusLudlow2016(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,virialDensityContrast_)
!![
-
!!]
return
end function ludlow2016ConstructorParameters
- function ludlow2016ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function ludlow2016ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileScaleRadius_,virialDensityContrast_) result(self)
!!{
Constructor for the {\normalfont \ttfamily ludlow2016} dark matter halo profile concentration class.
!!}
@@ -156,10 +151,9 @@ function ludlow2016ConstructorInternal(C,f,timeFormationSeekDelta,cosmologyFunct
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterProfileScaleRadiusClass ), intent(in ), target :: darkMatterProfileScaleRadius_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
!![
-
+
!!]
! Find the density contrast as used to define masses by Ludlow et al. (2016).
@@ -178,7 +172,6 @@ subroutine ludlow2016Destructor(self)
-
!!]
return
@@ -194,6 +187,8 @@ double precision function ludlow2016Radius(self,node)
use :: Display , only : displayGreen , displayReset
use :: Error , only : Error_Report , errorStatusSuccess
use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile, treeNode
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly , massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
use :: Merger_Tree_Walkers , only : mergerTreeWalkerIsolatedNodesBranch
use :: Numerical_Comparison , only : Values_Agree
use :: Numerical_Constants_Math , only : Pi
@@ -204,6 +199,7 @@ double precision function ludlow2016Radius(self,node)
type (treeNode ) , pointer :: nodeBranch
class (nodeComponentBasic ) , pointer :: basic , basicBranch
class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile_ , darkMatterProfileChild_
+ class (massDistributionClass ), pointer :: massDistribution_
integer , parameter :: iterationCountMaximum =100
type (ludlow2016State ), allocatable , dimension(:) :: statesTmp
type (mergerTreeWalkerIsolatedNodesBranch ) :: treeWalker
@@ -264,14 +260,18 @@ double precision function ludlow2016Radius(self,node)
iterationCount=iterationCount+1
! Compute the characteristic halo mass, M₋₂.
if (iterationCount == 1) then
- basic => node %basic ( )
+ basic => node %basic ( )
states(stateCount)%self => self
states(stateCount)%node => node
- states(stateCount)%hubbleParameterPresent = self%cosmologyFunctions_ %hubbleParameterEpochal(expansionFactor=1.0d0 )
+ states(stateCount)%hubbleParameterPresent = self%cosmologyFunctions_%hubbleParameterEpochal(expansionFactor=1.0d0 )
states(stateCount)%timePrevious = -1.0d0
states(stateCount)%densityContrast = -huge(0.0d0)
end if
- massHaloCharacteristic = +self%darkMatterProfileDMO_%enclosedMass (node,darkMatterProfile_%scale())
+ massDistribution_ => node %massDistribution (componentTypeDarkMatterOnly,massTypeDark)
+ massHaloCharacteristic = +massDistribution_ %massEnclosedBySphere (darkMatterProfile_%scale() )
+ !![
+
+ !!]
states(stateCount)%massHaloCharacteristic = massHaloCharacteristic
states(stateCount)%massLimit = +self%f &
& *Dark_Matter_Profile_Mass_Definition( &
@@ -279,7 +279,6 @@ double precision function ludlow2016Radius(self,node)
& ludlow2016DensityContrast(states(stateCount),basic%time()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
! Find the earliest time in the branch. Also estimate the earliest and latest times between which the formation time will lie.
@@ -448,7 +447,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& ludlow2016DensityContrast(states(stateCount),basicChild%time()), &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& )
if (nodeChild%isPrimaryProgenitor()) then
@@ -463,7 +461,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& ludlow2016DensityContrast(states(stateCount),basicSibling%time()), &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& )
nodeSibling => nodeSibling %sibling
@@ -474,7 +471,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& ludlow2016DensityContrast(states(stateCount),basicBranch %time()), &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& ) &
& -massSiblings &
@@ -507,7 +503,6 @@ Function used to find the formation time of a halo in the {\normalfont \ttfamily
& ludlow2016DensityContrast(states(stateCount),basicBranch%time()), &
& cosmologyParameters_ =states(stateCount)%self%cosmologyParameters_ , &
& cosmologyFunctions_ =states(stateCount)%self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =states(stateCount)%self%darkMatterProfileDMO_ , &
& virialDensityContrast_=states(stateCount)%self%virialDensityContrast_ &
& )
if (massProgenitor >= states(stateCount)%massLimit) &
diff --git a/source/dark_matter_profiles.structure.scale.concentration.F90 b/source/dark_matter_profiles.structure.scale.concentration.F90
index 41ac1753b3..8f662d37e0 100644
--- a/source/dark_matter_profiles.structure.scale.concentration.F90
+++ b/source/dark_matter_profiles.structure.scale.concentration.F90
@@ -315,11 +315,13 @@ double precision function concentrationMassRoot(massDefinitionTrial)
Root function used to find the mass of a halo corresponding to the definition used for a particular concentration class.
!!}
use :: Calculations_Resets, only : Calculations_Reset
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- double precision, intent(in ) :: massDefinitionTrial
- double precision :: radiusOuterDefinition, concentrationDefinition, &
- & radiusCore , massOuter , &
- & radiusOuter , densityOuter
+ double precision , intent(in ) :: massDefinitionTrial
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: radiusOuterDefinition, concentrationDefinition, &
+ & radiusCore , massOuter , &
+ & radiusOuter , densityOuter
! Set the mass of the worker node.
call state_(stateCount)%basic%massSet(massDefinitionTrial)
@@ -343,12 +345,17 @@ Root function used to find the mass of a halo corresponding to the definition us
call state_(stateCount)%darkMatterProfile%scaleSet(radiusCore)
call Calculations_Reset(state_(stateCount)%nodeWork)
! Find the non-alt density.
- densityOuter=+state_(stateCount)%self%cosmologyFunctions_ %matterDensityEpochal ( state_(stateCount)%basic%time()) &
- & *state_(stateCount)%self%virialDensityContrast_ %densityContrast (state_(stateCount)%basic%mass(),state_(stateCount)%basic%time())
+ densityOuter=+state_(stateCount)%self%cosmologyFunctions_ %matterDensityEpochal( state_(stateCount)%basic%time()) &
+ & *state_(stateCount)%self%virialDensityContrast_%densityContrast (state_(stateCount)%basic%mass(),state_(stateCount)%basic%time())
+ ! Get the current mass distribution.
+ massDistribution_ => state_(stateCount)%self%darkMatterProfileDMODefinition%get(state_(stateCount)%nodeWork)
! Solve for radius which encloses required non-alt density.
- radiusOuter =+state_(stateCount)%self%darkMatterProfileDMODefinition%radiusEnclosingDensity(state_(stateCount)%nodeWork ,densityOuter)
+ radiusOuter=massDistribution_%radiusEnclosingDensity(densityOuter)
! Get the mass within this radius.
- massOuter =+state_(stateCount)%self%darkMatterProfileDMODefinition%enclosedMass (state_(stateCount)%nodeWork , radiusOuter)
+ massOuter =massDistribution_%massEnclosedBySphere ( radiusOuter)
+ !![
+
+ !!]
! Return root function.
concentrationMassRoot=massOuter-state_(stateCount)%mass
return
diff --git a/source/dark_matter_profiles.structure.scale.random_walk.F90 b/source/dark_matter_profiles.structure.scale.random_walk.F90
index d1df29c751..7e6c43cf16 100644
--- a/source/dark_matter_profiles.structure.scale.random_walk.F90
+++ b/source/dark_matter_profiles.structure.scale.random_walk.F90
@@ -70,11 +70,11 @@
end interface darkMatterProfileScaleRadiusRandomWalk
! Sub-module-scope variables used in root finding.
- double precision :: energyTarget_
+ double precision :: energyTarget_ , radiusVirial_
class (darkMatterProfileScaleRadiusRandomWalk), pointer :: self_
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile_
type (treeNode ), pointer :: node_
- !$omp threadprivate(energyTarget_,self_,node_,darkMatterProfile_)
+ !$omp threadprivate(energyTarget_,self_,node_,darkMatterProfile_,radiusVirial_)
contains
@@ -152,34 +152,47 @@ double precision function darkMatterProfileScaleRandomWalkRadius(self,node) resu
use :: Galacticus_Nodes , only : nodeComponentBasic
use :: Root_Finder , only : rootFinder , rangeExpandMultiplicative, rangeExpandSignExpectPositive, rangeExpandSignExpectNegative
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (darkMatterProfileScaleRadiusRandomWalk), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfileChild
class (nodeComponentBasic ) , pointer :: basic , basicChild
+ class (massDistributionClass ) , pointer :: massDistribution_ , massDistributionChild_
type (rootFinder ) :: finder
- double precision :: energyPerturbation , radiusScaleOriginal, &
- & energyScaleChild , energyScale
+ double precision :: energyPerturbation , radiusScaleOriginal , &
+ & energyScaleChild , energyScale , &
+ & radiusVirialChild
! Set the scale radius to that given by the lower level scale radius class.
radiusScale=self%darkMatterProfileScaleRadius_%radius(node)
! For nodes with a child, evaluate the perturbation to this radius.
if (associated(node%firstChild)) then
- basic => node %basic ( )
- basicChild => node %firstChild %basic ( )
- darkMatterProfileChild => node %firstChild %darkMatterProfile( )
- energyScale = +gravitationalConstantGalacticus &
- & *basic %mass ( )**2 &
- & /self %darkMatterHaloScale_ %radiusVirial (node )
- energyScaleChild = +gravitationalConstantGalacticus &
- & *basicChild %mass ( )**2 &
- & /self %darkMatterHaloScale_ %radiusVirial (node%firstChild)
- energyPerturbation = +self %darkMatterProfileDMO_%energy (node%firstChild)
- radiusScaleOriginal = +darkMatterProfileChild %scale ( )
- call darkMatterProfileChild%scaleSet(self%darkMatterProfileScaleRadius_%radius (node%firstChild))
- energyPerturbation = +energyPerturbation &
- & -self %darkMatterProfileDMO_%energy (node%firstChild)
- call darkMatterProfileChild%scaleSet( radiusScaleOriginal )
+ basic => node %basic ( )
+ basicChild => node %firstChild %basic ( )
+ darkMatterProfileChild => node %firstChild %darkMatterProfile( )
+ radiusVirial_ = self %darkMatterHaloScale_ %radiusVirial (node )
+ radiusVirialChild = self %darkMatterHaloScale_ %radiusVirial (node%firstChild )
+ energyScale = +gravitationalConstantGalacticus &
+ & *basic %mass ( )**2 &
+ & /self %darkMatterHaloScale_ %radiusVirial (node )
+ energyScaleChild = +gravitationalConstantGalacticus &
+ & *basicChild %mass ( )**2 &
+ & /radiusVirialChild
+ massDistributionChild_ => self %darkMatterProfileDMO_%get (node%firstChild )
+ energyPerturbation = +massDistributionChild_ %energy (radiusVirialChild,massDistributionChild_)
+ !![
+
+ !!]
+ radiusScaleOriginal = +darkMatterProfileChild %scale ( )
+ call darkMatterProfileChild%scaleSet(self%darkMatterProfileScaleRadius_%radius (node%firstChild ))
+ massDistributionChild_ => self %darkMatterProfileDMO_%get (node%firstChild )
+ energyPerturbation = +energyPerturbation &
+ & -massDistributionChild_ %energy (radiusVirialChild,massDistributionChild_)
+ !![
+
+ !!]
+ call darkMatterProfileChild%scaleSet( radiusScaleOriginal )
if (energyScale > energyScaleChild) then
energyPerturbation =+energyPerturbation &
& +sqrt( &
@@ -204,13 +217,17 @@ double precision function darkMatterProfileScaleRandomWalkRadius(self,node) resu
& )
self_ => self
node_ => node
- darkMatterProfile_ => node %darkMatterProfile (autoCreate=.true. )
- radiusScaleOriginal = darkMatterProfile_ %scale ( )
+ darkMatterProfile_ => node %darkMatterProfile (autoCreate=.true. )
+ radiusScaleOriginal = darkMatterProfile_ %scale ( )
call darkMatterProfile_%scaleSet(radiusScale )
- energyTarget_ = +self %darkMatterProfileDMO_%energy ( node ) &
- & + energyPerturbation
- radiusScale = finder %find (rootGuess =darkMatterProfile_%scale())
- call darkMatterProfile_%scaleSet(radiusScaleOriginal)
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ energyTarget_ = +massDistribution_ %energy (radiusVirial_,massDistribution_ ) &
+ & + energyPerturbation
+ radiusScale = finder %find (rootGuess =darkMatterProfile_%scale())
+ call darkMatterProfile_%scaleSet(radiusScaleOriginal)
+ !![
+
+ !!]
end if
return
end function darkMatterProfileScaleRandomWalkRadius
@@ -219,11 +236,17 @@ double precision function energyRoot(radiusScale)
!!{
Root function used in finding the scale radius corresponding to a given halo energy.
!!}
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
- double precision, intent(in ) :: radiusScale
+ double precision , intent(in ) :: radiusScale
+ class (massDistributionClass), pointer :: massDistribution_
call darkMatterProfile_%scaleSet(radiusScale)
- energyRoot=+self_%darkMatterProfileDMO_%energy (node_) &
- & - energyTarget_
+ massDistribution_ => self_ %darkMatterProfileDMO_%get (node_ )
+ energyRoot = +massDistribution_ %energy (radiusVirial_,massDistribution_) &
+ & - energyTarget_
+ !![
+
+ !!]
return
end function energyRoot
diff --git a/source/dark_matter_profiles.structure_tasks.F90 b/source/dark_matter_profiles.structure_tasks.F90
deleted file mode 100644
index 4f8c78a8fc..0000000000
--- a/source/dark_matter_profiles.structure_tasks.F90
+++ /dev/null
@@ -1,471 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 structure tasks related to the dark matter halo density profile.
-!!}
-
-module Dark_Matter_Profile_Structure_Tasks
- !!{
- Implements structure tasks related to the dark matter halo density profile.
- !!}
- use :: Dark_Matter_Profiles, only : darkMatterProfileClass
- private
- public :: Dark_Matter_Profile_Enclosed_Mass_Task , Dark_Matter_Profile_Density_Task , Dark_Matter_Profile_Rotation_Curve_Task , Dark_Matter_Profile_Potential_Task , &
- & Dark_Matter_Profile_Rotation_Curve_Gradient_Task , Dark_Matter_Profile_Acceleration_Task , Dark_Matter_Profile_Tidal_Tensor_Task , Dark_Matter_Profile_Chandrasekhar_Integral_Task , &
- & Dark_Matter_Profile_Structure_Tasks_Thread_Initialize, Dark_Matter_Profile_Structure_Tasks_Thread_Uninitialize, Dark_Matter_Profile_Structure_Tasks_State_Store, Dark_Matter_Profile_Structure_Tasks_State_Restore, &
- & Dark_Matter_Profile_Density_Spherical_Average_Task , Dark_Matter_Profile_Radius_Enclosing_Mass
-
- class(darkMatterProfileClass), pointer :: darkMatterProfile_
- !$omp threadprivate(darkMatterProfile_)
-
-contains
-
- !![
-
- Dark_Matter_Profile_Structure_Tasks_Thread_Initialize
-
- !!]
- subroutine Dark_Matter_Profile_Structure_Tasks_Thread_Initialize(parameters_)
- !!{
- Initializes the dark matter profile structure tasks module.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type(inputParameters), intent(inout) :: parameters_
-
- !![
-
- !!]
- return
- end subroutine Dark_Matter_Profile_Structure_Tasks_Thread_Initialize
-
- !![
-
- Dark_Matter_Profile_Structure_Tasks_Thread_Uninitialize
-
- !!]
- subroutine Dark_Matter_Profile_Structure_Tasks_Thread_Uninitialize()
- !!{
- Uninitializes the dark matter profile structure tasks module.
- !!}
- implicit none
-
- !![
-
- !!]
- return
- end subroutine Dark_Matter_Profile_Structure_Tasks_Thread_Uninitialize
-
- !![
-
- Dark_Matter_Profile_Enclosed_Mass_Task
-
- !!]
- double precision function Dark_Matter_Profile_Enclosed_Mass_Task(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for a dark matter profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeDarkHalo, massTypeAll , massTypeDark , &
- & radiusLarge , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ) , pointer :: basic
- !$GLC attributes unused :: weightIndex
-
- Dark_Matter_Profile_Enclosed_Mass_Task=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDarkHalo)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeDark )) return
- if (.not.(weightBy == weightByMass )) return
-
- ! Test radius.
- if (radius >= radiusLarge) then
- ! Return the total mass of the halo in this case.
- basic => node%basic()
- Dark_Matter_Profile_Enclosed_Mass_Task=basic%mass()
- else if (radius <= 0.0d0) then
- ! Zero radius. Return zero mass.
- Dark_Matter_Profile_Enclosed_Mass_Task=0.0d0
- else
- ! Return the mass within the radius.
- Dark_Matter_Profile_Enclosed_Mass_Task=darkMatterProfile_%enclosedMass(node,radius)
- end if
- return
- end function Dark_Matter_Profile_Enclosed_Mass_Task
-
- !![
-
- Dark_Matter_Profile_Acceleration_Task
-
- !!]
- function Dark_Matter_Profile_Acceleration_Task(node,positionCartesian,componentType,massType)
- !!{
- Computes the acceleration due to a dark matter profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: Dark_Matter_Profile_Acceleration_Task
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
-
- radius=sqrt(sum(positionCartesian**2))
- Dark_Matter_Profile_Acceleration_Task=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *Dark_Matter_Profile_Enclosed_Mass_Task(node,radius,componentType,massType,weightByMass,weightIndexNull) &
- & *positionCartesian &
- & /radius**3
- return
- end function Dark_Matter_Profile_Acceleration_Task
-
- !![
-
- Dark_Matter_Profile_Chandrasekhar_Integral_Task
-
- !!]
- function Dark_Matter_Profile_Chandrasekhar_Integral_Task(node,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
- !!{
- Computes the Chandrasekhar integral due to a dark matter profile.
- !!}
- use :: Galactic_Structure_Options, only : weightByMass, weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- double precision , dimension(3) :: Dark_Matter_Profile_Chandrasekhar_Integral_Task
- type (treeNode ), intent(inout) :: node , nodeSatellite
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- double precision , dimension(3) :: positionSpherical
- double precision , parameter :: XvMaximum =10.0d0
- double precision :: radius , velocity , &
- & density , xV , &
- & velocityDispersion
- !$GLC attributes unused :: nodeSatellite
-
- Dark_Matter_Profile_Chandrasekhar_Integral_Task=0.0d0
- radius =sqrt(sum(positionCartesian**2))
- velocity =sqrt(sum(velocityCartesian**2))
- if (velocity <= 0.0d0) return
- positionSpherical =[radius,0.0d0,0.0d0]
- density =Dark_Matter_Profile_Density_Task(node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (density <= 0.0d0) return
- velocityDispersion=darkMatterProfile_%radialVelocityDispersion(node,radius)
- if (velocityDispersion > 0.0d0) then
- xV =+velocity &
- & /velocityDispersion &
- & /sqrt(2.0d0)
- else
- xV=huge(0.0d0)
- end if
- Dark_Matter_Profile_Chandrasekhar_Integral_Task = -density &
- & *velocityCartesian &
- & /velocity **3
- if (Xv <= XvMaximum) &
- & Dark_Matter_Profile_Chandrasekhar_Integral_Task=+Dark_Matter_Profile_Chandrasekhar_Integral_Task &
- & *( &
- & +erf ( xV ) &
- & -2.0d0 &
- & * xV &
- & *exp (-xV**2) &
- & /sqrt( Pi ) &
- & )
- return
- end function Dark_Matter_Profile_Chandrasekhar_Integral_Task
-
- !![
-
- Dark_Matter_Profile_Tidal_Tensor_Task
-
- !!]
- function Dark_Matter_Profile_Tidal_Tensor_Task(node,positionCartesian,componentType,massType)
- !!{
- Computes the tidalTensor due to a dark matter profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorIdentityR2D3Sym, assignment(=) , operator(*)
- use :: Vectors , only : Vector_Outer_Product
- implicit none
- type (tensorRank2Dimension3Symmetric) :: Dark_Matter_Profile_Tidal_Tensor_Task
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision , dimension(3) :: positionSpherical
- double precision :: radius , massEnclosed, &
- & density
- type (tensorRank2Dimension3Symmetric) :: positionTensor
-
- radius =sqrt(sum(positionCartesian**2))
- positionSpherical=[radius,0.0d0,0.0d0]
- massEnclosed =Dark_Matter_Profile_Enclosed_Mass_Task(node,radius ,componentType,massType,weightByMass,weightIndexNull)
- density =Dark_Matter_Profile_Density_Task (node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- positionTensor =Vector_Outer_Product ( positionCartesian,symmetrize=.true. )
- Dark_Matter_Profile_Tidal_Tensor_Task=+gravitationalConstantGalacticus &
- & *( &
- & -(massEnclosed /radius**3)*tensorIdentityR2D3Sym &
- & +(massEnclosed*3.0d0 /radius**5)*positionTensor &
- & -(density *4.0d0*Pi/radius**2)*positionTensor &
- & )
- return
- end function Dark_Matter_Profile_Tidal_Tensor_Task
-
- !![
-
- Dark_Matter_Profile_Rotation_Curve_Task
-
- !!]
- double precision function Dark_Matter_Profile_Rotation_Curve_Task(node,radius,componentType,massType)
- !!{
- Computes the rotation curve at a given radius for a dark matter profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentMass
-
- ! Set to zero by default.
- Dark_Matter_Profile_Rotation_Curve_Task=0.0d0
-
- ! Compute if a spheroid is present.
- if (radius > 0.0d0) then
- componentMass=Dark_Matter_Profile_Enclosed_Mass_Task(node,radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) Dark_Matter_Profile_Rotation_Curve_Task=sqrt(gravitationalConstantGalacticus*componentMass)&
- &/sqrt(radius)
- end if
- return
- end function Dark_Matter_Profile_Rotation_Curve_Task
-
- !![
-
- Dark_Matter_Profile_Density_Task
-
- !!]
- double precision function Dark_Matter_Profile_Density_Task(node,positionSpherical,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for a dark matter profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll, componentTypeDarkHalo , massTypeAll , massTypeDark , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Galacticus_Nodes , only : treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionSpherical(3)
- !$GLC attributes unused :: weightIndex
-
- ! Return zero if the component and mass type is not matched.
- Dark_Matter_Profile_Density_Task=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDarkHalo)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeDark )) return
- if (.not.(weightBy == weightByMass )) return
- ! Compute the density
- Dark_Matter_Profile_Density_Task=darkMatterProfile_%density(node,positionSpherical(1))
- return
- end function Dark_Matter_Profile_Density_Task
-
- !![
-
- Dark_Matter_Profile_Density_Spherical_Average_Task
-
- !!]
- double precision function Dark_Matter_Profile_Density_Spherical_Average_Task(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for a dark matter profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll, componentTypeDarkHalo , massTypeAll , massTypeDark , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: weightIndex
-
- ! Return zero if the component and mass type is not matched.
- Dark_Matter_Profile_Density_Spherical_Average_Task=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDarkHalo)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeDark )) return
- if (.not.(weightBy == weightByMass )) return
- ! Compute the density
- Dark_Matter_Profile_Density_Spherical_Average_Task=+3.0d0 &
- & *darkMatterProfile_%enclosedMass(node,radius) &
- & /4.0d0 &
- & /Pi &
- & /radius**3
- return
- end function Dark_Matter_Profile_Density_Spherical_Average_Task
-
- !![
-
- Dark_Matter_Profile_Rotation_Curve_Gradient_Task
-
- !!]
- double precision function Dark_Matter_Profile_Rotation_Curve_Gradient_Task(node,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient for the dark matter.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDarkHalo, massTypeAll , massTypeDark , &
- & weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentDensity, componentMass, positionSpherical(3)
-
- ! Set to zero by default.
- Dark_Matter_Profile_Rotation_Curve_Gradient_Task=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDarkHalo)) return
- if (.not.( massType == massTypeAll .or. massType == massTypeDark )) return
- if (radius <= 0.0d0) return
-
- positionSpherical=[radius,0.0d0,0.0d0]
- componentMass =Dark_Matter_Profile_Enclosed_Mass_Task(node,radius ,componentType,massType,weightByMass,weightIndexNull)
- componentDensity=Dark_Matter_Profile_Density_Task (node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass ==0.0d0 .or. componentDensity == 0.0d0) return
- Dark_Matter_Profile_Rotation_Curve_Gradient_Task= &
- & gravitationalConstantGalacticus &
- & *(-componentMass/radius**2 &
- & +4.0d0*Pi*radius*componentDensity &
- & )
- return
- end function Dark_Matter_Profile_Rotation_Curve_Gradient_Task
-
- !![
-
- Dark_Matter_Profile_Potential_Task
-
- !!]
- double precision function Dark_Matter_Profile_Potential_Task(node,radius,componentType,massType,status)
- !!{
- Return the potential due to dark matter.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeDarkHalo , massTypeAll , massTypeDark , &
- & structureErrorCodeSuccess, enumerationComponentTypeType, enumerationMassTypeType, enumerationStructureErrorCodeType
- use :: Galacticus_Nodes , only : treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- type (enumerationStructureErrorCodeType) :: statusLocal
-
- Dark_Matter_Profile_Potential_Task=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDarkHalo)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeDark )) return
- Dark_Matter_Profile_Potential_Task=darkMatterProfile_%potential(node,radius,statusLocal)
- if (present(status).and.statusLocal /= structureErrorCodeSuccess) status=structureErrorCodeSuccess
- return
- end function Dark_Matter_Profile_Potential_Task
-
- double precision function Dark_Matter_Profile_Radius_Enclosing_Mass(node,mass)
- !!{
- Return the radius enclosing the given mass of dark matter.
- !!}
- use :: Galacticus_Nodes, only : treeNode
- implicit none
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: mass
-
- Dark_Matter_Profile_Radius_Enclosing_Mass=darkMatterProfile_%radiusEnclosingMass(node,mass)
- return
- end function Dark_Matter_Profile_Radius_Enclosing_Mass
-
- !![
-
- Dark_Matter_Profile_Structure_Tasks_State_Store
-
- !!]
- subroutine Dark_Matter_Profile_Structure_Tasks_State_Store(stateFile,gslStateFile,stateOperationID)
- !!{
- Store object state,
- !!}
- use :: Display , only : displayMessage, verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
- implicit none
- integer , intent(in ) :: stateFile
- integer(c_size_t), intent(in ) :: stateOperationID
- type (c_ptr ), intent(in ) :: gslStateFile
-
- call displayMessage('Storing state for: dark matter profile structure tasks',verbosity=verbosityLevelInfo)
- !![
-
- !!]
- return
- end subroutine Dark_Matter_Profile_Structure_Tasks_State_Store
-
- !![
-
- Dark_Matter_Profile_Structure_Tasks_State_Restore
-
- !!]
- subroutine Dark_Matter_Profile_Structure_Tasks_State_Restore(stateFile,gslStateFile,stateOperationID)
- !!{
- Retrieve object state.
- !!}
- use :: Display , only : displayMessage, verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
- implicit none
- integer , intent(in ) :: stateFile
- integer(c_size_t), intent(in ) :: stateOperationID
- type (c_ptr ), intent(in ) :: gslStateFile
-
- call displayMessage('Retrieving state for: dark matter profile structure tasks',verbosity=verbosityLevelInfo)
- !![
-
- !!]
- return
- end subroutine Dark_Matter_Profile_Structure_Tasks_State_Restore
-
-end module Dark_Matter_Profile_Structure_Tasks
diff --git a/source/dark_matter_profiles_DMO.Burkert.F90 b/source/dark_matter_profiles_DMO.Burkert.F90
index d020e33227..dfc289348c 100644
--- a/source/dark_matter_profiles_DMO.Burkert.F90
+++ b/source/dark_matter_profiles_DMO.Burkert.F90
@@ -21,40 +21,13 @@
An implementation of \cite{burkert_structure_1995} dark matter halo profiles.
!!}
- use :: Kind_Numbers , only : kind_int8
- use :: Numerical_Constants_Math, only : Pi
- use :: Tables , only : table1D , table1DLogarithmicLinear
-
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
- A dark matter profile DMO class which implements the \citep{burkert_structure_1995} density profile is used
- \begin{equation}
- \rho_\mathrm{dark matter}(r) \propto \left(1+{r\over r_\mathrm{s}}\right)^{-1} \left(1+[{r\over
- r_\mathrm{s}}]^2\right)^{-1},
- \end{equation}
- normalized such that the total mass of the \gls{node} is enclosed with the virial radius and with the scale length
- $r_\mathrm{s} = r_\mathrm{virial}/c$ where $c$ is the halo concentration (see
- \refPhysics{darkMatterProfileConcentration}). The mass enclosed within radius $r$ is given by
- \begin{equation}
- M(<r) = M_\mathrm{virial} {2 \log(1 + R) + \log(1 + R^2) -2 \tan^{-1}(R) \over 2 \log(1 + c) + \log(1 + c^2) -2 \tan^{-1}(c)},
- \end{equation}
- where $R=r/r_\mathrm{s}$. The associated gravitational potential is
- \begin{equation}
- \Phi(r) = -\mathrm{G} \left(1+{1 \over R}\right) { 2 \tan^{-1}(R) - 2 \log(1 + R) + \log(1 + R^2) \over -2 \tan^{-1}(c) + 2
- \log(1 + c) + \log(1 + c^2) }.
- \end{equation}
- The peak of the rotation curve occurs at $R=3.2446257246042642$ (found by numerical solution), and the Fourier transform of
- the profile, $F(k) = \int_0^c 4 \pi r^2 \exp(-i k r) \rho(r) \mathrm{d} r / k r$ (needed in calculations of clustering
- using the halo model) is given by
- \begin{eqnarray}
- F(k) &=& \left\{2 \exp(-i k) \mathrm{C}_\mathrm{i}(k) - 2 \exp(-i k) \mathrm{C}_\mathrm{i}(k[1 + c]) + (1 + i)
- \left[-i \exp(k) \pi - \exp(k) \mathrm{E}_\mathrm{i}(-k) \right. \right. \nonumber \\
- & & +i \exp(-k) \mathrm{E}_\mathrm{i}(k) + \exp(k) \mathrm{E}_\mathrm{i}(i [i + c] k) - i \exp(-k)
- \mathrm{E}_\mathrm{i}(k [1 + i c ]) + (1 + i) \exp(-i k) \mathrm{S}_\mathrm{i}(k) \nonumber \\
- & & \left. \left. - (1 + i) \exp(-i k) \mathrm{S}_\mathrm{i}(k[1 + c])\right]\right\}/\left[k \left\{-2
- \tan^{-1}(c) + 2 \log(1 + c) + \log(1 + c^2)\right\}\right].
- \end{eqnarray}
+ A dark matter only profile class which builds \refClass{massDistributionBurkert} objects to compute the
+ \citep{burkert_structure_1995} density profile.
!!]
@@ -63,89 +36,10 @@
A dark matter halo profile class implementing \cite{burkert_structure_1995} dark matter halos.
!!}
private
- ! Minimum and maximum concentrations to tabulate.
- double precision :: concentrationMinimum , concentrationMaximum
- ! Minimum and maximum radii to tabulate.
- double precision :: freefallRadiusMinimum , radiusMinimum , &
- & densityRadiusMinimum , radialVelocityDispersionRadiusMinimum
- double precision :: freefallRadiusMaximum , radiusMaximum , &
- & densityRadiusMaximum , radialVelocityDispersionRadiusMaximum
- double precision :: freefallTimeMinimum , specificAngularMomentumMinimum , &
- & densityMinimum
- double precision :: freefallTimeMaximum , specificAngularMomentumMaximum , &
- & densityMaximum
- ! Tables of Burkert properties.
- logical :: burkertFreefallTableInitialized =.false., burkertInverseTableInitialized =.false., &
- & burkertTableInitialized =.false., burkertDensityTableInitialized =.false., &
- & burkertRadialVelocityDispersionTableInitialized =.false.
- integer :: burkertFreefallTableNumberPoints , burkertInverseTableNumberPoints , &
- & burkertTableNumberPoints , burkertDensityTableNumberPoints , &
- & burkertRadialVelocityDispersionTableNumberPoints
- type (table1DLogarithmicLinear) :: burkertConcentrationTable
- ! Tables.
- type (table1DLogarithmicLinear) :: burkertFreeFall , burkertSpecificAngularMomentum , &
- & burkertDensityTable , burkertRadialVelocityDispersionTable
- class (table1D ), allocatable :: burkertFreefallInverse , burkertSpecificAngularMomentumInverse , &
- & burkertDensityTableInverse
- ! Module variables used in integrations.
- double precision :: concentrationParameter , radiusStart
- ! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID
- ! Record of whether or not quantities have been computed.
- logical :: specificAngularMomentumScalingsComputed , maximumVelocityComputed
- ! Stored values of computed quantities.
- double precision :: specificAngularMomentumLengthScale , specificAngularMomentumScale , &
- & concentrationPrevious , burkertNormalizationFactorPrevious , &
- & maximumVelocityPrevious
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
- !![
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- !!]
- final :: burkertDestructor
- procedure :: autoHook => burkertAutoHook
- procedure :: calculationReset => burkertCalculationReset
- procedure :: density => burkertDensity
- procedure :: densityLogSlope => burkertDensityLogSlope
- procedure :: enclosedMass => burkertEnclosedMass
- procedure :: radiusEnclosingDensity => burkertRadiusEnclosingDensity
- procedure :: radiusEnclosingDensityTabulate => burkertRadiusEnclosingDensityTabulate
- procedure :: potential => burkertPotential
- procedure :: circularVelocity => burkertCircularVelocity
- procedure :: circularVelocityMaximum => burkertCircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => burkertRadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => burkertRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => burkertRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => burkertRotationNormalization
- procedure :: energy => burkertEnergy
- procedure :: kSpace => burkertKSpace
- procedure :: freefallRadius => burkertFreefallRadius
- procedure :: freefallRadiusIncreaseRate => burkertFreefallRadiusIncreaseRate
- procedure :: profileEnergy => burkertProfileEnergy
- procedure :: specificAngularMomentumScaleFree => burkertSpecificAngularMomentumScaleFree
- procedure :: angularMomentumScaleFree => burkertAngularMomentumScaleFree
- procedure :: enclosedMassScaleFree => burkertEnclosedMassScaleFree
- procedure :: densityScaleFree => burkertDensityScaleFree
- procedure :: radialVelocityDispersionScaleFree => burkertRadialVelocityDispersionScaleFree
- procedure :: radialVelocityDispersionTabulate => burkertRadialVelocityDispersionTabulate
- procedure :: tabulate => burkertTabulate
- procedure :: inverseAngularMomentum => burkertInverseAngularMomentum
- procedure :: freefallTabulate => burkertFreefallTabulate
- procedure :: freefallTimeScaleFree => burkertFreefallTimeScaleFree
- procedure :: radialMoment => burkertRadialMoment
+ final :: burkertDestructor
+ procedure :: get => burkertGet
end type darkMatterProfileDMOBurkert
interface darkMatterProfileDMOBurkert
@@ -156,19 +50,6 @@
module procedure burkertConstructorInternal
end interface darkMatterProfileDMOBurkert
- ! Number of points per decade of concentration in Burkert tabulations.
- integer , parameter :: tablePointsPerDecade =100
- integer , parameter :: densityTablePointsPerDecade =100
- integer , parameter :: inverseTablePointsPerDecade =100
- integer , parameter :: freefallTablePointsPerDecade =100
- integer , parameter :: radialVelocityDispersionTablePointsPerDecade=100
-
- ! Indices for tabulated quantities.
- integer , parameter :: concentrationEnergyIndex = 1 , concetrationRotationNormalizationIndex=2
-
- ! Minimum (scale-free) freefall time in the Burkert profile.
- double precision, parameter :: freefallTimeScaleFreeMinimum =sqrt(3.0d0)*Pi/4.0d0
-
contains
function burkertConstructorParameters(parameters) result(self)
@@ -205,23 +86,6 @@ function burkertConstructorInternal(darkMatterHaloScale_) result(self)
!!]
- self%concentrationPrevious = -1.0d+0
- self%concentrationMinimum = 1.0d+0
- self%concentrationMaximum = 20.0d+0
- self%densityRadiusMinimum = 1.0d-3
- self%freefallRadiusMinimum = 1.0d-3
- self%radiusMinimum = 1.0d-3
- self%radialVelocityDispersionRadiusMinimum = 1.0d-3
- self%densityRadiusMaximum = 1.0d+2
- self%freefallRadiusMaximum = 1.0d+2
- self%radiusMaximum = 1.0d+2
- self%radialVelocityDispersionRadiusMaximum = 1.0d+2
- self%burkertDensityTableInitialized = .false.
- self%burkertFreefallTableInitialized = .false.
- self%burkertInverseTableInitialized = .false.
- self%burkertTableInitialized = .false.
- self%burkertRadialVelocityDispersionTableInitialized= .false.
- self%lastUniqueID = -1
! Ensure that the dark matter profile component supports a "scale" property.
if (.not.defaultDarkMatterProfileComponent%scaleIsGettable()) &
& call Error_Report &
@@ -233,1252 +97,78 @@ function burkertConstructorInternal(darkMatterHaloScale_) result(self)
& ) // &
& {introspection:location} &
& )
- ! Initialize the tabulations.
- call self%tabulate ()
- call self%inverseAngularMomentum ()
- call self%radialVelocityDispersionTabulate()
return
end function burkertConstructorInternal
- subroutine burkertAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOBurkert), intent(inout) :: self
-
- call calculationResetEvent%attach(self,burkertCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOBurkert')
- return
- end subroutine burkertAutoHook
-
subroutine burkertDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily burkert} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOBurkert), intent(inout) :: self
- if (self%burkertFreefallTableInitialized ) then
- call self%burkertFreeFall %destroy()
- call self%burkertFreeFallInverse %destroy()
- deallocate(self%burkertFreefallInverse )
- end if
- if (self%burkertDensityTableInitialized ) then
- call self%burkertDensityTable %destroy()
- call self%burkertDensityTableInverse %destroy()
- deallocate(self%burkertDensityTableInverse )
- end if
- if (self%burkertInverseTableInitialized ) then
- call self%burkertSpecificAngularMomentum %destroy()
- call self%burkertSpecificAngularMomentumInverse%destroy()
- deallocate(self%burkertSpecificAngularMomentumInverse)
- end if
- if (self%burkertTableInitialized ) then
- call self%burkertConcentrationTable %destroy()
- end if
- if (self%burkertRadialVelocityDispersionTableInitialized) then
- call self%burkertRadialVelocityDispersionTable %destroy()
- end if
!![
!!]
- if (calculationResetEvent%isAttached(self,burkertCalculationReset)) call calculationResetEvent%detach(self,burkertCalculationReset)
return
end subroutine burkertDestructor
- subroutine burkertCalculationReset(self,node,uniqueID)
+ function burkertGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Reset the dark matter profile calculation.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Kind_Numbers, only : kind_int8
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionBurkert, kinematicsDistributionBurkert
implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%specificAngularMomentumScalingsComputed=.false.
- self%maximumVelocityComputed =.false.
- self%lastUniqueID =uniqueID
- return
- end subroutine burkertCalculationReset
-
- subroutine burkertTabulate(self,concentration)
- !!{
- Tabulate properties of the Burkert halo profile which must be computed numerically.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ), optional :: concentration
- integer :: iConcentration
- logical :: retabulate
- double precision :: tableConcentration
-
- retabulate=.not.self%burkertTableInitialized
- if (present(concentration)) then
- if (concentration < self%concentrationMinimum) then
- self%concentrationMinimum=0.5d0*concentration
- retabulate=.true.
- end if
- if (concentration > self%concentrationMaximum) then
- self%concentrationMaximum=2.0d0*concentration
- retabulate=.true.
- end if
- end if
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%burkertTableNumberPoints=int(log10(self%concentrationMaximum/self%concentrationMinimum)*dble(tablePointsPerDecade))+1
- call self%burkertConcentrationTable%destroy()
- call self%burkertConcentrationTable%create(self%concentrationMinimum,self%concentrationMaximum,self%burkertTableNumberPoints,2)
- ! Loop over concentrations and populate tables.
- do iConcentration=1,self%burkertTableNumberPoints
- tableConcentration=self%burkertConcentrationTable%x(iConcentration)
- call self%burkertConcentrationTable%populate( self%profileEnergy (tableConcentration),iConcentration,table=concentrationEnergyIndex )
- call self%burkertConcentrationTable%populate(tableConcentration/self%angularMomentumScaleFree(tableConcentration),iConcentration,table=concetrationRotationNormalizationIndex)
- end do
- ! Specify that tabulation has been made.
- self%burkertTableInitialized=.true.
- end if
- return
- end subroutine burkertTabulate
-
- subroutine burkertInverseAngularMomentum(self,specificAngularMomentum)
- !!{
- Tabulates the specific angular momentum vs. radius in an Burkert profile for rapid inversion.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ), optional :: specificAngularMomentum
- integer :: iRadius
- logical :: retabulate
-
- retabulate=.not.self%burkertInverseTableInitialized
- ! If the table has not yet been made, compute and store the specific angular momenta corresponding to the minimum and maximum
- ! radii that will be tabulated by default.
- if (retabulate) then
- self%specificAngularMomentumMinimum=self%specificAngularMomentumScaleFree(self%radiusMinimum)
- self%specificAngularMomentumMaximum=self%specificAngularMomentumScaleFree(self%radiusMaximum)
- end if
- if (present(specificAngularMomentum)) then
- do while (specificAngularMomentum < self%specificAngularMomentumMinimum)
- self%radiusMinimum =0.5d0*self%radiusMinimum
- self%specificAngularMomentumMinimum=self%specificAngularMomentumScaleFree(self%radiusMinimum)
- retabulate=.true.
- end do
- do while (specificAngularMomentum > self%specificAngularMomentumMaximum)
- self%radiusMaximum =2.0d0*self%radiusMaximum
- self%specificAngularMomentumMaximum=self%specificAngularMomentumScaleFree(self%radiusMaximum)
- retabulate=.true.
- end do
- end if
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%burkertInverseTableNumberPoints=int(log10(self%radiusMaximum/self%radiusMinimum)*dble(inverseTablePointsPerDecade))+1
- ! Create a range of radii.
- call self%burkertSpecificAngularMomentum%destroy( )
- call self%burkertSpecificAngularMomentum%create (self%radiusMinimum,self%radiusMaximum,self%burkertInverseTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%burkertInverseTableNumberPoints
- call self%burkertSpecificAngularMomentum%populate( &
- & self%specificAngularMomentumScaleFree(self%burkertSpecificAngularMomentum%x(iRadius)), &
- & iRadius &
- & )
- end do
- call self%burkertSpecificAngularMomentum%reverse(self%burkertSpecificAngularMomentumInverse)
- ! Specify that tabulation has been made.
- self%burkertInverseTableInitialized=.true.
- end if
- return
- end subroutine burkertInverseAngularMomentum
-
- double precision function burkertDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- basic => node %basic ( )
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- burkertDensity = self%densityScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius)*basic%mass()/scaleRadius**3
- return
- end function burkertDensity
-
- double precision function burkertDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily
- radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius, scaleRadius
- !$GLC attributes unused :: self
-
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- radiusOverScaleRadius = + radius &
- & /scaleRadius
- burkertDensityLogSlope = - radiusOverScaleRadius /(1.0d0+radiusOverScaleRadius ) &
- & -2.0d0*radiusOverScaleRadius**2/(1.0d0+radiusOverScaleRadius**2)
- return
- end function burkertDensityLogSlope
-
- double precision function burkertEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- burkertEnclosedMass = self%enclosedMassScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius)*basic%mass()
- return
- end function burkertEnclosedMass
-
- double precision function burkertPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galactic_Structure_Options, only : enumerationStructureErrorCodeType, structureErrorCodeSuccess
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile
- double precision , parameter :: radiusSmall =1.0d-10
- double precision :: radiusOverScaleRadius , virialRadiusOverScaleRadius
-
- if (present(status)) status=structureErrorCodeSuccess
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- radiusOverScaleRadius =radius /darkMatterProfile%scale()
- virialRadiusOverScaleRadius =self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- if (radiusOverScaleRadius < radiusSmall) then
- burkertPotential= &
- & +( &
- & -Pi &
- & +2.0d0 &
- & /3.0d0 &
- & *radiusOverScaleRadius**2 &
- & ) &
- & /( &
- & -2.0d0 *atan( virialRadiusOverScaleRadius ) &
- & +2.0d0 *log (1.0d0+virialRadiusOverScaleRadius ) &
- & + log (1.0d0+virialRadiusOverScaleRadius**2) &
- & ) &
- & *virialRadiusOverScaleRadius &
- & *self%darkMatterHaloScale_%velocityVirial(node)**2
- else
- burkertPotential= &
- & +( &
- & -Pi &
- & *radiusOverScaleRadius &
- & +( &
- & +2.0d0*(1.0d0+radiusOverScaleRadius)*atan( radiusOverScaleRadius ) &
- & -2.0d0*(1.0d0+radiusOverScaleRadius)*log (1.0d0+radiusOverScaleRadius ) &
- & - (1.0d0-radiusOverScaleRadius)*log (1.0d0+radiusOverScaleRadius **2) &
- & ) &
- & ) &
- & /radiusOverScaleRadius &
- & /( &
- & -2.0d0 *atan( virialRadiusOverScaleRadius ) &
- & +2.0d0 *log (1.0d0+virialRadiusOverScaleRadius ) &
- & + log (1.0d0+virialRadiusOverScaleRadius**2) &
- & ) &
- & *virialRadiusOverScaleRadius &
- & *self%darkMatterHaloScale_%velocityVirial(node)**2
- end if
- return
- end function burkertPotential
-
- double precision function burkertCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- burkertCircularVelocity=sqrt(gravitationalConstantGalacticus*self%enclosedMass(node,radius)/radius)
- else
- burkertCircularVelocity=0.0d0
- end if
- return
- end function burkertCircularVelocity
-
- double precision function burkertCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- ! The radius (in units of the scale radius) at which the rotation speed peaks in a Burkert halo.
- double precision , parameter :: radiusMaximum =3.2446257246042642d0
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: scaleRadius
-
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Check if maximum velocity is already computed. Compute and store if not.
- if (.not.self%maximumVelocityComputed) then
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true. )
- scaleRadius = darkMatterProfile%scale ( )
- self%maximumVelocityPrevious = self %circularVelocity (node ,radiusMaximum*scaleRadius)
- self%maximumVelocityComputed = .true.
- end if
- burkertCircularVelocityMaximum=self%maximumVelocityPrevious
- return
- end function burkertCircularVelocityMaximum
-
- double precision function burkertRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- ! The radius (in units of the scale radius) at which the rotation speed peaks in a Burkert halo.
- double precision , parameter :: radiusMaximum =3.2446257246042642d0
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
-
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- burkertRadiusCircularVelocityMaximum = + radiusMaximum &
- & *darkMatterProfile%scale ( )
- return
- end function burkertRadiusCircularVelocityMaximum
-
- double precision function burkertRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- if (radius > 0.0d0) then
- call self%radialVelocityDispersionTabulate(radiusOverScaleRadius)
- burkertRadialVelocityDispersion=self%burkertRadialVelocityDispersionTable%interpolate(radiusOverScaleRadius)
- else
- burkertRadialVelocityDispersion=0.0d0
- end if
- ! Compute the normalization factor.
- call burkertMassNormalizationFactor(self,virialRadiusOverScaleRadius)
- ! Evaluate the radial velocity dispersion.
- burkertRadialVelocityDispersion=+burkertRadialVelocityDispersion &
- & *sqrt( &
- & +self%burkertNormalizationFactorPrevious &
- & *virialRadiusOverScaleRadius &
- & ) &
- & *self%darkMatterHaloScale_%velocityVirial(node)
- return
- end function burkertRadialVelocityDispersion
-
- double precision function burkertRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily
- specificAngularMomentum} (given in units of km s$^{-1}$ Mpc)
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: specificAngularMomentumScaleFree
-
- ! Return immediately with zero radius for non-positive specific angular momenta.
- if (specificAngularMomentum <= 0.0d0) then
- burkertRadiusFromSpecificAngularMomentum=0.0d0
- return
- end if
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Check if scalings are already computed. Compute and store if not.
- if (.not.self%specificAngularMomentumScalingsComputed) then
- ! Flag that scale quantities are now computed.
- self%specificAngularMomentumScalingsComputed=.true.
-
- ! Get the dark matter profile.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- self%specificAngularMomentumLengthScale=darkMatterProfile%scale()
-
- ! Get the specific angular momentum scale.
- self%specificAngularMomentumScale=self%specificAngularMomentumLengthScale &
- & *self%circularVelocity(node,self%specificAngularMomentumLengthScale)
- end if
-
- ! Compute the specific angular momentum in scale free units (using the scale length for distances and sqrt(G M(r_scale) /
- ! r_scale) for velocities).
- specificAngularMomentumScaleFree=specificAngularMomentum/self%specificAngularMomentumScale
-
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%inverseAngularMomentum(specificAngularMomentumScaleFree)
-
- ! Interpolate to get the dimensionless radius at which this specific angular momentum is found.
- burkertRadiusFromSpecificAngularMomentum=self%burkertSpecificAngularMomentumInverse%interpolate(specificAngularMomentumScaleFree)
-
- ! Convert to a physical radius.
- burkertRadiusFromSpecificAngularMomentum=burkertRadiusFromSpecificAngularMomentum*self%specificAngularMomentumLengthScale
- return
- end function burkertRadiusFromSpecificAngularMomentum
-
- double precision function burkertRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Find the concentration parameter of this halo.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
-
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%tabulate(concentration)
-
- ! Find the rotation normalization by interpolation.
- burkertRotationNormalization=+self%burkertConcentrationTable%interpolate(concentration,table=concetrationRotationNormalizationIndex) &
- & /self%darkMatterHaloScale_%radiusVirial(node)
- return
- end function burkertRotationNormalization
-
- double precision function burkertEnergy(self,node)
- !!{
- Return the energy of an Burkert halo density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- class (nodeComponentBasic ), pointer :: basic
- double precision :: concentration
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Find the concentration parameter of this halo.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
-
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%tabulate(concentration)
-
- ! Find the energy by interpolation.
- burkertEnergy=self%burkertConcentrationTable%interpolate(concentration,table=concentrationEnergyIndex) &
- & *basic%mass()*self%darkMatterHaloScale_%velocityVirial(node)**2
- return
- end function burkertEnergy
-
- double precision function burkertAngularMomentumScaleFree(self,concentration)
- !!{
- Returns the total angular momentum (in units of the virial mass times scale radius times [assumed constant] rotation speed)
- in an Burkert dark matter profile with given {\normalfont \ttfamily concentration}. This is given by:
- \begin{equation}
- J = \left. \int_0^c 4 \pi x^3 \rho(x) \d x \right/ \int_0^c 4 \pi x^2 \rho(x) \d x,
- \end{equation}
- where $x$ is radius in units of the scale radius and $c$ is concentration. This can be evaluated to give
- \begin{equation}
- J = \left. \left[ 2 \tan^{-1} c + 2 \log(1+c) + \log(1+c^2) - 4c \right] \right/ \left[ 2 \tan^{-1} c - 2 \log(1+c) - \log(1+c^2) \right].
- \end{equation}
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: concentration
- !$GLC attributes unused :: self
-
- burkertAngularMomentumScaleFree=+( &
- & +2.0d0*atan( concentration ) &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & -4.0d0* concentration &
- & ) &
- & /( &
- & +2.0d0*atan( concentration ) &
- & -2.0d0*log (1.0d0+concentration ) &
- & - log (1.0d0+concentration**2) &
- & )
- return
- end function burkertAngularMomentumScaleFree
-
- double precision function burkertSpecificAngularMomentumScaleFree(self,radius)
- !!{
- Returns the specific angular momentum, normalized to unit scale length and unit velocity at the scale radius, at position
- {\normalfont \ttfamily radius} (in units of the scale radius) in an Burkert profile.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: radius
-
- burkertSpecificAngularMomentumScaleFree=sqrt(radius*self%enclosedMassScaleFree(radius,1.0d0))
- return
- end function burkertSpecificAngularMomentumScaleFree
-
- double precision function burkertEnclosedMassScaleFree(self,radius,concentration)
- !!{
- Returns the enclosed mass (in units of the virial mass) in an Burkert dark matter profile with given {\normalfont \ttfamily
- concentration} at the given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: concentration , radius
- double precision , parameter :: minimumRadiusForExactSolution=1.0d-4
-
- if (radius < minimumRadiusForExactSolution) then
- ! Use a series solution for small radii.
- burkertEnclosedMassScaleFree=(4.0d0/3.0d0)*radius**3*(1.0d0-radius)
- else
- ! Use the exact solution.
- burkertEnclosedMassScaleFree=( &
- & +2.0d0*log (1.0d0+radius ) &
- & + log (1.0d0+radius**2) &
- & -2.0d0*atan( radius ) &
- & )
- end if
- ! Compute the mass profile normalization factor.
- call burkertMassNormalizationFactor(self,concentration)
- ! Evaluate the scale-free enclosed mass.
- burkertEnclosedMassScaleFree=burkertEnclosedMassScaleFree*self%burkertNormalizationFactorPrevious
- return
- end function burkertEnclosedMassScaleFree
-
- subroutine burkertMassNormalizationFactor(self,concentration)
- !!{
- Compute the normalization factor for the burkert mass profile.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: concentration
- ! Precomputed Burkert normalization factor for unit concentration.
- double precision , parameter :: burkertNormalizationFactorUnitConcentration=1.0d0/(3.0d0*log(2.0d0)-2.0d0*atan(1.0d0))
-
- ! Check if we were called with a different concentration compared to the previous call.
- if (concentration /= self%concentrationPrevious) then
- ! We were, so recompute the normalization factor.
- if (concentration == 1.0d0) then
- self%burkertNormalizationFactorPrevious=burkertNormalizationFactorUnitConcentration
- else
- self%burkertNormalizationFactorPrevious=+1.0d0 &
- & /( &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- end if
- self%concentrationPrevious=concentration
- end if
- return
- end subroutine burkertMassNormalizationFactor
-
- double precision function burkertDensityScaleFree(self,radius,concentration)
- !!{
- Returns the density (in units such that the virial mass and scale length are unity) in an Burkert dark matter profile with
- given {\normalfont \ttfamily concentration} at the given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: concentration, radius
- !$GLC attributes unused :: self
-
- burkertDensityScaleFree=+1.0d0 &
- & /(1.0d0+radius ) &
- & /(1.0d0+radius**2) &
- & /Pi &
- & /( &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- return
- end function burkertDensityScaleFree
-
- double precision function burkertProfileEnergy(self,concentration)
- !!{
- Computes the total energy of an Burkert profile halo of given {\normalfont \ttfamily concentration} using the methods of
- \citeauthor{cole_hierarchical_2000}~(\citeyear{cole_hierarchical_2000}; their Appendix~A).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Integration , only : integrator
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: concentration
- type (integrator ) :: integratorPotential , integratorJeans , &
- & integratorKinetic
- double precision :: jeansEquationIntegral , kineticEnergy , &
- & kineticEnergyIntegral , potentialEnergy , &
- & potentialEnergyIntegral, radiusMaximum , &
- & radiusMinimum , concentrationParameter
-
- ! Compute the potential energy.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- concentrationParameter =concentration
- integratorPotential =integrator(burkertPotentialEnergyIntegrand,toleranceRelative=1.0d-3)
- potentialEnergyIntegral=integratorPotential%integrate(radiusMinimum,radiusMaximum)
- potentialEnergy =-0.5d0*(1.0d0/concentration+potentialEnergyIntegral)
- ! Compute the velocity dispersion at the virial radius.
- radiusMinimum =concentration
- radiusMaximum =100.0d0*concentration
- concentrationParameter=concentration
- integratorJeans =integrator(burkertJeansEquationIntegrand,toleranceRelative=1.0d-3)
- jeansEquationIntegral =integratorJeans%integrate(radiusMinimum,radiusMaximum)
- ! Compute the kinetic energy.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- concentrationParameter=concentration
- integratorKinetic =integrator(burkertKineticEnergyIntegrand,toleranceRelative=1.0d-3)
- kineticEnergyIntegral =integratorKinetic%integrate(radiusMinimum,radiusMaximum)
- kineticEnergy =2.0d0*Pi*(jeansEquationIntegral*concentration**3+kineticEnergyIntegral)
- ! Compute the total energy.
- burkertProfileEnergy =(potentialEnergy+kineticEnergy)*concentration
- return
-
- contains
-
- double precision function burkertPotentialEnergyIntegrand(radius)
- !!{
- Integrand for Burkert profile potential energy.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- burkertPotentialEnergyIntegrand=(self%enclosedMassScaleFree(radius,concentrationParameter)/radius)**2
- return
- end function burkertPotentialEnergyIntegrand
-
- double precision function burkertKineticEnergyIntegrand(radius)
- !!{
- Integrand for Burkert profile kinetic energy.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- burkertKineticEnergyIntegrand=+self%EnclosedMassScaleFree(radius,concentrationParameter) &
- & *self%densityScaleFree (radius,concentrationParameter) &
- & *radius
- return
- end function burkertKineticEnergyIntegrand
-
- double precision function burkertJeansEquationIntegrand(radius)
- !!{
- Integrand for Burkert profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- burkertJeansEquationIntegrand=+self%enclosedMassScaleFree(radius,concentrationParameter) &
- & *self%densityScaleFree (radius,concentrationParameter) &
- & /radius**2
- return
- end function burkertJeansEquationIntegrand
-
- end function burkertProfileEnergy
-
- double precision function burkertKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the Burkert density profile at the specified {\normalfont \ttfamily waveNumber} (given in Mpc$^{-1}$), using the
- expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- use :: Exponential_Integrals , only : Cosine_Integral , Exponential_Integral, Sine_Integral
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: concentration , radiusScale, &
- & waveNumberScaleFree
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
-
- ! Compute the concentration parameter.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the dimensionless wavenumber.
- waveNumberScaleFree=waveNumber*radiusScale
-
- ! Compute the Fourier transformed profile.
- burkertKSpace=dimag( &
- & +( &
- & + 2.0d0 *exp(-dcmplx(0.0d0,1.0d0)*waveNumberScaleFree)*Cosine_Integral ( waveNumberScaleFree) &
- & - 2.0d0 *exp(-dcmplx(0.0d0,1.0d0)*waveNumberScaleFree)*Cosine_Integral ( (+1.0d0+concentration)*waveNumberScaleFree) &
- & +dcmplx(1.0d0,1.0d0) &
- & *( &
- & -dcmplx(0.0d0,1.0d0)*exp(+ waveNumberScaleFree)*Pi &
- & - exp(+ waveNumberScaleFree)*Exponential_Integral( -1.0d0 *waveNumberScaleFree) &
- & +dcmplx(0.0d0,1.0d0)*exp(- waveNumberScaleFree)*Exponential_Integral( +1.0d0 *waveNumberScaleFree) &
- & + exp(+ waveNumberScaleFree)*Exponential_Integral(dcmplx(-1.0d0,concentration)*waveNumberScaleFree) &
- & -dcmplx(0.0d0,1.0d0)*exp(- waveNumberScaleFree)*Exponential_Integral(dcmplx(+1.0d0,concentration)*waveNumberScaleFree) &
- & +dcmplx(1.0d0,1.0d0)*exp(-dcmplx(0.0d0,1.0d0)*waveNumberScaleFree)*Sine_Integral ( waveNumberScaleFree) &
- & -dcmplx(1.0d0,1.0d0)*exp(-dcmplx(0.0d0,1.0d0)*waveNumberScaleFree)*Sine_Integral ( (+1.0d0+concentration)*waveNumberScaleFree) &
- & ) &
- & ) &
- & /( &
- & +waveNumberScaleFree &
- & *( &
- & -2.0d0*atan( concentration ) &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & ) &
- & ) &
- & )
- return
- end function burkertKSpace
-
- double precision function burkertFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the Burkert density profile at the specified {\normalfont \ttfamily time} (given in Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration , freefallTimeScaleFree, &
- & radiusScale , timeScale , &
- & velocityScale
-
- ! For non-positive freefall times, return a zero freefall radius immediately.
- if (time <= 0.0d0) then
- burkertFreefallRadius=0.0d0
- return
- end if
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
- ! Get the concentration.
- concentration=+self%darkMatterHaloScale_%radiusVirial (node) &
- & /radiusScale
- ! Get the virial velocity.
- velocityScale=+self%darkMatterHaloScale_%velocityVirial(node)
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & /sqrt( &
- & + concentration &
- & ) &
- & *sqrt( &
- & -2.0d0*atan( concentration ) &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & )
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree)
- ! The freefall time is finite at zero radius in this profile. If the requested time is less than this, return zero radius.
- if (freefallTimeScaleFree < freefallTimeScaleFreeMinimum) then
- burkertFreefallRadius=0.0d0
- else
- burkertFreefallRadius=self%burkertFreefallInverse%interpolate(freefallTimeScaleFree)*radiusScale
- end if
- return
- end function burkertFreefallRadius
-
- double precision function burkertFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the Burkert density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration , freefallTimeScaleFree, &
- & radiusScale , timeScale , &
- & velocityScale
-
- ! For non-positive freefall times, return the limiting value for small radii.
- if (time <= 0.0d0) then
- burkertFreefallRadiusIncreaseRate=0.0d0
- return
- end if
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
- ! Get the concentration.
- concentration=+self%darkMatterHaloScale_%radiusVirial (node) &
- & /radiusScale
- ! Get the virial velocity.
- velocityScale=+self%darkMatterHaloScale_%velocityVirial(node)
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & /sqrt( &
- & + concentration &
- & ) &
- & *sqrt( &
- & -2.0d0*atan( concentration ) &
- & +2.0d0*log (1.0d0+concentration ) &
- & + log (1.0d0+concentration**2) &
- & )
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree)
- ! The freefall time is finite at zero radius in this profile. If the requested time is less than this, return zero radius.
- if (freefallTimeScaleFree < freefallTimeScaleFreeMinimum) then
- burkertFreefallRadiusIncreaseRate=0.0d0
- else
- burkertFreefallRadiusIncreaseRate=self%burkertFreefallInverse%interpolateGradient(freefallTimeScaleFree)*radiusScale/timeScale
- end if
- return
- end function burkertFreefallRadiusIncreaseRate
-
- subroutine burkertFreefallTabulate(self,freefallTimeScaleFree)
- !!{
- Tabulates the freefall time vs. freefall radius for Burkert halos.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: freefallTimeScaleFree
- logical :: retabulate
- integer :: iRadius
- double precision :: freefallTime
-
- retabulate=.not.self%burkertFreefallTableInitialized
- ! If the table has not yet been made, compute and store the freefall corresponding to the minimum and maximum
- ! radii that will be tabulated by default.
- if (retabulate) then
- self%freefallTimeMinimum=self%freefallTimeScaleFree(self%freefallRadiusMinimum)
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusMaximum)
- end if
- do while (freefallTimeScaleFree > self%freefallTimeMaximum)
- self%freefallRadiusMaximum=2.0d0*self%freefallRadiusMaximum
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusMaximum)
- retabulate=.true.
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%burkertFreefallTableNumberPoints=int(log10(self%freefallRadiusMaximum/self%freefallRadiusMinimum)*dble(freefallTablePointsPerDecade))+1
- ! Create the table.
- call self%burkertFreefall%destroy( )
- call self%burkertFreefall%create (self%freefallRadiusMinimum,self%freefallRadiusMaximum,self%burkertFreefallTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%burkertFreefallTableNumberPoints
- freefallTime=self%freefallTimeScaleFree(self%burkertFreefall%x(iRadius))
- if (iRadius > 1) freefallTime=max(freefallTime,self%burkertFreefall%y(iRadius-1))
- call self%burkertFreefall%populate(freefallTime,iRadius)
- end do
- call self%burkertFreefall%reverse(self%burkertFreefallInverse)
- ! Specify that tabulation has been made.
- self%burkertFreefallTableInitialized=.true.
- end if
- return
- end subroutine burkertFreefallTabulate
-
- double precision function burkertFreefallTimeScaleFree(self,radius)
- !!{
- Compute the freefall time in a scale-free Burkert halo.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (integrator ) :: integrator_
- double precision :: radiusEnd , radiusStart
- !$GLC attributes unused :: self
-
- radiusStart =radius
- radiusEnd =0.0d0
- integrator_ =integrator (burkertFreefallTimeScaleFreeIntegrand,toleranceRelative=1.0d-5)
- burkertFreefallTimeScaleFree=integrator_%integrate(radiusEnd ,radiusStart )
- return
-
- contains
-
- double precision function burkertFreefallTimeScaleFreeIntegrand(radius)
- !!{
- Integrand function used for finding the free-fall time in Burkert halos.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
- double precision, parameter :: radiusSmall =1.0d-2
- double precision :: potential , potentialStart, &
- & potentialDifference
-
- if (radius < radiusSmall) then
- potential =+Pi &
- & -2.0d0*radius **2/3.0d0 &
- & + radius **3/3.0d0
- else
- potential =-( &
- & -Pi * radius &
- & +2.0d0*(1.0d0+radius )*atan( radius ) &
- & -2.0d0*(1.0d0+radius )*log (1.0d0+radius ) &
- & - (1.0d0-radius )*log (1.0d0+radius **2) &
- & ) &
- & / radius
- end if
- if (radiusStart < radiusSmall) then
- potentialStart=+Pi &
- & -2.0d0*radiusStart**2/3.0d0 &
- & + radiusStart**3/3.0d0
- else
- potentialStart=-( &
- & -Pi * radiusStart &
- & +2.0d0*(1.0d0+radiusStart)*atan( radiusStart ) &
- & -2.0d0*(1.0d0+radiusStart)*log (1.0d0+radiusStart ) &
- & - (1.0d0-radiusStart)*log (1.0d0+radiusStart**2) &
- & ) &
- & / radiusStart
- end if
- potentialDifference=+potential-potentialStart
- if (potentialDifference > 0.0d0) then
- burkertFreefallTimeScaleFreeIntegrand=1.0d0/sqrt(2.0d0*potentialDifference)
- else
- burkertFreefallTimeScaleFreeIntegrand=0.0d0
- end if
- return
- end function burkertFreefallTimeScaleFreeIntegrand
-
- end function burkertFreefallTimeScaleFree
-
- double precision function burkertRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: radiusMinimumActual, radiusMaximumActual, &
- & radiusScale , concentration
-
- basic => node %basic ( )
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- radiusScale = darkMatterProfile %scale ( )
- concentration = +self %darkMatterHaloScale_%radiusVirial ( node ) &
- & /radiusScale
- radiusMinimumActual=0.0d0
- radiusMaximumActual=concentration
- if (present(radiusMinimum)) radiusMinimumActual=radiusMinimum/radiusScale
- if (present(radiusMaximum)) radiusMaximumActual=radiusMaximum/radiusScale
- burkertRadialMoment=+basic%mass() &
- & *radiusScale**(moment-2.0d0) &
- & *( &
- & +radialMoment(radiusMaximumActual) &
- & -radialMoment(radiusMinimumActual) &
- & )
- return
-
- contains
-
- double precision function radialMoment(radius)
- !!{
- Evaluate the radial moment in the Burkert profile.
- !!}
- use :: Hypergeometric_Functions, only : Hypergeometric_2F1
- use :: Numerical_Comparison , only : Values_Agree
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- if (Values_Agree(moment,1.0d0,absTol=1.0d-6)) then
- radialMoment=+( &
- & -2.0d0* log(1.0d0+radius ) &
- & + log(1.0d0+radius **2) &
- & +2.0d0*atan( radius ) &
- & ) &
- & /4.0d0 &
- & /Pi &
- & /( &
- & +2.0d0* log(1.0d0+concentration ) &
- & + log(1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- else if (Values_Agree(moment,2.0d0,absTol=1.0d-6)) then
- radialMoment=+( &
- & +2.0d0* log(1.0d0+radius ) &
- & + log(1.0d0+radius **2) &
- & -2.0d0*atan( radius ) &
- & ) &
- & /4.0d0 &
- & /Pi &
- & /( &
- & +2.0d0* log(1.0d0+concentration ) &
- & + log(1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- else if (Values_Agree(moment,3.0d0,absTol=1.0d-6)) then
- radialMoment=+( &
- & +4.0d0* radius &
- & -2.0d0* log(1.0d0+radius ) &
- & - log(1.0d0+radius **2) &
- & -2.0d0*atan( radius ) &
- & ) &
- & /4.0d0 &
- & /Pi &
- & /( &
- & +2.0d0* log(1.0d0+concentration ) &
- & + log(1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- else
- radialMoment=+ radius** (1.0d0+moment) &
- & *( &
- & - radius &
- & *Hypergeometric_2F1([1.0d0,0.5d0*(2.0d0+moment)],[0.5d0*(4.0d0+moment)],-radius**2)/(2.0d0+moment) &
- & +Hypergeometric_2F1([1.0d0,0.5d0*(1.0d0+moment)],[0.5d0*(3.0d0+moment)],-radius**2)/(1.0d0+moment) &
- & +Hypergeometric_2F1([1.0d0, (1.0d0+moment)],[ (2.0d0+moment)],-radius )/(1.0d0+moment) &
- & ) &
- & /2.0d0 &
- & /Pi &
- & /( &
- & +2.0d0* log(1.0d0+concentration ) &
- & + log(1.0d0+concentration**2) &
- & -2.0d0*atan( concentration ) &
- & )
- end if
- return
- end function radialMoment
-
- end function burkertRadialMoment
-
- double precision function burkertRadiusEnclosingDensity(self,node,density)
- !!{
- Null implementation of function to compute the radius enclosing a given density for Burkert dark matter halo profiles.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOBurkert ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- class (nodeComponentBasic ), pointer :: basic
- double precision :: densityScaleFree , radiusScale, &
- & concentration
-
- basic => node %basic ( )
- darkMatterProfile => node %darkMatterProfile (autoCreate=.true.)
- radiusScale = darkMatterProfile %scale ( )
- concentration = +self %darkMatterHaloScale_%radiusVirial ( node ) &
- & /radiusScale
- densityScaleFree = +density &
- & *radiusScale **3 &
- & /basic %mass ( ) &
- & /self %enclosedMassScaleFree(1.0d0,concentration)
- call self%radiusEnclosingDensityTabulate(densityScaleFree)
- burkertRadiusEnclosingDensity=+self%burkertDensityTableInverse%interpolate(-densityScaleFree) &
- & *radiusScale
- return
- end function burkertRadiusEnclosingDensity
-
- subroutine burkertRadiusEnclosingDensityTabulate(self,densityScaleFree)
- !!{
- Tabulates the radius vs. enclosed density for Burkert halos.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: densityScaleFree
- logical :: retabulate
- integer :: iRadius
-
- retabulate=.not.self%burkertDensityTableInitialized
- ! If the table has not yet been made, compute and store the enclosed density corresponding to the minimum and maximum radii
- ! that will be tabulated by default.
- if (retabulate) then
- self%densityMaximum=3.0d0*self%enclosedMassScaleFree(self%densityRadiusMinimum,1.0d0)/self%densityRadiusMinimum**3/4.0d0/Pi
- self%densityMinimum=3.0d0*self%enclosedMassScaleFree(self%densityRadiusMaximum,1.0d0)/self%densityRadiusMaximum**3/4.0d0/Pi
- end if
- do while (densityScaleFree < self%densityMinimum)
- self%densityRadiusMaximum=2.0d0*self%densityRadiusMaximum
- self%densityMinimum =3.0d0*self%enclosedMassScaleFree(self%densityRadiusMaximum,1.0d0)/self%densityRadiusMaximum**3/4.0d0/Pi
- retabulate =.true.
- end do
- do while (densityScaleFree > self%densityMaximum)
- self%densityRadiusMinimum=0.5d0*self%densityRadiusMinimum
- self%densityMaximum =3.0d0*self%enclosedMassScaleFree(self%densityRadiusMinimum,1.0d0)/self%densityRadiusMinimum**3/4.0d0/Pi
- retabulate =.true.
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%burkertDensityTableNumberPoints=int(log10(self%densityRadiusMaximum/self%densityRadiusMinimum)*dble(densityTablePointsPerDecade))+1
- ! Create the table.
- call self%burkertDensityTable%destroy( )
- call self%burkertDensityTable%create (self%densityRadiusMinimum,self%densityRadiusMaximum,self%burkertDensityTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%burkertDensityTableNumberPoints
- call self%burkertDensityTable%populate( &
- & -3.0d0 &
- & /4.0d0 &
- & /Pi &
- & *self%enclosedMassScaleFree(self%burkertDensityTable%x(iRadius),1.0d0) &
- & / self%burkertDensityTable%x(iRadius) **3, &
- & iRadius &
- & )
- end do
- call self%burkertDensityTable%reverse(self%burkertDensityTableInverse)
- ! Specify that tabulation has been made.
- self%burkertDensityTableInitialized=.true.
- end if
- return
- end subroutine burkertRadiusEnclosingDensityTabulate
-
- double precision function burkertRadialVelocityDispersionScaleFree(self,radius)
- !!{
- Compute the radial velocity dispersion in a scale-free Burkert halo.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusTiny =1.0d-9, radiusLarge =5.0d3
- double precision :: radiusMinimum , radiusMaximum
- type (integrator ) :: integrator_
- !$GLC attributes unused :: self
-
- radiusMinimum =max( radius,radiusTiny )
- radiusMaximum =max(10.0d0*radius,radiusLarge)
- integrator_ =integrator(burkertJeansEquationIntegrand,toleranceRelative=1.0d-6)
- burkertRadialVelocityDispersionScaleFree=sqrt( &
- & +integrator_%integrate(radiusMinimum,radiusMaximum) &
- & *(1.0d0+radius ) &
- & *(1.0d0+radius**2) &
- & )
- return
-
- contains
-
- double precision function burkertJeansEquationIntegrand(radius)
- !!{
- Integrand for Burkert drak matter profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- burkertJeansEquationIntegrand=+( &
- & +2.0d0*log (1.0d0+radius ) &
- & + log (1.0d0+radius**2) &
- & -2.0d0*atan( radius ) &
- & ) &
- & /(1.0d0+radius ) &
- & /(1.0d0+radius**2) &
- & / radius**2
- else
- burkertJeansEquationIntegrand=0.0d0
- end if
- return
- end function burkertJeansEquationIntegrand
-
- end function burkertRadialVelocityDispersionScaleFree
-
- subroutine burkertRadialVelocityDispersionTabulate(self,radius)
- !!{
- Tabulates the radial velocity dispersion vs. radius for Burkert halos.
- !!}
- implicit none
- class (darkMatterProfileDMOBurkert), intent(inout) :: self
- double precision , intent(in ), optional :: radius
- logical :: retabulate
- integer :: iRadius
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionBurkert ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOBurkert ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
+ !![
+
+ !!]
- retabulate=.not.self%burkertRadialVelocityDispersionTableInitialized
- if (present(radius)) then
- do while (radius < self%radialVelocityDispersionRadiusMinimum)
- self%radialVelocityDispersionRadiusMinimum=0.5d0*self%radialVelocityDispersionRadiusMinimum
- retabulate=.true.
- end do
- do while (radius > self%radialVelocityDispersionRadiusMaximum)
- self%radialVelocityDispersionRadiusMaximum=2.0d0*self%radialVelocityDispersionRadiusMaximum
- retabulate=.true.
- end do
- end if
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%burkertRadialVelocityDispersionTableNumberPoints=int(log10(self%radialVelocityDispersionRadiusMaximum/self%radialVelocityDispersionRadiusMinimum) &
- & *dble(radialVelocityDispersionTablePointsPerDecade ) &
- & )+1
- ! Create the table.
- call self%burkertRadialVelocityDispersionTable%destroy( )
- call self%burkertRadialVelocityDispersionTable%create (self%radialVelocityDispersionRadiusMinimum ,self%radialVelocityDispersionRadiusMaximum, &
- & self%burkertRadialVelocityDispersionTableNumberPoints )
- ! Loop over radii and populate tables.
- do iRadius=1,self%burkertRadialVelocityDispersionTableNumberPoints
- call self%burkertRadialVelocityDispersionTable%populate(self%radialVelocityDispersionScaleFree(self%burkertRadialVelocityDispersionTable%x(iRadius)),iRadius)
- end do
- ! Specify that tabulation has been made.
- self%burkertRadialVelocityDispersionTableInitialized=.true.
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionBurkert :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionBurkert)
+ basic => node%basic ()
+ darkMatterProfile => node%darkMatterProfile()
+ !![
+
+
+ massDistributionBurkert( &
+ & mass =basic %mass ( ), &
+ & radiusOuter =self %darkMatterHaloScale_%radiusVirial (node), &
+ & scaleLength =darkMatterProfile%scale ( ), &
+ & componentType= componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionBurkert( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end subroutine burkertRadialVelocityDispersionTabulate
+ end function burkertGet
diff --git a/source/dark_matter_profiles_DMO.Einasto.F90 b/source/dark_matter_profiles_DMO.Einasto.F90
index 4196c60098..8d26fed2de 100644
--- a/source/dark_matter_profiles_DMO.Einasto.F90
+++ b/source/dark_matter_profiles_DMO.Einasto.F90
@@ -21,20 +21,16 @@
An implementation of ``Einasto'' dark matter halo profiles.
!!}
- use :: Numerical_Interpolation, only : interpolator
- use :: Root_Finder , only : rootFinder
-
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
- A dark matter profile DMO class which implements the Einasto density profile (e.g. \citealt{cardone_spherical_2005}):
- \begin{equation}
- \rho_\mathrm{dark matter}(r) = \rho_{-2} \exp \left( - {2 \over \alpha} \left[ \left( {r \over r_{-2}} \right)^\alpha - 1
- \right] \right),
- \end{equation}
- normalized such that the total mass of the \gls{node} is enclosed with the virial radius and with the characteristic length
- $r_{-2} = r_\mathrm{virial}/c$ where $c$ is the halo concentration (see \refPhysics{darkMatterProfileConcentration}). The
- shape parameter, $\alpha$, is set using the density profile shape method (see \refPhysics{darkMatterProfileShape}).
+ A dark matter profile DMO class which builds \refClass{massDistributionEinasto} objects to compute the Einasto density profile
+ (e.g. \citealt{cardone_spherical_2005}), normalized such that the total mass of the \gls{node} is enclosed with the virial
+ radius and with the characteristic length $r_{-2} = r_\mathrm{virial}/c$ where $c$ is the halo concentration (see
+ \refPhysics{darkMatterProfileConcentration}). The shape parameter, $\alpha$, is set using the density profile shape method
+ (see \refPhysics{darkMatterProfileShape}).
!!]
@@ -42,112 +38,10 @@
!!{
A dark matter halo profile class implementing ``Einasto'' dark matter halos.
!!}
- private
- ! Tables for specific angular momentum vs. radius table
- double precision :: angularMomentumTableRadiusMinimum
- double precision :: angularMomentumTableRadiusMaximum
- double precision :: angularMomentumTableAlphaMinimum
- double precision :: angularMomentumTableAlphaMaximum
- logical :: angularMomentumTableInitialized
- integer :: angularMomentumTableAlphaCount , angularMomentumTableRadiusCount
- double precision , allocatable, dimension(: ) :: angularMomentumTableAlpha , angularMomentumTableRadius
- double precision , allocatable, dimension(:,:) :: angularMomentumTable
- type (interpolator), allocatable :: angularMomentumTableAlphaInterpolator
- type (interpolator), allocatable, dimension(: ) :: angularMomentumTableRadiusInterpolator
- ! Tables for freefall time vs. radius table
- double precision :: freefallRadiusTableRadiusMinimum
- double precision :: freefallRadiusTableRadiusMaximum
- double precision :: freefallRadiusTableAlphaMinimum
- double precision :: freefallRadiusTableAlphaMaximum
- logical :: freefallRadiusTableInitialized
- integer :: freefallRadiusTableAlphaCount , freefallRadiusTableRadiusCount
- double precision , allocatable, dimension(: ) :: freefallRadiusTableAlpha , freefallRadiusTableRadius
- double precision , allocatable, dimension(:,:) :: freefallRadiusTable
- double precision :: freefallTimeMaximum , freefallTimeMinimum
- type (interpolator), allocatable :: freefallRadiusTableAlphaInterpolator
- type (interpolator), allocatable, dimension(: ) :: freefallRadiusTableRadiusInterpolator
- ! Tables for radial velocity dispersion vs. radius table
- double precision :: radialVelocityDispersionRadiusMinimum
- double precision :: radialVelocityDispersionRadiusMaximum
- double precision :: radialVelocityDispersionAlphaMinimum
- double precision :: radialVelocityDispersionAlphaMaximum
- logical :: radialVelocityDispersionTableInitialized
- integer :: radialVelocityDispersionTableAlphaCount , radialVelocityDispersionTableRadiusCount
- double precision , allocatable, dimension(: ) :: radialVelocityDispersionTableAlpha , radialVelocityDispersionTableRadius
- double precision , allocatable, dimension(:,:) :: radialVelocityDispersionTable
- type (interpolator), allocatable :: radialVelocityDispersionTableAlphaInterpolator, radialVelocityDispersionTableRadiusInterpolator
- ! Tables for energy as a function of concentration and α.
- double precision :: energyTableConcentrationMinimum
- double precision :: energyTableConcentrationMaximum
- double precision :: energyTableAlphaMinimum
- double precision :: energyTableAlphaMaximum
- logical :: energyTableInitialized
- integer :: energyTableAlphaCount , energyTableConcentrationCount
- double precision , allocatable, dimension(: ) :: energyTableAlpha , energyTableConcentration
- double precision , allocatable, dimension(:,:) :: energyTable
- type (interpolator), allocatable :: energyTableAlphaInterpolator , energyTableConcentrationInterpolator
- ! Tables for specific Fourier transform of density profile as a function of α and radius.
- double precision :: fourierProfileTableConcentrationMinimum
-
- double precision :: fourierProfileTableConcentrationMaximum
- double precision :: fourierProfileTableWavenumberMinimum
- double precision :: fourierProfileTableWavenumberMaximum
- double precision :: fourierProfileTableAlphaMinimum
- double precision :: fourierProfileTableAlphaMaximum
- logical :: fourierProfileTableInitialized
- integer :: fourierProfileTableAlphaCount , fourierProfileTableConcentrationCount , &
- & fourierProfileTableWavenumberCount
- double precision , allocatable, dimension(: ) :: fourierProfileTableAlpha , fourierProfileTableConcentration , &
- & fourierProfileTableWavenumber
- double precision , allocatable, dimension(:,:,:) :: fourierProfileTable
- type (interpolator), allocatable :: fourierProfileTableAlphaInterpolator , fourierProfileTableConcentrationInterpolator , &
- & fourierProfileTableWavenumberInterpolator
- ! Root finders.
- type (rootFinder ) :: finderEnclosedDensity , finderVelocityPeak
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
- !![
-
-
-
-
-
-
-
-
-
-
-
-
-
- !!]
- final :: einastoDestructor
- procedure :: density => einastoDensity
- procedure :: densityLogSlope => einastoDensityLogSlope
- procedure :: radialMoment => einastoRadialMoment
- procedure :: enclosedMass => einastoEnclosedMass
- procedure :: radiusEnclosingDensity => einastoRadiusEnclosingDensity
- procedure :: potential => einastoPotential
- procedure :: circularVelocity => einastoCircularVelocity
- procedure :: circularVelocityMaximum => einastoCircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => einastoRadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => einastoRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => einastoRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => einastoRotationNormalization
- procedure :: energy => einastoEnergy
- procedure :: kSpace => einastoKSpace
- procedure :: freefallRadius => einastoFreefallRadius
- procedure :: freefallRadiusIncreaseRate => einastoFreefallRadiusIncreaseRate
- procedure :: densityScaleFree => einastoDensityScaleFree
- procedure :: enclosedMassScaleFree => einastoEnclosedMassScaleFree
- procedure :: radialVelocityDispersionScaleFree => einastoRadialVelocityDispersionScaleFree
- procedure :: radialVelocityDispersionTabulate => einastoRadialVelocityDispersionTabulate
- procedure :: freefallTimeScaleFree => einastoFreefallTimeScaleFree
- procedure :: potentialScaleFree => einastoPotentialScaleFree
- procedure :: radiusFromSpecificAngularMomentumScaleFree => einastoRadiusFromSpecificAngularMomentumScaleFree
- procedure :: radiusFromSpecificAngularMomentumTableMake => einastoRadiusFromSpecificAngularMomentumTableMake
- procedure :: freefallTabulate => einastoFreefallTabulate
- procedure :: energyTableMake => energyTableMake
- procedure :: fourierProfileTableMake => fourierProfileTableMake
+ final :: einastoDestructor
+ procedure :: get => einastoGet
end type darkMatterProfileDMOEinasto
interface darkMatterProfileDMOEinasto
@@ -158,25 +52,6 @@
module procedure einastoConstructorInternal
end interface darkMatterProfileDMOEinasto
- ! Granularity parameters for tabulations.
- integer, parameter :: angularMomentumTableRadiusPointsPerDecade =100
- integer, parameter :: angularMomentumTableAlphaPointsPerUnit =100
- integer, parameter :: freefallRadiusTableRadiusPointsPerDecade = 30
- integer, parameter :: freefallRadiusTableAlphaPointsPerUnit = 30
- integer, parameter :: energyTableConcentrationPointsPerDecade =100
- integer, parameter :: energyTableAlphaPointsPerUnit =100
- integer, parameter :: fourierProfileTableConcentrationPointsPerDecade =100
- integer, parameter :: fourierProfileTableWavenumberPointsPerDecade =100
- integer, parameter :: fourierProfileTableAlphaPointsPerUnit =100
- integer, parameter :: radialVelocityDispersionTableRadiusPointsPerDecade=100
- integer, parameter :: radialVelocityDispersionTableAlphaPointsPerUnit =100
-
- ! Module-scope variables used in root finding.
- class (darkMatterProfileDMOEinasto), pointer :: self_
- type (treeNode ), pointer :: node_
- double precision :: densityEnclosed_, alpha_
- !$omp threadprivate(self_,node_,densityEnclosed_,alpha_)
-
contains
function einastoConstructorParameters(parameters) result(self)
@@ -207,61 +82,13 @@ function einastoConstructorInternal(darkMatterHaloScale_) result(self)
use :: Array_Utilities , only : operator(.intersection.)
use :: Error , only : Component_List , Error_Report
use :: Galacticus_Nodes, only : defaultDarkMatterProfileComponent
- use :: Root_Finder , only : rangeExpandMultiplicative , rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- implicit none
- type (darkMatterProfileDMOEinasto) :: self
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-3
+ implicit none
+ type (darkMatterProfileDMOEinasto) :: self
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
!!]
-
- ! Initialize table states.
- self%angularMomentumTableRadiusMinimum = 1.0d-3
- self%angularMomentumTableRadiusMaximum =20.0d+0
- self%angularMomentumTableAlphaMinimum = 0.1d+0
- self%angularMomentumTableAlphaMaximum = 0.3d+0
- self%angularMomentumTableInitialized =.false.
- self%freefallRadiusTableRadiusMinimum = 1.0d-3
- self%freefallRadiusTableRadiusMaximum =20.0d+0
- self%freefallRadiusTableAlphaMinimum = 0.1d+0
- self%freefallRadiusTableAlphaMaximum = 0.3d+0
- self%freefallRadiusTableInitialized =.false.
- self%energyTableConcentrationMinimum = 2.0d0
- self%energyTableConcentrationMaximum =20.0d0
- self%energyTableAlphaMinimum = 0.1d0
- self%energyTableAlphaMaximum = 0.3d0
- self%energyTableInitialized =.false.
- self%fourierProfileTableConcentrationMinimum = 2.0d0
- self%fourierProfileTableConcentrationMaximum =20.0d0
- self%fourierProfileTableWavenumberMinimum = 1.0d-3
- self%fourierProfileTableWavenumberMaximum = 1.0d+3
- self%fourierProfileTableAlphaMinimum = 0.1d+0
- self%fourierProfileTableAlphaMaximum = 0.3d+0
- self%fourierProfileTableInitialized =.false.
- self%radialVelocityDispersionRadiusMinimum = 1.0d-3
- self%radialVelocityDispersionRadiusMaximum =20.0d+0
- self%radialVelocityDispersionAlphaMinimum = 0.1d+0
- self%radialVelocityDispersionAlphaMaximum = 0.3d+0
- self%radialVelocityDispersionTableInitialized=.false.
- ! Initialize root finders.
- self%finderEnclosedDensity=rootFinder( &
- & rootFunction =einastoRadiusEnclosingDensityRoot, &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
- & )
- self%finderVelocityPeak =rootFinder( &
- & rootFunction =einastoCircularVelocityPeakRadius, &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
+
! Ensure that the dark matter profile component supports both "scale" and "shape" properties. Since we've been called with
! a treeNode to process, it should have been initialized by now.
if ( &
@@ -284,8 +111,6 @@ function einastoConstructorInternal(darkMatterHaloScale_) result(self)
& {introspection:location} &
& )
end if
- ! Initialize the tabulations.
- call self%radialVelocityDispersionTabulate()
return
end function einastoConstructorInternal
@@ -302,1402 +127,62 @@ subroutine einastoDestructor(self)
return
end subroutine einastoDestructor
- double precision function einastoDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , radiusOverScaleRadius , &
- & scaleRadius , virialRadiusOverScaleRadius
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- radiusOverScaleRadius =radius /scaleRadius
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- einastoDensity =self%densityScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius,alpha) &
- & *basic%mass()/scaleRadius**3
- return
- end function einastoDensity
-
- double precision function einastoDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , radiusOverScaleRadius , &
- & scaleRadius
- !$GLC attributes unused :: self
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- alpha = darkMatterProfile%shape()
- radiusOverScaleRadius = radius/scaleRadius
- einastoDensityLogSlope = -2.0d0 &
- & *radiusOverScaleRadius**alpha
- return
- end function einastoDensityLogSlope
-
- double precision function einastoRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
+ function einastoGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentDarkMatterProfile , treeNode
- use :: Gamma_Functions , only : Gamma_Function , Gamma_Function_Incomplete_Complementary
- use :: Numerical_Constants_Math, only : Pi
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionEinasto, kinematicsDistributionCollisionless
implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: scaleRadius , virialRadiusOverScaleRadius, &
- & radiusMinimumActual, radiusMaximumActual , &
- & alpha , densityNormalization
-
- radiusMinimumActual=0.0d0
- radiusMaximumActual=self%darkMatterHaloScale_%radiusVirial(node)
- if (present(radiusMinimum)) radiusMinimumActual=radiusMinimum
- if (present(radiusMaximum)) radiusMaximumActual=radiusMaximum
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- densityNormalization= (alpha/4.0d0/Pi) &
- & * ((2.0d0/alpha) **(3.0d0/alpha) ) &
- & *exp(-2.0d0/alpha ) &
- & /Gamma_Function (3.0d0/alpha ) &
- & /Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha)
- einastoRadialMoment=+densityNormalization &
- & *basic%mass() &
- & *scaleRadius**(moment-2.0d0) &
- & *( &
- & +einastoRadialMomentScaleFree(radiusMaximumActual/scaleRadius) &
- & -einastoRadialMomentScaleFree(radiusMinimumActual/scaleRadius) &
- & )
- return
-
- contains
-
- double precision function einastoRadialMomentScaleFree(radius)
- !!{
- Provides the scale-free part of the radial moment of the Einasto density profile.
- !!}
- use :: Gamma_Functions, only : Gamma_Function_Incomplete
- implicit none
- double precision, intent(in ) :: radius
-
- einastoRadialMomentScaleFree=-exp(2.0d0/alpha) &
- & *0.5d0 **((1.0d0+moment)/alpha) &
- & *alpha **((1.0d0+moment)/alpha) &
- & *Gamma_Function ((1.0d0+moment)/alpha ) &
- & *Gamma_Function_Incomplete((1.0d0+moment)/alpha,2.0d0*radius**alpha/alpha) &
- & /alpha
- return
- end function einastoRadialMomentScaleFree
-
- end function einastoRadialMoment
-
- double precision function einastoEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , radiusOverScaleRadius , &
- & scaleRadius , virialRadiusOverScaleRadius
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- radiusOverScaleRadius =radius /scaleRadius
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- einastoEnclosedMass =self%enclosedMassScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius,alpha) &
- & *basic%mass()
- return
- end function einastoEnclosedMass
-
- double precision function einastoCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- einastoCircularVelocity=sqrt(gravitationalConstantGalacticus*self%enclosedMass(node,radius)/radius)
- else
- einastoCircularVelocity=0.0d0
- end if
- return
- end function einastoCircularVelocity
-
- double precision function einastoCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOEinasto), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- ! Find the peak velocity.
- einastoCircularVelocityMaximum=self%circularVelocity(node,self%radiusCircularVelocityMaximum(node))
- return
- end function einastoCircularVelocityMaximum
-
- double precision function einastoRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , radiusScale, &
- & radiusPeak
-
- ! Get the shape parameter for this halo.
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- alpha = darkMatterProfile%shape ( )
- radiusScale = darkMatterProfile%scale ( )
- ! Solve for the radius (in units of the scale radius) at which the rotation curve peaks.
- alpha_ = alpha
- radiusPeak = self %finderVelocityPeak%find(rootGuess=radiusScale)
- ! Convert to a physical radius.
- einastoRadiusCircularVelocityMaximum=radiusPeak*radiusScale
- return
- end function einastoRadiusCircularVelocityMaximum
-
- double precision function einastoCircularVelocityPeakRadius(radius)
- !!{
- Computes the derivative of the square of circular velocity for an Einasto density profile.
- !!}
- use :: Gamma_Functions, only : Gamma_Function, Gamma_Function_Incomplete_Complementary
- implicit none
- double precision, intent(in ) :: radius
-
- einastoCircularVelocityPeakRadius=+ 2.0d0 **( +3.0d0/alpha_) &
- & * radius **(-2.0d0+ alpha_) &
- & *( radius**alpha_/alpha_)**(-1.0d0+3.0d0/alpha_) &
- & * exp( &
- & -( &
- & +2.0d0 &
- & *radius**alpha_ &
- & ) &
- & /alpha_ &
- & ) &
- & /Gamma_Function ( &
- & 3.0d0 /alpha_ &
- & ) &
- & -Gamma_Function_Incomplete_Complementary( &
- & 3.0d0 /alpha_, &
- & 2.0d0*radius**alpha_/alpha_ &
- & ) &
- & / radius ** 2
- return
- end function einastoCircularVelocityPeakRadius
-
- double precision function einastoRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile , treeNode
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Complementary
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- integer (c_size_t ), dimension(0:1) :: jAlpha
- double precision , dimension(0:1) :: hAlpha
- integer :: iAlpha
- double precision :: alpha , scaleRadius , &
- & radiusOverScaleRadius, virialRadiusOverScaleRadius
-
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- ! Get the scale radius.
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- ! Get the shape parameter.
- alpha = darkMatterProfile%shape()
- if (radius > 0.0d0) then
- ! Ensure table is sufficiently extensive.
- call self%radialVelocityDispersionTabulate(radiusOverScaleRadius,alpha)
- ! Interpolate to get the radial velocity dispersion.
- ! Get interpolating factors in α.
- call self%radialVelocityDispersionTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
- einastoRadialVelocityDispersion=0.0d0
- do iAlpha=0,1
- einastoRadialVelocityDispersion=+einastoRadialVelocityDispersion &
- & +self%radialVelocityDispersionTableRadiusInterpolator%interpolate(radiusOverScaleRadius,self%radialVelocityDispersionTable(:,jAlpha(iAlpha))) &
- & * hAlpha(iAlpha)
- end do
- einastoRadialVelocityDispersion=+einastoRadialVelocityDispersion &
- & *self%darkMatterHaloScale_%velocityVirial(node) &
- & *sqrt( &
- & +virialRadiusOverScaleRadius &
- & /Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha) &
- & )
- else
- einastoRadialVelocityDispersion=0.0d0
- end if
- return
- end function einastoRadialVelocityDispersion
-
- double precision function einastoPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType, structureErrorCodeSuccess
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile
- double precision :: alpha , radiusOverScaleRadius , &
- & scaleRadius , virialRadiusOverScaleRadius
-
- ! Assume success.
- if (present(status)) status=structureErrorCodeSuccess
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- radiusOverScaleRadius =radius /scaleRadius
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- einastoPotential=+self%potentialScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius,alpha) &
- & *gravitationalConstantGalacticus &
- & *basic%mass() &
- & /scaleRadius
- return
- end function einastoPotential
-
- double precision function einastoRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , scaleRadius, &
- & specificAngularMomentumScaleFree
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- ! Get the scale radius of the halo.
- scaleRadius=darkMatterProfile%scale()
- ! Get the shape parameter of the halo.
- alpha =darkMatterProfile%shape()
- ! Compute the specific angular momentum in scale free units.
- specificAngularMomentumScaleFree=specificAngularMomentum &
- & /sqrt( &
- & +gravitationalConstantGalacticus &
- & *scaleRadius &
- & *self%enclosedMass(node,scaleRadius) &
- & )
- ! Compute the corresponding radius.
- einastoRadiusFromSpecificAngularMomentum=scaleRadius &
- & *self%radiusFromSpecificAngularMomentumScaleFree(alpha,specificAngularMomentumScaleFree)
- return
- end function einastoRadiusFromSpecificAngularMomentum
-
- double precision function einastoRadiusFromSpecificAngularMomentumScaleFree(self,alpha,specificAngularMomentumScaleFree)
- !!{
- Compute the radius at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentumScaleFree} in a scale free Einasto
- profile.
- !!}
- use, intrinsic :: ISO_C_Binding, only : c_size_t
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alpha , specificAngularMomentumScaleFree
- integer (c_size_t ), dimension(0:1) :: jAlpha
- double precision , dimension(0:1) :: hAlpha
- integer :: iAlpha
-
- ! Return immediately for zero angular momentum.
- if (specificAngularMomentumScaleFree <= 0.0d0) then
- einastoRadiusFromSpecificAngularMomentumScaleFree=0.0d0
- return
- end if
-
- ! Ensure the table exists and is sufficiently tabulated.
- call self%radiusFromSpecificAngularMomentumTableMake(alpha,specificAngularMomentumScaleFree)
-
- ! Get interpolating factors in α.
- call self%angularMomentumTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
-
- ! Interpolate in specific angular momentum to get radius.
- einastoRadiusFromSpecificAngularMomentumScaleFree=0.0d0
- do iAlpha=0,1
- einastoRadiusFromSpecificAngularMomentumScaleFree= &
- & einastoRadiusFromSpecificAngularMomentumScaleFree &
- & +self%angularMomentumTableRadiusInterpolator(jAlpha(iAlpha))%interpolate(specificAngularMomentumScaleFree) &
- & * hAlpha(iAlpha)
- end do
- return
- end function einastoRadiusFromSpecificAngularMomentumScaleFree
-
- subroutine einastoRadiusFromSpecificAngularMomentumTableMake(self,alphaRequired,specificAngularMomentumRequired)
- !!{
- Create a tabulation of the relation between specific angular momentum and radius in an Einasto profile.
- !!}
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Complementary
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear, rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alphaRequired, specificAngularMomentumRequired
- integer :: iAlpha , iRadius
- logical :: makeTable
- double precision :: alpha , enclosedMass , &
- & radius
-
- ! Always check if we need to make the table.
- makeTable=.true.
- do while (makeTable)
- ! Assume table does not need remaking.
- makeTable=.false.
- ! Check for uninitialized table.
- if (.not.self%angularMomentumTableInitialized) then
- makeTable=.true.
- ! Check for α out of range.
- else if (alphaRequired < self%angularMomentumTableAlpha(1) .or. alphaRequired >&
- & self%angularMomentumTableAlpha(self%angularMomentumTableAlphaCount)) then
- makeTable=.true.
- ! Compute the range of tabulation and number of points to use.
- self%angularMomentumTableAlphaMinimum=min(self%angularMomentumTableAlphaMinimum,0.9d0*alphaRequired)
- self%angularMomentumTableAlphaMaximum=max(self%angularMomentumTableAlphaMaximum,1.1d0*alphaRequired)
- ! Check for angular momentum below minimum tabulated value.
- else if (any(specificAngularMomentumRequired < self%angularMomentumTable(1,:))) then
- makeTable=.true.
- self%angularMomentumTableRadiusMinimum=0.5d0*self%angularMomentumTableRadiusMinimum
- ! Check for angular momentum above maximum tabulated value.
- else if (any(specificAngularMomentumRequired > self%angularMomentumTable(self%angularMomentumTableRadiusCount,:))) then
- makeTable=.true.
- self%angularMomentumTableRadiusMaximum=2.0d0*self%angularMomentumTableRadiusMaximum
- end if
- ! Remake the table if necessary.
- if (makeTable) then
- ! Allocate arrays to the appropriate sizes.
- self%angularMomentumTableAlphaCount =int( (self%angularMomentumTableAlphaMaximum -self%angularMomentumTableAlphaMinimum ) &
- & *dble(angularMomentumTableAlphaPointsPerUnit ) &
- & )+1
- self%angularMomentumTableRadiusCount=int(log10(self%angularMomentumTableRadiusMaximum/self%angularMomentumTableRadiusMinimum) &
- & *dble(angularMomentumTableRadiusPointsPerDecade) &
- & )+1
- if (allocated(self%angularMomentumTableAlpha )) deallocate(self%angularMomentumTableAlpha )
- if (allocated(self%angularMomentumTableRadius)) deallocate(self%angularMomentumTableRadius)
- if (allocated(self%angularMomentumTable )) deallocate(self%angularMomentumTable )
- allocate(self%angularMomentumTableAlpha (self%angularMomentumTableAlphaCount))
- allocate(self%angularMomentumTableRadius(self%angularMomentumTableRadiusCount ))
- allocate(self%angularMomentumTable (self%angularMomentumTableRadiusCount,self%angularMomentumTableAlphaCount))
- ! Create ranges of α and radius.
- self%angularMomentumTableAlpha =Make_Range(self%angularMomentumTableAlphaMinimum ,self%angularMomentumTableAlphaMaximum, &
- & self%angularMomentumTableAlphaCount ,rangeType=rangeTypeLinear )
- self%angularMomentumTableRadius=Make_Range(self%angularMomentumTableRadiusMinimum,self%angularMomentumTableRadiusMaximum, &
- & self%angularMomentumTableRadiusCount ,rangeType=rangeTypeLogarithmic)
- ! Tabulate the radius vs. specific angular momentum relation.
- do iAlpha=1,self%angularMomentumTableAlphaCount
- alpha=self%angularMomentumTableAlpha(iAlpha)
- do iRadius=1,self%angularMomentumTableRadiusCount
- radius=self%angularMomentumTableRadius(iRadius)
- enclosedMass= Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*radius**alpha/alpha) &
- & /Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0 /alpha)
- self%angularMomentumTable(iRadius,iAlpha)=sqrt(enclosedMass*radius)
- end do
- end do
- ! Build interpolators.
- if (allocated(self%angularMomentumTableRadiusInterpolator)) deallocate(self%angularMomentumTableRadiusInterpolator)
- if (allocated(self%angularMomentumTableAlphaInterpolator )) deallocate(self%angularMomentumTableAlphaInterpolator )
- allocate(self%angularMomentumTableRadiusInterpolator(self%angularMomentumTableAlphaCount))
- allocate(self%angularMomentumTableAlphaInterpolator )
- do iAlpha=1,self%angularMomentumTableAlphaCount
- self%angularMomentumTableRadiusInterpolator(iAlpha)=interpolator(self%angularMomentumTable (:,iAlpha),self%angularMomentumTableRadius)
- end do
- self%angularMomentumTableAlphaInterpolator =interpolator(self%angularMomentumTableAlpha )
- ! Flag that the table is now initialized.
- self%angularMomentumTableInitialized=.true.
- end if
- end do
- return
- end subroutine einastoRadiusFromSpecificAngularMomentumTableMake
-
- double precision function einastoRotationNormalization(self,node)
- !!{
- Return the rotation normalization of an Einasto halo density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- use :: Gamma_Functions , only : Gamma_Function , Gamma_Function_Incomplete_Complementary
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: alpha , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- ! Get scale radius, shape and concentration.
- scaleRadius =+darkMatterProfile%scale()
- alpha =+darkMatterProfile%shape()
- virialRadiusOverScaleRadius =+self %darkMatterHaloScale_%radiusVirial(node) &
- & / scaleRadius
- einastoRotationNormalization=+(2.0d0/alpha)**(1.0d0/alpha) &
- & *Gamma_Function (3.0d0/alpha ) &
- & /Gamma_Function (4.0d0/alpha ) &
- & *Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha) &
- & /Gamma_Function_Incomplete_Complementary(4.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha) &
- & /scaleRadius
- return
- end function einastoRotationNormalization
-
- double precision function einastoEnergy(self,node)
- !!{
- Return the energy of an Einasto halo density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- integer (c_size_t ), dimension(0:1) :: jAlpha
- double precision , dimension(0:1) :: hAlpha
- integer :: iAlpha
- double precision :: alpha , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get scale radius, shape parameter and concentration.
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
-
- ! Ensure the table exists and is sufficiently tabulated.
- call self%energyTableMake(virialRadiusOverScaleRadius,alpha)
-
- ! Get interpolating factors in α.
- call self%energyTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
-
- ! Find the energy by interpolation.
- einastoEnergy=0.0d0
- do iAlpha=0,1
- einastoEnergy=+einastoEnergy &
- & +self%energyTableConcentrationInterpolator%interpolate(virialRadiusOverScaleRadius,self%energyTable(:,jAlpha(iAlpha))) &
- & * hAlpha(iAlpha)
- end do
-
- ! Scale to dimensionful units.
- einastoEnergy=einastoEnergy*basic%mass() &
- & *self%darkMatterHaloScale_%velocityVirial(node)**2
- return
- end function einastoEnergy
-
- subroutine energyTableMake(self,concentrationRequired,alphaRequired)
- !!{
- Create a tabulation of the energy of Einasto profiles as a function of their concentration of $\alpha$ parameter.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Integration , only : integrator
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear, rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alphaRequired , concentrationRequired
- integer :: iAlpha , iConcentration
- logical :: makeTable
- double precision :: alpha , concentration , &
- & jeansEquationIntegral , kineticEnergy , &
- & kineticEnergyIntegral , potentialEnergy , &
- & potentialEnergyIntegral, radiusMaximum , &
- & radiusMinimum , concentrationParameter, &
- & alphaParameter
- type (integrator ) :: integratorPotential , integratorKinetic , &
- & integratorJeans
-
- ! Assume table does not need remaking.
- makeTable=.false.
- ! Check for uninitialized table.
- if (.not.self%energyTableInitialized) makeTable=.true.
- ! Check for α out of range.
- if (alphaRequired < self%energyTableAlphaMinimum .or. alphaRequired > self%energyTableAlphaMaximum) then
- makeTable=.true.
- ! Compute the range of tabulation and number of points to use.
- self%energyTableAlphaMinimum =min(self%energyTableAlphaMinimum,0.9d0*alphaRequired)
- self%energyTableAlphaMaximum =max(self%energyTableAlphaMaximum,1.1d0*alphaRequired)
- end if
- ! Check for concentration below minimum tabulated value.
- if (concentrationRequired < self%energyTableConcentrationMinimum .or. concentrationRequired > self%energyTableConcentrationMaximum) then
- makeTable=.true.
- self%energyTableConcentrationMinimum=min(self%energyTableConcentrationMinimum,0.5d0*self%energyTableConcentrationMinimum)
- self%energyTableConcentrationMaximum=max(self%energyTableConcentrationMaximum,2.0d0*self%energyTableConcentrationMaximum)
- end if
- ! Remake the table if necessary.
- if (makeTable) then
- ! Allocate arrays to the appropriate sizes.
- self%energyTableAlphaCount =int( (self%energyTableAlphaMaximum -self%energyTableAlphaMinimum ) &
- & *dble(energyTableAlphaPointsPerUnit ) &
- & )+1
- self%energyTableConcentrationCount=int(log10(self%energyTableConcentrationMaximum/self%energyTableConcentrationMinimum) &
- & *dble(energyTableConcentrationPointsPerDecade ) &
- & )+1
- if (allocated(self%energyTableAlpha )) deallocate(self%energyTableAlpha )
- if (allocated(self%energyTableConcentration)) deallocate(self%energyTableConcentration)
- if (allocated(self%energyTable )) deallocate(self%energyTable )
- allocate(self%energyTableAlpha (self%energyTableAlphaCount))
- allocate(self%energyTableConcentration(self%energyTableConcentrationCount ))
- allocate(self%energyTable (self%energyTableConcentrationCount,self%energyTableAlphaCount))
- ! Create ranges of α and concentration.
- self%energyTableAlpha =Make_Range(self%energyTableAlphaMinimum ,self%energyTableAlphaMaximum , &
- & self%energyTableAlphaCount ,rangeType=rangeTypeLinear )
- self%energyTableConcentration=Make_Range(self%energyTableConcentrationMinimum,self%energyTableConcentrationMaximum, &
- & self%energyTableConcentrationCount ,rangeType=rangeTypeLogarithmic)
- ! Tabulate the radius vs. specific angular momentum relation.
- integratorPotential=integrator(einastoPotentialEnergyIntegrand,toleranceRelative=1.0d-3)
- integratorKinetic =integrator(einastoKineticEnergyIntegrand ,toleranceRelative=1.0d-3)
- integratorJeans =integrator(einastoJeansEquationIntegrand ,toleranceRelative=1.0d-3)
- do iAlpha=1,self%energyTableAlphaCount
- alpha=self%energyTableAlpha(iAlpha)
- do iConcentration=1,self%energyTableConcentrationCount
- concentration=self%energyTableConcentration(iConcentration)
-
- ! Compute the potential energy.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- concentrationParameter=concentration
- alphaParameter =alpha
- potentialEnergyIntegral=integratorPotential%integrate(radiusMinimum,radiusMaximum)
- potentialEnergy=-0.5d0*(1.0d0/concentration+potentialEnergyIntegral)
-
- ! Compute the velocity dispersion at the virial radius.
- radiusMinimum = concentration
- radiusMaximum =100.0d0*concentration
- concentrationParameter= concentration
- alphaParameter =alpha
- jeansEquationIntegral=integratorJeans%integrate(radiusMinimum,radiusMaximum)
-
- ! Compute the kinetic energy.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- concentrationParameter=concentration
- alphaParameter =alpha
- kineticEnergyIntegral=integratorKinetic%integrate(radiusMinimum,radiusMaximum)
- kineticEnergy=2.0d0*Pi*(jeansEquationIntegral*concentration**3+kineticEnergyIntegral)
-
- ! Compute the total energy.
- self%energyTable(iConcentration,iAlpha)=(potentialEnergy+kineticEnergy)*concentration
-
- end do
- end do
- ! Build interpolators.
- if (allocated(self%energyTableConcentrationInterpolator)) deallocate(self%energyTableConcentrationInterpolator)
- if (allocated(self%energyTableAlphaInterpolator )) deallocate(self%energyTableAlphaInterpolator )
- allocate(self%energyTableConcentrationInterpolator)
- allocate(self%energyTableAlphaInterpolator )
- self%energyTableConcentrationInterpolator=interpolator(self%energyTableConcentration)
- self%energyTableAlphaInterpolator =interpolator(self%energyTableAlpha )
- ! Flag that the table is now initialized.
- self%energyTableInitialized=.true.
- end if
- return
-
- contains
-
- double precision function einastoPotentialEnergyIntegrand(radius)
- !!{
- Integrand for Einasto profile potential energy.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- einastoPotentialEnergyIntegrand=(self%enclosedMassScaleFree(radius,concentrationParameter,alphaParameter)/radius)**2
- return
- end function einastoPotentialEnergyIntegrand
-
- double precision function einastoKineticEnergyIntegrand(radius)
- !!{
- Integrand for Einasto profile kinetic energy.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- einastoKineticEnergyIntegrand=self%enclosedMassScaleFree(radius,concentrationParameter,alphaParameter) &
- & *self%densityScaleFree (radius,concentrationParameter,alphaParameter) &
- & *radius
- return
- end function einastoKineticEnergyIntegrand
-
- double precision function einastoJeansEquationIntegrand(radius)
- !!{
- Integrand for Einasto profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- einastoJeansEquationIntegrand=self%enclosedMassScaleFree(radius,concentrationParameter,alphaParameter) &
- & *self%densityScaleFree (radius,concentrationParameter,alphaParameter) &
- & /radius**2
- return
- end function einastoJeansEquationIntegrand
-
- end subroutine energyTableMake
-
- double precision function einastoEnclosedMassScaleFree(self,radius,concentration,alpha)
- !!{
- Returns the enclosed mass (in units of the virial mass) in an Einasto dark matter profile with given {\normalfont \ttfamily concentration} at the
- given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- use :: Gamma_Functions, only : Gamma_Function_Incomplete_Complementary
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alpha, concentration, radius
- !$GLC attributes unused :: self
-
- einastoEnclosedMassScaleFree=+Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*radius **alpha/alpha) &
- & /Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*concentration**alpha/alpha)
- return
- end function einastoEnclosedMassScaleFree
-
- double precision function einastoDensityScaleFree(self,radius,concentration,alpha)
- !!{
- Returns the density (in units such that the virial mass and scale length are unity) in an Einasto dark matter profile with
- given {\normalfont \ttfamily concentration} and {\normalfont \ttfamily alpha} at the given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- use :: Gamma_Functions , only : Gamma_Function, Gamma_Function_Incomplete_Complementary
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alpha , concentration, radius
- double precision :: densityNormalization
- !$GLC attributes unused :: self
-
- densityNormalization= (alpha/4.0d0/Pi) &
- & * ((2.0d0/alpha) **(3.0d0/alpha) ) &
- & *exp(-2.0d0/alpha ) &
- & /Gamma_Function (3.0d0/alpha ) &
- & /Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*concentration**alpha/alpha)
- einastoDensityScaleFree=densityNormalization*exp(-(2.0d0/alpha)*(radius**alpha-1.0d0))
- return
- end function einastoDensityScaleFree
-
- double precision function einastoPotentialScaleFree(self,radius,concentration,alpha)
- !!{
- Returns the gravitational potential (in units where the virial mass and scale radius are unity) in an Einasto dark matter
- profile with given {\normalfont \ttfamily concentration} and {\normalfont \ttfamily alpha} at the given {\normalfont
- \ttfamily radius} (given in units of the scale radius). Uses the results from \cite{retana-montenegro_analytical_2012},
- their equations (19) and (20).
- !!}
- use :: Gamma_Functions, only : Gamma_Function, Gamma_Function_Incomplete, Gamma_Function_Incomplete_Complementary
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alpha , concentration, &
- & radius
- !$GLC attributes unused :: self
-
- if (radius <= 0.0d0) then
- einastoPotentialScaleFree=-((2.0d0/alpha)**(1.0d0/alpha)) &
- & * Gamma_Function (2.0d0/alpha ) &
- & / Gamma_Function (3.0d0/alpha ) &
- & / Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*concentration**alpha/alpha)
-
- else
- einastoPotentialScaleFree=-1.0d0 &
- & /radius &
- & *( &
- & +Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*radius **alpha/alpha) &
- & +( &
- & +radius &
- & *(2.0d0/alpha)**(1.0d0/alpha) &
- & ) &
- & *Gamma_Function_Incomplete (2.0d0/alpha,2.0d0*radius **alpha/alpha) &
- & *Gamma_Function (2.0d0/alpha ) &
- & /Gamma_Function (3.0d0/alpha ) &
- & ) &
- & / Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*concentration**alpha/alpha)
- end if
- return
- end function einastoPotentialScaleFree
-
- double precision function einastoKSpace(self,node,wavenumber)
- !!{
- Returns the Fourier transform of the Einasto density profile at the specified {\normalfont \ttfamily waveNumber} (given in Mpc$^{-1}$)).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- implicit none
- class (darkMatterProfileDMOEinasto ) , intent(inout) :: self
- type (treeNode ) , intent(inout), target :: node
- double precision , intent(in ) :: wavenumber
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- integer (c_size_t ), dimension(0:1) :: jAlpha , jConcentration
- double precision , dimension(0:1) :: hAlpha , hConcentration
- integer :: iAlpha , iConcentration
- double precision :: alpha , scaleRadius , &
- & virialRadiusOverScaleRadius, wavenumberScaleFree
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get scale radius, shape parameter and concentration.
- scaleRadius =darkMatterProfile%scale()
- alpha =darkMatterProfile%shape()
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- wavenumberScaleFree =wavenumber*scaleRadius
-
- ! Ensure the table exists and is sufficiently tabulated.
- call self%fourierProfileTableMake(wavenumberScaleFree,virialRadiusOverScaleRadius,alpha)
-
- ! Get interpolating factors in α.
- call self%fourierProfileTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
-
- ! Get interpolating factors in concentration.
- call self%fourierProfileTableConcentrationInterpolator%linearFactors(virialRadiusOverScaleRadius,jConcentration(0),hConcentration)
- jConcentration(1)=jConcentration(0)+1
-
- ! Find the Fourier profile by interpolation.
- einastoKSpace=0.0d0
- do iAlpha=0,1
- do iConcentration=0,1
- einastoKSpace=+einastoKSpace &
- & +self%fourierProfileTableWavenumberInterpolator%interpolate(wavenumberScaleFree,self%fourierProfileTable(:,jConcentration(iConcentration),jAlpha(iAlpha))) &
- & * hAlpha(iAlpha) &
- & * hConcentration(iConcentration)
- end do
- end do
- return
- end function einastoKSpace
-
- subroutine fourierProfileTableMake(self,wavenumberRequired,concentrationRequired,alphaRequired)
- !!{
- Create a tabulation of the Fourier transform of Einasto profiles as a function of their $\alpha$ parameter and
- dimensionless wavenumber.
- !!}
- use :: Display , only : displayCounter , displayCounterClear , displayIndent , displayUnindent, &
- & verbosityLevelInfo, verbosityLevelWorking
- use :: Error , only : Error_Report , errorStatusSuccess
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Integration, only : integrator
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear , rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alphaRequired , concentrationRequired, wavenumberRequired
- double precision , parameter :: profileTruncateLevel =6.0d-6
- integer :: iAlpha , iConcentration , iWavenumber , &
- & percentage , errorStatus
- logical :: makeTable
- double precision :: alpha , concentration , radiusMaximum , &
- & radiusMinimum , wavenumber , wavenumberParameter, &
- & concentrationParameter , alphaParameter
- type (integrator ) :: integrator_
- character (len=12 ) :: label
- type (varying_string ) :: message
-
- ! Assume table does not need remaking.
- makeTable=.false.
- ! Check for uninitialized table.
- if (.not.self%fourierProfileTableInitialized) makeTable=.true.
- ! Check for α out of range.
- if (alphaRequired < self%fourierProfileTableAlphaMinimum .or. alphaRequired > self%fourierProfileTableAlphaMaximum ) then
- makeTable=.true.
- ! Compute the range of tabulation and number of points to use.
- self%fourierProfileTableAlphaMinimum =min(self%fourierProfileTableAlphaMinimum ,0.9d0*alphaRequired )
- self%fourierProfileTableAlphaMaximum =max(self%fourierProfileTableAlphaMaximum ,1.1d0*alphaRequired )
- end if
- ! Check for concentration out of range.
- if (concentrationRequired < self%fourierProfileTableConcentrationMinimum .or. concentrationRequired > self%fourierProfileTableConcentrationMaximum ) then
- makeTable=.true.
- ! Compute the range of tabulation and number of points to use.
- self%fourierProfileTableConcentrationMinimum=min(self%fourierProfileTableConcentrationMinimum,0.5d0*concentrationRequired)
- self%fourierProfileTableConcentrationMaximum=max(self%fourierProfileTableConcentrationMaximum,2.0d0*concentrationRequired)
- end if
- ! Check for wavenumber below minimum tabulated value.
- if (wavenumberRequired < self%fourierProfileTableWavenumberMinimum .or. wavenumberRequired > self%fourierProfileTableWavenumberMaximum ) then
- makeTable=.true.
- self%fourierProfileTableWavenumberMinimum =min(self%fourierProfileTableWavenumberMinimum ,0.5d0*wavenumberRequired )
- self%fourierProfileTableWavenumberMaximum =max(self%fourierProfileTableWavenumberMaximum ,2.0d0*wavenumberRequired )
- end if
- ! Remake the table if necessary.
- if (makeTable) then
- ! Display a message.
- call displayIndent('Constructing Einasto profile Fourier transform lookup table...',verbosityLevelInfo)
- ! Allocate arrays to the appropriate sizes.
- self%fourierProfileTableAlphaCount =int( (self%fourierProfileTableAlphaMaximum -self%fourierProfileTableAlphaMinimum ) &
- &*dble(fourierProfileTableAlphaPointsPerUnit ))+1
- self%fourierProfileTableConcentrationCount=int(log10(self%fourierProfileTableConcentrationMaximum/self%fourierProfileTableConcentrationMinimum) &
- &*dble(fourierProfileTableConcentrationPointsPerDecade))+1
- self%fourierProfileTableWavenumberCount =int(log10(self%fourierProfileTableWavenumberMaximum /self%fourierProfileTableWavenumberMinimum ) &
- &*dble(fourierProfileTableWavenumberPointsPerDecade ))+1
- if (allocated(self%fourierProfileTableAlpha )) deallocate(self%fourierProfileTableAlpha )
- if (allocated(self%fourierProfileTableConcentration)) deallocate(self%fourierProfileTableConcentration)
- if (allocated(self%fourierProfileTableWavenumber )) deallocate(self%fourierProfileTableWavenumber )
- if (allocated(self%fourierProfileTable )) deallocate(self%fourierProfileTable )
- allocate(self%fourierProfileTableAlpha (self%fourierProfileTableAlphaCount))
- allocate(self%fourierProfileTableConcentration(self%fourierProfileTableConcentrationCount ))
- allocate(self%fourierProfileTableWavenumber (self%fourierProfileTableWavenumberCount ))
- allocate(self%fourierProfileTable (self%fourierProfileTableWavenumberCount,self%fourierProfileTableConcentrationCount,self%fourierProfileTableAlphaCount))
- ! Create ranges of α and wavenumber.
- self%fourierProfileTableAlpha =Make_Range(self%fourierProfileTableAlphaMinimum ,self%fourierProfileTableAlphaMaximum , &
- & self%fourierProfileTableAlphaCount ,rangeType=rangeTypeLinear )
- self%fourierProfileTableConcentration=Make_Range(self%fourierProfileTableConcentrationMinimum,self%fourierProfileTableConcentrationMaximum, &
- & self%fourierProfileTableConcentrationCount ,rangeType=rangeTypeLogarithmic)
- self%fourierProfileTableWavenumber =Make_Range(self%fourierProfileTableWavenumberMinimum ,self%fourierProfileTableWavenumberMaximum , &
- & self%fourierProfileTableWavenumberCount ,rangeType=rangeTypeLogarithmic)
- ! Tabulate the Fourier profile.
- integrator_=integrator(einastoFourierProfileIntegrand,toleranceRelative=1.0d-3,intervalsMaximum=1000_c_size_t)
- do iAlpha=1,self%fourierProfileTableAlphaCount
- alpha=self%fourierProfileTableAlpha(iAlpha)
- do iConcentration=1,self%fourierProfileTableConcentrationCount
- concentration=self%fourierProfileTableConcentration(iConcentration)
-
- ! Show progress.
- percentage=int(100.0d0*dble((iAlpha-1)*self%fourierProfileTableConcentrationCount+iConcentration-1 ) &
- & /dble(self%fourierProfileTableAlphaCount*self%fourierProfileTableConcentrationCount) &
- & )
- call displayCounter(percentage,iAlpha == 1 .and. iConcentration == 1,verbosityLevelWorking)
-
- do iWavenumber=1,self%fourierProfileTableWavenumberCount
- ! If the Fourier profile has fallen below some minimal level, simply truncate to zero to avoid numerical
- ! integration problems.
- if (iWavenumber > 1 .and. self%fourierProfileTable(iWavenumber-1,iConcentration,iAlpha) <= profileTruncateLevel) then
- self%fourierProfileTable(iWavenumber,iConcentration,iAlpha)=0.0d0
- else
- wavenumber=self%fourierProfileTableWavenumber(iWavenumber)
- ! Compute the Fourier profile.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- wavenumberParameter =wavenumber
- alphaParameter =alpha
- concentrationParameter=concentration
- self%fourierProfileTable(iWavenumber,iConcentration,iAlpha)=integrator_%integrate(radiusMinimum,radiusMaximum,status=errorStatus)
- if (errorStatus /= errorStatusSuccess) then
- message="Integration of Einasto profile Fourier transform failed at:"//char(10)
- write (label,'(e12.6)') wavenumber
- message=message//" wavenumber: k="//trim(adjustl(label))//"Mpc"//char(10)
- if (iWavenumber == 1) then
- message=message//" no previous tabulated point"
- else
- write (label,'(e12.6)') self%fourierProfileTable(iWavenumber-1,iConcentration,iAlpha)
- message=message//" value at previous tabulated point was "//trim(adjustl(label))
- end if
- call Error_Report(message//{introspection:location})
- end if
- end if
- end do
- end do
- end do
- call displayCounterClear(verbosityLevelWorking)
- ! Build interpolators.
- if (allocated(self%fourierProfileTableWavenumberInterpolator )) deallocate(self%fourierProfileTableWavenumberInterpolator )
- if (allocated(self%fourierProfileTableAlphaInterpolator )) deallocate(self%fourierProfileTableAlphaInterpolator )
- if (allocated(self%fourierProfileTableConcentrationInterpolator)) deallocate(self%fourierProfileTableConcentrationInterpolator)
- allocate(self%fourierProfileTableWavenumberInterpolator )
- allocate(self%fourierProfileTableAlphaInterpolator )
- allocate(self%fourierProfileTableConcentrationInterpolator)
- self%fourierProfileTableWavenumberInterpolator =interpolator(self%fourierProfileTableWavenumber)
- self%fourierProfileTableAlphaInterpolator =interpolator(self%fourierProfileTableAlpha)
- self%fourierProfileTableConcentrationInterpolator=interpolator(self%fourierProfileTableConcentration)
- ! Flag that the table is now initialized.
- self%fourierProfileTableInitialized=.true.
- ! Display a message.
- call displayUnindent('done',verbosityLevelInfo)
- end if
- return
-
- contains
-
- double precision function einastoFourierProfileIntegrand(radius)
- !!{
- Integrand for Einasto Fourier profile.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- einastoFourierProfileIntegrand=4.0d0*Pi*radius*sin(wavenumberParameter*radius)*self%densityScaleFree(radius&
- &,concentrationParameter,alphaParameter)/wavenumberParameter
- return
- end function einastoFourierProfileIntegrand
-
- end subroutine fourierProfileTableMake
-
- double precision function einastoFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the Einasto density profile at the specified {\normalfont \ttfamily time} (given in Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile , treeNode
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Complementary
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr , gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) , target :: self
- type (treeNode ), intent(inout) , target :: node
- double precision , intent(in ) :: time
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- integer (c_size_t ), dimension(0:1) :: jAlpha
- double precision , dimension(0:1) :: hAlpha
- integer :: iAlpha
- double precision :: alpha , freefallTimeScaleFree , &
- & radiusScale , timeScale , &
- & velocityScale , virialRadiusOverScaleRadius
-
- ! For non-positive freefall times, return a zero freefall radius immediately.
- if (time <= 0.0d0) then
- einastoFreefallRadius=0.0d0
- return
- end if
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the shape parameter.
- alpha =darkMatterProfile%shape()
-
- ! Get the scale radius.
- radiusScale =darkMatterProfile%scale()
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the velocity scale.
- velocityScale=sqrt(gravitationalConstantGalacticus*basic%mass()/radiusScale)
-
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & *sqrt(Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha)) &
- & /sqrt(Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0 /alpha))
-
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
-
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree,alpha)
-
- ! Interpolate to get the freefall radius.
- ! Get interpolating factors in α.
- call self%freefallRadiusTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
-
- einastoFreefallRadius=0.0d0
- do iAlpha=0,1
- einastoFreefallRadius=+einastoFreefallRadius &
- & +self%freefallRadiusTableRadiusInterpolator(jAlpha(iAlpha))%interpolate(freefallTimeScaleFree,self%freefallRadiusTableRadius) &
- & * hAlpha(iAlpha)
- end do
- einastoFreefallRadius=einastoFreefallRadius*radiusScale
- return
- end function einastoFreefallRadius
-
- double precision function einastoFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the Einasto density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile , treeNode
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Complementary
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr , gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOEinasto ), intent(inout) , target :: self
- type (treeNode ), intent(inout) , target :: node
- double precision , intent(in ) :: time
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- integer (c_size_t ), dimension(0:1) :: jAlpha
- double precision , dimension(0:1) :: hAlpha
- integer :: iAlpha
- double precision :: alpha , freefallTimeScaleFree , &
- & radiusScale , timeScale , &
- & velocityScale , virialRadiusOverScaleRadius
-
- ! For non-positive freefall times, return a zero freefall radius immediately.
- if (time <= 0.0d0) then
- einastoFreefallRadiusIncreaseRate=0.0d0
- return
- end if
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the shape parameter.
- alpha =darkMatterProfile%shape()
-
- ! Get the scale radius.
- radiusScale =darkMatterProfile%scale()
- virialRadiusOverScaleRadius=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the velocity scale.
- velocityScale=sqrt(gravitationalConstantGalacticus*basic%mass()/radiusScale)
-
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & *sqrt(Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*virialRadiusOverScaleRadius**alpha/alpha)) &
- & /sqrt(Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0 /alpha))
-
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
-
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree,alpha)
-
- ! Interpolate to get the freefall radius.
- ! Get interpolating factors in α.
- call self%freefallRadiusTableAlphaInterpolator%linearFactors(alpha,jAlpha(0),hAlpha)
- jAlpha(1)=jAlpha(0)+1
-
- einastoFreefallRadiusIncreaseRate=0.0d0
- do iAlpha=0,1
- einastoFreefallRadiusIncreaseRate=+einastoFreefallRadiusIncreaseRate &
- & +self%freefallRadiusTableRadiusInterpolator(jAlpha(iAlpha))%derivative(freefallTimeScaleFree,self%freefallRadiusTableRadius) &
- & * hAlpha(iAlpha)
- end do
- einastoFreefallRadiusIncreaseRate=einastoFreefallRadiusIncreaseRate*radiusScale/timeScale
- return
- end function einastoFreefallRadiusIncreaseRate
-
- subroutine einastoFreefallTabulate(self,freefallTimeScaleFree,alphaRequired)
- !!{
- Tabulates the freefall time vs. freefall radius for Einasto halos.
- !!}
- use :: Display , only : displayCounter, displayIndent , displayUnindent , verbosityLevelWorking
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear, rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alphaRequired, freefallTimeScaleFree
- logical :: retabulate
- integer :: iAlpha , iRadius , percentage
- double precision :: alpha
-
- retabulate=.not.self%freefallRadiusTableInitialized
- ! If the table has not yet been made, compute and store the freefall times corresponding to the minimum and maximum
- ! radii that will be tabulated by default.
- if (retabulate) then
- self%freefallTimeMinimum=self%freefallTimeScaleFree(self%freefallRadiusTableRadiusMinimum,alphaRequired)
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusTableRadiusMaximum,alphaRequired)
- end if
- do while (freefallTimeScaleFree < self%freefallTimeMinimum)
- self%freefallRadiusTableRadiusMinimum=0.5d0*self%freefallRadiusTableRadiusMinimum
- self%freefallTimeMinimum=self%freefallTimeScaleFree(self%freefallRadiusTableRadiusMinimum,alphaRequired)
- retabulate=.true.
- end do
- do while (freefallTimeScaleFree > self%freefallTimeMaximum)
- self%freefallRadiusTableRadiusMaximum=2.0d0*self%freefallRadiusTableRadiusMaximum
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusTableRadiusMaximum,alphaRequired)
- retabulate=.true.
- end do
- ! Check for α out of range.
- if (alphaRequired < self%freefallRadiusTableAlphaMinimum .or. alphaRequired > self%freefallRadiusTableAlphaMaximum) then
- retabulate=.true.
- ! Compute the range of tabulation.
- self%freefallRadiusTableAlphaMinimum=min(self%freefallRadiusTableAlphaMinimum,0.9d0*alphaRequired)
- self%freefallRadiusTableAlphaMaximum=max(self%freefallRadiusTableAlphaMaximum,1.1d0*alphaRequired)
- end if
-
- if (retabulate) then
- ! Display a message.
- call displayIndent('Constructing Einasto profile freefall radius lookup table...',verbosityLevelWorking)
- ! Decide how many points to tabulate and allocate table arrays.
- self%freefallRadiusTableRadiusCount=int(log10(self%freefallRadiusTableRadiusMaximum/self%freefallRadiusTableRadiusMinimum)*dble(freefallRadiusTableRadiusPointsPerDecade))+1
- self%freefallRadiusTableAlphaCount =int( (self%freefallRadiusTableAlphaMaximum -self%freefallRadiusTableAlphaMinimum )*dble(freefallRadiusTableAlphaPointsPerUnit ))+1
- if (allocated(self%freefallRadiusTableRadius)) then
- deallocate(self%freefallRadiusTableAlpha )
- deallocate(self%freefallRadiusTableRadius)
- deallocate(self%freefallRadiusTable )
- end if
- allocate(self%freefallRadiusTableAlpha (self%freefallRadiusTableAlphaCount))
- allocate(self%freefallRadiusTableRadius(self%freefallRadiusTableRadiusCount ))
- allocate(self%freefallRadiusTable (self%freefallRadiusTableRadiusCount,self%freefallRadiusTableAlphaCount))
- ! Create a range of radii and α.
- self%freefallRadiusTableAlpha =Make_Range(self%freefallRadiusTableAlphaMinimum ,self%freefallRadiusTableAlphaMaximum ,self%freefallRadiusTableAlphaCount ,rangeType=rangeTypeLinear )
- self%freefallRadiusTableRadius=Make_Range(self%freefallRadiusTableRadiusMinimum,self%freefallRadiusTableRadiusMaximum,self%freefallRadiusTableRadiusCount,rangeType=rangeTypeLogarithmic)
- ! Loop over radii and α and populate tables.
- do iAlpha=1,self%freefallRadiusTableAlphaCount
- alpha=self%freefallRadiusTableAlpha(iAlpha)
- do iRadius=1,self%freefallRadiusTableRadiusCount
- ! Show progress.
- percentage=int(100.0d0*dble((iAlpha-1)*self%freefallRadiusTableRadiusCount+iRadius-1 ) &
- & /dble(self%freefallRadiusTableAlphaCount*self%freefallRadiusTableRadiusCount) &
- & )
- call displayCounter(percentage,iAlpha == 1 .and. iRadius == 1,verbosityLevelWorking)
- ! Compute the freefall radius.
- self%freefallRadiusTable(iRadius,iAlpha)=self%freefallTimeScaleFree(self%freefallRadiusTableRadius(iRadius),alpha)
- end do
- end do
- ! Build interpolators.
- if (allocated(self%freefallRadiusTableAlphaInterpolator )) deallocate(self%freefallRadiusTableAlphaInterpolator )
- if (allocated(self%freefallRadiusTableRadiusInterpolator)) deallocate(self%freefallRadiusTableRadiusInterpolator)
- allocate(self%freefallRadiusTableAlphaInterpolator )
- allocate(self%freefallRadiusTableRadiusInterpolator(self%freefallRadiusTableAlphaCount))
- self %freefallRadiusTableAlphaInterpolator =interpolator(self%freefallRadiusTableAlpha )
- do iAlpha=1,self%freefallRadiusTableAlphaCount
- self%freefallRadiusTableRadiusInterpolator(iAlpha)=interpolator(self%freefallRadiusTable (:,iAlpha))
- end do
- ! Store the minimum and maximum tabulated freefall times across all α values.
- self%freefallTimeMinimum=maxval(self%freefallRadiusTable( 1,:))
- self%freefallTimeMaximum=minval(self%freefallRadiusTable(self%freefallRadiusTableRadiusCount,:))
- ! Display a message.
- call displayUnindent('...done',verbosityLevelWorking)
- ! Specify that tabulation has been made.
- self%freefallRadiusTableInitialized=.true.
- end if
- return
- end subroutine einastoFreefallTabulate
-
- double precision function einastoFreefallTimeScaleFree(self,radius,alpha)
- !!{
- Compute the freefall time in a scale-free Einasto halo.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: alpha , radius
- type (integrator ) :: integrator_
- double precision :: radiusStart , radiusEnd, &
- & alphaParameter
-
- radiusStart =radius
- radiusEnd =0.0d0
- alphaParameter =alpha
- integrator_ =integrator (einastoFreefallTimeScaleFreeIntegrand,toleranceRelative=1.0d-3)
- einastoFreefallTimeScaleFree=integrator_%integrate(radiusEnd ,radiusStart )
- return
-
- contains
-
- double precision function einastoFreefallTimeScaleFreeIntegrand(radius)
- !!{
- Integrand function used for finding the free-fall time in Einasto halos.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- einastoFreefallTimeScaleFreeIntegrand=+1.0d0 &
- & /sqrt(+2.0d0 &
- & *( &
- & +self%potentialScaleFree(radiusStart,1.0d0,alphaParameter) &
- & -self%potentialScaleFree(radius ,1.0d0,alphaParameter) &
- & ) &
- & )
- return
- end function einastoFreefallTimeScaleFreeIntegrand
-
- end function einastoFreefallTimeScaleFree
-
- double precision function einastoRadiusEnclosingDensity(self,node,density)
- !!{
- Implementation of function to compute the radius enclosing a given density for Einasto dark matter halo profiles. This
- function uses a numerical root finder to find the enclosing radius---this is likely not the most efficient solution\ldots
- !!}
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- densityEnclosed_ = density
- node_ => node
- self_ => self
- einastoRadiusEnclosingDensity = self%finderEnclosedDensity%find(rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- return
- end function einastoRadiusEnclosingDensity
-
- double precision function einastoRadiusEnclosingDensityRoot(radius)
- !!{
- Root function used in finding the radius enclosing a given density in Einasto profiles.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- einastoRadiusEnclosingDensityRoot=+densityEnclosed_ &
- & -self_%enclosedMass(node_,radius) &
- & *3.0d0 &
- & /4.0d0 &
- & /Pi &
- & /radius**3
- return
- end function einastoRadiusEnclosingDensityRoot
-
- double precision function einastoRadialVelocityDispersionScaleFree(self,radius,alpha)
- !!{
- Compute the radial velocity dispersion in a scale-free Einasto halo.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ) :: radius , alpha
- double precision , parameter :: radiusTiny =1.0d-9, radiusLarge =5.0d3
- double precision :: radiusMinimum , radiusMaximum
- type (integrator ) :: integrator_
- !$GLC attributes unused :: self
-
- radiusMinimum=max( radius,radiusTiny )
- radiusMaximum=max(10.0d0*radius,radiusLarge)
- integrator_=integrator(einastoJeansEquationIntegrand,toleranceRelative=1.0d-6)
- einastoRadialVelocityDispersionScaleFree=sqrt( &
- & +integrator_%integrate(radiusMinimum,radiusMaximum) &
- & /exp(-(2.0d0/alpha)*(radius**alpha-1.0d0)) &
- & )
- return
-
- contains
-
- double precision function einastoJeansEquationIntegrand(radius)
- !!{
- Integrand for Einasto dark matter profile Jeans equation.
- !!}
- use :: Gamma_Functions, only : Gamma_Function_Incomplete_Complementary
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- einastoJeansEquationIntegrand=+Gamma_Function_Incomplete_Complementary(3.0d0/alpha,2.0d0*radius**alpha/alpha) &
- & *exp(-(2.0d0/alpha)*(radius**alpha-1.0d0)) &
- & /radius**2
- else
- einastoJeansEquationIntegrand=0.0d0
- end if
- return
- end function einastoJeansEquationIntegrand
-
- end function einastoRadialVelocityDispersionScaleFree
-
- subroutine einastoRadialVelocityDispersionTabulate(self,radius,alphaRequired)
- !!{
- Tabulates the radial velocity dispersion vs. radius for Einasto halos.
- !!}
- use :: Display , only : displayCounter, displayIndent , displayUnindent , verbosityLevelWorking
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear, rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOEinasto), intent(inout) :: self
- double precision , intent(in ), optional :: radius , alphaRequired
- logical :: retabulate
- integer :: iAlpha , iRadius , percentage
- double precision :: alpha
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOEinasto ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ !![
+
+ !!]
- retabulate=.not.self%radialVelocityDispersionTableInitialized
- if (present(radius)) then
- do while (radius < self%radialVelocityDispersionRadiusMinimum)
- self%radialVelocityDispersionRadiusMinimum=0.5d0*self%radialVelocityDispersionRadiusMinimum
- retabulate=.true.
- end do
- do while (radius > self%radialVelocityDispersionRadiusMaximum)
- self%radialVelocityDispersionRadiusMaximum=2.0d0*self%radialVelocityDispersionRadiusMaximum
- retabulate=.true.
- end do
- end if
- if (present(alphaRequired)) then
- ! Check for α out of range.
- if (alphaRequired < self%radialVelocityDispersionAlphaMinimum .or. alphaRequired > self%radialVelocityDispersionAlphaMaximum) then
- retabulate=.true.
- ! Compute the range of tabulation.
- self%radialVelocityDispersionAlphaMinimum=min(self%radialVelocityDispersionAlphaMinimum,0.9d0*alphaRequired)
- self%radialVelocityDispersionAlphaMaximum=max(self%radialVelocityDispersionAlphaMaximum,1.1d0*alphaRequired)
- end if
- end if
- if (retabulate) then
- ! Display a message.
- call displayIndent('Constructing Einasto profile radial velocity dispersion lookup table...',verbosityLevelWorking)
- ! Decide how many points to tabulate and allocate table arrays.
- self%radialVelocityDispersionTableRadiusCount=int(log10(self%radialVelocityDispersionRadiusMaximum/self%radialVelocityDispersionRadiusMinimum) &
- & *dble(radialVelocityDispersionTableRadiusPointsPerDecade ) &
- & )+1
- self%radialVelocityDispersionTableAlphaCount =int( (self%radialVelocityDispersionAlphaMaximum -self%radialVelocityDispersionAlphaMinimum ) &
- & *dble(radialVelocityDispersionTableAlphaPointsPerUnit ) &
- & )+1
- if (allocated(self%radialVelocityDispersionTableRadius)) then
- deallocate(self%radialVelocityDispersionTableAlpha )
- deallocate(self%radialVelocityDispersionTableRadius)
- deallocate(self%radialVelocityDispersionTable )
- end if
- allocate(self%radialVelocityDispersionTableAlpha (self%radialVelocityDispersionTableAlphaCount))
- allocate(self%radialVelocityDispersionTableRadius(self%radialVelocityDispersionTableRadiusCount ))
- allocate(self%radialVelocityDispersionTable (self%radialVelocityDispersionTableRadiusCount,self%radialVelocityDispersionTableAlphaCount))
- ! Create a range of radii and α.
- self%radialVelocityDispersionTableAlpha =Make_Range(self%radialVelocityDispersionAlphaMinimum ,self%radialVelocityDispersionAlphaMaximum ,self%radialVelocityDispersionTableAlphaCount ,rangeType=rangeTypeLinear )
- self%radialVelocityDispersionTableRadius=Make_Range(self%radialVelocityDispersionRadiusMinimum,self%radialVelocityDispersionRadiusMaximum,self%radialVelocityDispersionTableRadiusCount,rangeType=rangeTypeLogarithmic)
- ! Loop over radii and α and populate tables.
- do iAlpha=1,self%radialVelocityDispersionTableAlphaCount
- alpha=self%radialVelocityDispersionTableAlpha(iAlpha)
- do iRadius=1,self%radialVelocityDispersionTableRadiusCount
- ! Show progress.
- percentage=int(100.0d0*dble((iAlpha-1)*self%radialVelocityDispersionTableRadiusCount+iRadius-1 ) &
- & /dble(self%radialVelocityDispersionTableAlphaCount*self%radialVelocityDispersionTableRadiusCount) &
- & )
- call displayCounter(percentage,iAlpha == 1 .and. iRadius == 1,verbosityLevelWorking)
- ! Compute the radial velocity dispersion.
- self%radialVelocityDispersionTable(iRadius,iAlpha)=self%radialVelocityDispersionScaleFree(self%radialVelocityDispersionTableRadius(iRadius),alpha)
- end do
- end do
- ! Build interpolators.
- if (allocated(self%radialVelocityDispersionTableAlphaInterpolator )) deallocate(self%radialVelocityDispersionTableAlphaInterpolator )
- if (allocated(self%radialVelocityDispersionTableRadiusInterpolator)) deallocate(self%radialVelocityDispersionTableRadiusInterpolator)
- allocate(self%radialVelocityDispersionTableAlphaInterpolator )
- allocate(self%radialVelocityDispersionTableRadiusInterpolator)
- self%radialVelocityDispersionTableAlphaInterpolator =interpolator(self%radialVelocityDispersionTableAlpha )
- self%radialVelocityDispersionTableRadiusInterpolator=interpolator(self%radialVelocityDispersionTableRadius)
- ! Display a message.
- call displayUnindent('...done',verbosityLevelWorking)
- ! Specify that tabulation has been made.
- self%radialVelocityDispersionTableInitialized=.true.
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionEinasto :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionEinasto)
+ basic => node%basic ()
+ darkMatterProfile => node%darkMatterProfile()
+ !![
+
+
+ massDistributionEinasto( &
+ & mass =basic %mass ( ), &
+ & virialRadius =self %darkMatterHaloScale_%radiusVirial (node), &
+ & scaleLength =darkMatterProfile%scale ( ), &
+ & shapeParameter=darkMatterProfile%shape ( ), &
+ & componentType= componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless()
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end subroutine einastoRadialVelocityDispersionTabulate
+ end function einastoGet
diff --git a/source/dark_matter_profiles_DMO.F90 b/source/dark_matter_profiles_DMO.F90
index 4888192a28..319ffee4b8 100644
--- a/source/dark_matter_profiles_DMO.F90
+++ b/source/dark_matter_profiles_DMO.F90
@@ -25,180 +25,27 @@ module Dark_Matter_Profiles_DMO
!!{
Provides an object that implements dark matter halo profiles.
!!}
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Dark_Matter_Profiles_Generic, only : darkMatterProfileGeneric
- use :: Galacticus_Nodes , only : treeNode
- use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionHeatingClass
+ use :: Galactic_Structure_Options, only : enumerationStructureErrorCodeType, enumerationWeightByType
private
!![
darkMatterProfileDMO
- darkMatterProfileGeneric
Dark Matter Only Halo Profiles
Class providing dark matter-only halo profiles.
NFW
-
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
+
+ Return the mass distribution of the dark matter-only profile.
+ class(massDistributionClass)
yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the logarithmic slope of the density profile in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the {\normalfont \ttfamily m}$^\mathrm{th}$ radial moment of the dark matter profile of {\normalfont \ttfamily node} optionally between the given {\normalfont \ttfamily radiusMinimum} and {\normalfont \ttfamily radiusMaximum} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
-
- Return the total energy for the given {\normalfont \ttfamily node} in units of $M_\odot$ km$^2$ s$^{-2}$.
- double precision
- yes
- type(treeNode), intent(inout) :: node
-
-
- Returns the relation between specific angular momentum and rotation velocity (assuming a rotation velocity that is constant in radius) for the given {\normalfont \ttfamily node}. Specifically, the normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} at which the specific angular momentum of a circular orbit equals {\normalfont \ttfamily specificAngularMomentum} (specified in units of km s$^{-1}$ Mpc.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
-
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
-
-
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the gravitational potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
-
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the radius (in Mpc) enclosing a given density threshold (in $M_\odot \hbox{Mpc}^{-3}$) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: density
-
-
- Returns the normalized Fourier space density profile of the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily waveNumber} (given in units of Mpc$^{-1}$).
- double precision
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: wavenumber
-
-
- Returns the freefall radius (in Mpc) corresponding to the given {\normalfont \ttfamily time} (in Gyr) in {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: time
-
-
- Returns the rate of increase of the freefall radius (in Mpc/Gyr) corresponding to the given {\normalfont \ttfamily time} (in Gyr) in {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: time
-
-
- Returns the radius (in Mpc) enclosing a given mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: mass
- Root_Finder Kind_Numbers
-
- double precision :: radiusGuess
- type (rootFinder), save :: finder
- logical , save :: finderConstructed=.false.
- double precision , save :: radiusPrevious =-huge(0.0d0)
- integer (kind_int8 ), save :: uniqueIDPrevious =-1_kind_int8
- !$omp threadprivate(finder,finderConstructed,radiusPrevious,uniqueIDPrevious)
- if(mass <= 0.0d0) then
- darkMatterProfileDMORadiusEnclosingMass=0.0d0
- return
- end if
- ! Initialize the root finder.
- if (.not.finderConstructed) then
- finder=rootFinder( &
- & rootFunction =darkMatterProfileDMOEnclosedMassRoot, &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & toleranceAbsolute =0.0d0 , &
- & toleranceRelative =1.0d-6 &
- & )
- finderConstructed=.true.
- end if
- if (node%uniqueID() == uniqueIDPrevious) then
- radiusGuess =radiusPrevious
- else
- radiusGuess =self%darkMatterHaloScale_%radiusVirial(node)
- uniqueIDPrevious=node %uniqueID ( )
- end if
- darkMatterProfileDMOSelf => self
- darkMatterProfileDMONode => node
- darkMatterProfileDMOMass_ = mass
- radiusPrevious=finder%find(rootGuess=radiusGuess)
- darkMatterProfileDMORadiusEnclosingMass=radiusPrevious
- return
-
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
!!]
@@ -211,49 +58,13 @@ module Dark_Matter_Profiles_DMO
Class providing models of heating of dark matter profiles.
null
-
- The specific energy of heating at the given {\normalfont \ttfamily radius} in the given {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (darkMatterProfileDMOClass), intent(inout) :: darkMatterProfileDMO_
-
-
- The gradient of the specific energy of heating at the given {\normalfont \ttfamily radius} in the given {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (darkMatterProfileDMOClass), intent(inout) :: darkMatterProfileDMO_
-
-
- Returns true if the specific energy is zero everywhere in the given {\normalfont \ttfamily node}.
- logical
+
+ Return the dark matter profile heating in the dark matter-only profile.
+ class(massDistributionHeatingClass)
yes
- type (treeNode ), intent(inout) :: node
- class(darkMatterProfileDMOClass), intent(inout) :: darkMatterProfileDMO_
+ type(treeNode), intent(inout) :: node
!!]
- ! Module-scope variables used in root finding.
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMOSelf => null()
- type (treeNode ), pointer :: darkMatterProfileDMONode => null()
- double precision :: darkMatterProfileDMOMass_
- !$omp threadprivate(darkMatterProfileDMOSelf,darkMatterProfileDMONode,darkMatterProfileDMOMass_)
-
-contains
-
- double precision function darkMatterProfileDMOEnclosedMassRoot(radius)
- !!{
- Root function used in solving for the radius that encloses a given mass.
- !!}
- implicit none
- double precision,intent(in) :: radius
-
- darkMatterProfileDMOEnclosedMassRoot=darkMatterProfileDMOSelf%enclosedMass(darkMatterProfileDMONode,radius)-darkMatterProfileDMOMass_
- return
- end function darkMatterProfileDMOEnclosedMassRoot
-
end module Dark_Matter_Profiles_DMO
diff --git a/source/dark_matter_profiles_DMO.NFW.F90 b/source/dark_matter_profiles_DMO.NFW.F90
index a076fc6699..5bd95a7062 100644
--- a/source/dark_matter_profiles_DMO.NFW.F90
+++ b/source/dark_matter_profiles_DMO.NFW.F90
@@ -21,19 +21,15 @@
An implementation of \cite{navarro_universal_1997} dark matter halo profiles.
!!}
- use :: Kind_Numbers, only : kind_int8
- use :: Tables , only : table1D , table1DLogarithmicLinear
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
- A dark matter profile DMO class which implements the \gls{nfw} density profile \citep{navarro_universal_1997}:
- \begin{equation}
- \rho_\mathrm{dark matter}(r) \propto \left({r\over r_\mathrm{s}}\right)^{-1} \left[1 + \left({r\over r_\mathrm{s}}\right)
- \right]^{-2},
- \end{equation}
- normalized such that the total mass of the \gls{node} is enclosed with the virial radius and with the scale length
- $r_\mathrm{s} = r_\mathrm{virial}/c$ where $c$ is the halo concentration (see \refPhysics{darkMatterProfileConcentration}).
+ A dark matter profile DMO class which builds \refClass{} objects to implement the \gls{nfw} density profile
+ \citep{navarro_universal_1997}, normalized such that the total mass of the \gls{node} is enclosed with the virial radius and
+ with the scale length $r_\mathrm{s} = r_\mathrm{virial}/c$ where $c$ is the halo concentration (see
+ \refPhysics{darkMatterProfileConcentration}).
!!]
@@ -42,95 +38,11 @@
A dark matter halo profile class implementing \cite{navarro_universal_1997} dark matter halos.
!!}
private
- ! Minimum and maximum concentrations to tabulate.
- double precision :: concentrationMinimum , concentrationMaximum
- ! Minimum and maximum radii to tabulate.
- double precision :: freefallRadiusMinimum , radiusMinimum
- double precision :: freefallRadiusMaximum , radiusMaximum
- double precision :: freefallTimeMinimum , specificAngularMomentumMinimum
- double precision :: freefallTimeMaximum , specificAngularMomentumMaximum
- double precision :: enclosedDensityRadiusMinimum , enclosedDensityRadiusMaximum
- double precision :: enclosedDensityMinimum , enclosedDensityMaximum
- ! Tables of NFW properties.
- logical :: nfwFreefallTableInitialized =.false., nfwInverseTableInitialized =.false., &
- & nfwTableInitialized =.false., nfwEnclosedDensityTableInitialized =.false.
- integer :: nfwFreefallTableNumberPoints , nfwInverseTableNumberPoints , &
- & nfwTableNumberPoints , nfwEnclosedDensityTableNumberPoints
- type (table1DLogarithmicLinear) :: nfwConcentrationTable
- ! Tables.
- type (table1DLogarithmicLinear) :: nfwFreeFall , nfwSpecificAngularMomentum , &
- & nfwEnclosedDensity
- class (table1D ), allocatable :: nfwFreefallInverse , nfwSpecificAngularMomentumInverse , &
- & nfwEnclosedDensityInverse
- ! Module variables used in integrations.
- double precision :: concentrationParameter , radiusStart
- ! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID
- ! Record of whether or not quantities have been computed.
- logical :: specificAngularMomentumScalingsComputed , maximumVelocityComputed
- ! Stored values of computed quantities.
- double precision :: specificAngularMomentumLengthScale , specificAngularMomentumScale , &
- & concentrationPrevious , nfwNormalizationFactorPrevious , &
- & maximumVelocityPrevious , enclosedDensityPrevious , &
- & enclosingDensityRadiusPrevious , densityScalePrevious , &
- & enclosedMassPrevious , enclosingMassRadiusPrevious , &
- & massScalePrevious , circularVelocityPrevious , &
- & circularVelocityRadiusPrevious , radialVelocityDispersionPrevious , &
- & radialVelocityDispersionRadiusPrevious , enclosedMassScaleFreePrevious , &
- & enclosedMassScaleFreeRadiusPrevious
- logical :: velocityDispersionUseSeriesExpansion
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ logical :: velocityDispersionUseSeriesExpansion
contains
- !![
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- !!]
- final :: nfwDestructor
- procedure :: autoHook => nfwAutoHook
- procedure :: calculationReset => nfwCalculationReset
- procedure :: density => nfwDensity
- procedure :: densityLogSlope => nfwDensityLogSlope
- procedure :: radialMoment => nfwRadialMoment
- procedure :: enclosedMass => nfwEnclosedMass
- procedure :: radiusEnclosingDensity => nfwRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => nfwRadiusEnclosingMass
- procedure :: potential => nfwPotential
- procedure :: circularVelocity => nfwCircularVelocity
- procedure :: radiusCircularVelocityMaximum => nfwRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => nfwCircularVelocityMaximum
- procedure :: radialVelocityDispersion => nfwRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => nfwRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => nfwRotationNormalization
- procedure :: energy => nfwEnergy
- procedure :: kSpace => nfwKSpace
- procedure :: freefallRadius => nfwFreefallRadius
- procedure :: freefallRadiusIncreaseRate => nfwFreefallRadiusIncreaseRate
- procedure :: profileEnergy => nfwProfileEnergy
- procedure :: specificAngularMomentumScaleFree => nfwSpecificAngularMomentumScaleFree
- procedure :: angularMomentumScaleFree => nfwAngularMomentumScaleFree
- procedure :: enclosedMassScaleFree => nfwEnclosedMassScaleFree
- procedure :: densityEnclosedByRadiusScaleFree => nfwDensityEnclosedByRadiusScaleFree
- procedure :: densityScaleFree => nfwDensityScaleFree
- procedure :: radialVelocityDispersionScaleFree => nfwRadialVelocityDispersionScaleFree
- procedure :: radialVelocityDispersionScaleFreeSeriesExpansion => nfwRadialVelocityDispersionScaleFreeSeriesExpansion
- procedure :: tabulate => nfwTabulate
- procedure :: inverseAngularMomentum => nfwInverseAngularMomentum
- procedure :: freefallTabulate => nfwFreefallTabulate
- procedure :: freefallTimeScaleFree => nfwFreefallTimeScaleFree
- procedure :: enclosedDensityTabulate => nfwEnclosedDensityTabulate
+ final :: nfwDestructor
+ procedure :: get => nfwGet
end type darkMatterProfileDMONFW
interface darkMatterProfileDMONFW
@@ -141,14 +53,6 @@
module procedure nfwConstructorInternal
end interface darkMatterProfileDMONFW
- ! Number of points per decade of concentration in NFW tabulations.
- integer, parameter :: tablePointsPerDecade =100
- integer, parameter :: inverseTablePointsPerDecade =100
- integer, parameter :: freefallTablePointsPerDecade =300
- integer, parameter :: enclosedDensityTablePointsPerDecade=100
- ! Indices for tabulated quantities.
- integer, parameter :: concentrationEnergyIndex = 1, concentrationRotationNormalizationIndex=2
-
contains
function nfwConstructorParameters(parameters) result(self)
@@ -193,22 +97,6 @@ function nfwConstructorInternal(velocityDispersionUseSeriesExpansion,darkMatterH
!!]
- self%enclosedMassScaleFreePrevious =-1.0d+0
- self%enclosedMassScaleFreeRadiusPrevious=-1.0d+0
- self%concentrationPrevious =-1.0d+0
- self%concentrationMinimum = 1.0d+0
- self%concentrationMaximum =20.0d+0
- self%freefallRadiusMinimum = 1.0d-3
- self%freefallRadiusMaximum = 1.0d+2
- self%radiusMinimum = 1.0d-3
- self%radiusMaximum = 1.0d+2
- self%enclosedDensityRadiusMinimum = 1.0d-3
- self%enclosedDensityRadiusMaximum = 1.0d+2
- self%nfwEnclosedDensityTableInitialized =.false.
- self%nfwFreefallTableInitialized =.false.
- self%nfwInverseTableInitialized =.false.
- self%nfwTableInitialized =.false.
- self%lastUniqueID =-1
! Ensure that the dark matter profile component supports a "scale" property.
if (.not.defaultDarkMatterProfileComponent%scaleIsGettable()) &
& call Error_Report &
@@ -220,1400 +108,79 @@ function nfwConstructorInternal(velocityDispersionUseSeriesExpansion,darkMatterH
& ) // &
& {introspection:location} &
& )
- ! Initialize the tabulations.
- call self%tabulate ()
- call self%inverseAngularMomentum()
return
end function nfwConstructorInternal
- subroutine nfwAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMONFW), intent(inout) :: self
-
- call calculationResetEvent%attach(self,nfwCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMONFW')
- return
- end subroutine nfwAutoHook
-
subroutine nfwDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily nfw} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMONFW), intent(inout) :: self
- if (self%nfwFreefallTableInitialized) then
- call self%nfwFreeFall %destroy()
- call self%nfwFreeFallInverse %destroy()
- deallocate(self%nfwFreefallInverse )
- end if
- if (self%nfwEnclosedDensityTableInitialized) then
- call self%nfwEnclosedDensity %destroy()
- call self%nfwEnclosedDensityInverse %destroy()
- deallocate(self%nfwEnclosedDensityInverse )
- end if
- if (self%nfwInverseTableInitialized ) then
- call self%nfwSpecificAngularMomentum %destroy()
- call self%nfwSpecificAngularMomentumInverse%destroy()
- deallocate(self%nfwSpecificAngularMomentumInverse)
- end if
- if (self%nfwTableInitialized ) then
- call self%nfwConcentrationTable %destroy()
- end if
!![
!!]
- if (calculationResetEvent%isAttached(self,nfwCalculationReset)) call calculationResetEvent%detach(self,nfwCalculationReset)
return
end subroutine nfwDestructor
- subroutine nfwCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%specificAngularMomentumScalingsComputed=.false.
- self%maximumVelocityComputed =.false.
- self%enclosedDensityPrevious =-1.0d0
- self%densityScalePrevious =-1.0d0
- self%enclosedMassPrevious =-1.0d0
- self%massScalePrevious =-1.0d0
- self%circularVelocityRadiusPrevious =-1.0d0
- self%radialVelocityDispersionRadiusPrevious =-1.0d0
- self%lastUniqueID =uniqueID
- return
- end subroutine nfwCalculationReset
-
- subroutine nfwTabulate(self,concentration)
- !!{
- Tabulate properties of the NFW halo profile which must be computed numerically.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ), optional :: concentration
- integer :: iConcentration
- logical :: retabulate
- double precision :: tableConcentration
-
- retabulate=.not.self%nfwTableInitialized
- if (present(concentration)) then
- if (concentration < self%concentrationMinimum) then
- self%concentrationMinimum=0.5d0*concentration
- retabulate=.true.
- end if
- if (concentration > self%concentrationMaximum) then
- self%concentrationMaximum=2.0d0*concentration
- retabulate=.true.
- end if
- end if
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%nfwTableNumberPoints=int(log10(self%concentrationMaximum/self%concentrationMinimum)*dble(tablePointsPerDecade))+1
- call self%nfwConcentrationTable%destroy()
- call self%nfwConcentrationTable%create(self%concentrationMinimum,self%concentrationMaximum,self%nfwTableNumberPoints,2)
- ! Loop over concentrations and populate tables.
- do iConcentration=1,self%nfwTableNumberPoints
- tableConcentration=self%nfwConcentrationTable%x(iConcentration)
- call self%nfwConcentrationTable%populate( self%profileEnergy (tableConcentration),iConcentration,table=concentrationEnergyIndex )
- call self%nfwConcentrationTable%populate(tableConcentration/self%angularMomentumScaleFree(tableConcentration),iConcentration,table=concentrationRotationNormalizationIndex)
- end do
- ! Specify that tabulation has been made.
- self%nfwTableInitialized=.true.
- end if
- return
- end subroutine nfwTabulate
-
- subroutine nfwInverseAngularMomentum(self,specificAngularMomentum)
- !!{
- Tabulates the specific angular momentum vs. radius in an NFW profile for rapid inversion.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ), optional :: specificAngularMomentum
- integer :: iRadius
- logical :: retabulate
-
- retabulate=.not.self%nfwInverseTableInitialized
- ! If the table has not yet been made, compute and store the specific angular momenta corresponding to the minimum and maximum
- ! radii that will be tabulated by default.
- if (retabulate) then
- self%specificAngularMomentumMinimum=self%specificAngularMomentumScaleFree(self%radiusMinimum)
- self%specificAngularMomentumMaximum=self%specificAngularMomentumScaleFree(self%radiusMaximum)
- end if
- if (present(specificAngularMomentum)) then
- do while (specificAngularMomentum < self%specificAngularMomentumMinimum)
- self%radiusMinimum =0.5d0*self%radiusMinimum
- self%specificAngularMomentumMinimum=self%specificAngularMomentumScaleFree(self%radiusMinimum)
- retabulate=.true.
- end do
- do while (specificAngularMomentum > self%specificAngularMomentumMaximum)
- self%radiusMaximum =2.0d0*self%radiusMaximum
- self%specificAngularMomentumMaximum=self%specificAngularMomentumScaleFree(self%radiusMaximum)
- retabulate=.true.
- end do
- end if
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%nfwInverseTableNumberPoints=int(log10(self%radiusMaximum/self%radiusMinimum)*dble(inverseTablePointsPerDecade))+1
- ! Create a range of radii.
- call self%nfwSpecificAngularMomentum%destroy( )
- call self%nfwSpecificAngularMomentum%create (self%radiusMinimum,self%radiusMaximum,self%nfwInverseTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%nfwInverseTableNumberPoints
- call self%nfwSpecificAngularMomentum%populate( &
- & self%specificAngularMomentumScaleFree(self%nfwSpecificAngularMomentum%x(iRadius)), &
- & iRadius &
- & )
- end do
- call self%nfwSpecificAngularMomentum%reverse(self%nfwSpecificAngularMomentumInverse)
- ! Specify that tabulation has been made.
- self%nfwInverseTableInitialized=.true.
- end if
- return
- end subroutine nfwInverseAngularMomentum
-
- double precision function nfwDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- basic => node %basic ( )
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self %darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- nfwDensity = +self %densityScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius) &
- & *basic%mass ( ) &
- & / scaleRadius**3
- return
- end function nfwDensity
-
- double precision function nfwDensityLogSlope(self,node,radius)
+ function nfwGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo, massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionNFW , kinematicsDistributionNFW
implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius, scaleRadius
- !$GLC attributes unused :: self
-
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius/scaleRadius
- nfwDensityLogSlope = -(1.0d0+3.0d0*radiusOverScaleRadius) &
- & /(1.0d0+ radiusOverScaleRadius)
- return
- end function nfwDensityLogSlope
-
- double precision function nfwRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- class (nodeComponentBasic ) , pointer :: basic
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: scaleRadius , virialRadiusOverScaleRadius, &
- & radiusMinimumActual , radiusMaximumActual
-
- radiusMinimumActual=0.0d0
- radiusMaximumActual=self%darkMatterHaloScale_%radiusVirial(node)
- if (present(radiusMinimum)) radiusMinimumActual=radiusMinimum
- if (present(radiusMaximum)) radiusMaximumActual=radiusMaximum
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius =darkMatterProfile%scale()
- virialRadiusOverScaleRadius =self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- nfwRadialMoment =+basic%mass() &
- & *scaleRadius**(moment-2.0d0) &
- & /( &
- & +log(1.0d0+virialRadiusOverScaleRadius) &
- & - virialRadiusOverScaleRadius &
- & / (1.0d0+virialRadiusOverScaleRadius) &
- & ) &
- & /4.0d0 &
- & /Pi &
- & *( &
- & +nfwRadialMomentScaleFree(radiusMaximumActual/scaleRadius) &
- & -nfwRadialMomentScaleFree(radiusMinimumActual/scaleRadius) &
- & )
- return
-
- contains
-
- double precision function nfwRadialMomentScaleFree(radius)
- !!{
- Provides the scale-free part of the radial moment of the NFW density profile.
- !!}
- use :: Hypergeometric_Functions, only : Hypergeometric_2F1
- use :: Numerical_Comparison , only : Values_Agree
- implicit none
- double precision, intent(in ) :: radius
-
- if (Values_Agree(moment,0.0d0,absTol=1.0d-6)) then
- ! Take the real part of this improper integral. The imaginary parts must cancel when taking differences to compute a
- ! proper integral.
- nfwRadialMomentScaleFree=+1.0d0/ (1.0d0+ radius ) &
- & -2.0d0*real(atanh(dcmplx(1.0d0+2.0d0*radius,0.0d0)))
- else if (Values_Agree(moment,1.0d0,absTol=1.0d-6)) then
- nfwRadialMomentScaleFree=-1.0d0/ (1.0d0 +radius )
- else if (Values_Agree(moment,2.0d0,absTol=1.0d-6)) then
- nfwRadialMomentScaleFree=+1.0d0/ (1.0d0 +radius ) &
- & + log (1.0d0 +radius )
- else if (Values_Agree(moment,3.0d0,absTol=1.0d-6)) then
- nfwRadialMomentScaleFree=+ radius &
- & -1.0d0/ (1.0d0 +radius ) &
- & -2.0d0*log (1.0d0 +radius )
- else
- nfwRadialMomentScaleFree=+(1.0d0+radius)**(moment-1.0d0) &
- & /moment &
- & / (moment-1.0d0) &
- & *( &
- & - moment &
- & * Hypergeometric_2F1([1.0d0-moment,-moment],[2.0d0-moment],1.0d0/(1.0d0+radius)) &
- & +(1.0d0+radius) &
- & *(moment-1.0d0) &
- & *( &
- & +(radius/(1.0d0+radius))**moment &
- & -Hypergeometric_2F1([ -moment,-moment],[1.0d0-moment],1.0d0/(1.0d0+radius)) &
- & ) &
- & )
- end if
- return
- end function nfwRadialMomentScaleFree
-
- end function nfwRadialMoment
-
- double precision function nfwEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- nfwEnclosedMass = self%enclosedMassScaleFree(radiusOverScaleRadius,virialRadiusOverScaleRadius) &
- & *basic%mass()
- return
- end function nfwEnclosedMass
-
- double precision function nfwPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galactic_Structure_Options, only : enumerationStructureErrorCodeType, structureErrorCodeSuccess
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile
- double precision , parameter :: radiusSmall =1.0d-10
- double precision :: radiusOverScaleRadius , radiusTerm, &
- & virialRadiusOverScaleRadius
-
- if (present(status)) status=structureErrorCodeSuccess
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- radiusOverScaleRadius = radius /darkMatterProfile%scale()
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- if (radiusOverScaleRadius < radiusSmall) then
- ! Use a series solution for very small radii.
- radiusTerm=1.0d0-0.5d0*radiusOverScaleRadius
- else
- ! Use the full expression for larger radii.
- radiusTerm=log(1.0d0+radiusOverScaleRadius)/radiusOverScaleRadius
- end if
- nfwPotential=-virialRadiusOverScaleRadius &
- & *radiusTerm &
- & /( &
- & +log(1.0d0+virialRadiusOverScaleRadius) &
- & - virialRadiusOverScaleRadius &
- & / (1.0d0+virialRadiusOverScaleRadius) &
- & ) &
- & *self%darkMatterHaloScale_%velocityVirial(node)**2
- return
- end function nfwPotential
-
- double precision function nfwCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Compute the circular velocity if the radius has changed.
- if (radius /= self%circularVelocityRadiusPrevious) then
- self%circularVelocityPrevious =sqrt(gravitationalConstantGalacticus*self%enclosedMass(node,radius)/radius)
- self%circularVelocityRadiusPrevious=radius
- end if
- nfwCircularVelocity=self%circularVelocityPrevious
- else
- nfwCircularVelocity=0.0d0
- end if
- return
- end function nfwCircularVelocity
-
- double precision function nfwRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- ! The radius (in scale-free units) at the peak of the NFW rotation curve. Numerical value found using Mathematica.
- double precision , parameter :: radiusCircularVelocityMaximumScaleFree=2.162581587064612d0
- double precision :: scaleRadius
-
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- nfwRadiusCircularVelocityMaximum = +radiusCircularVelocityMaximumScaleFree &
- & *scaleRadius
- return
- end function nfwRadiusCircularVelocityMaximum
-
- double precision function nfwCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- class (nodeComponentBasic ), pointer :: basic
- ! The circular velocity (in scale-free units) at the peak of the NFW rotation curve. Numerical value found using Mathematica.
- double precision , parameter :: circularVelocityMaximumScaleFree=0.4649909628174221d0
- double precision :: scaleRadius
-
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Check if maximum velocity is already computed. Compute and store if not.
- if (.not.self%maximumVelocityComputed) then
- basic => node %basic ( )
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- ! Ensure mass profile normalization factor has been computed.
- call nfwMassNormalizationFactor(self,self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius)
- ! Evaluate the circular velocity at the peak of the rotation curve.
- self%maximumVelocityPrevious=+circularVelocityMaximumScaleFree &
- & *sqrt( &
- & +gravitationalConstantGalacticus &
- & *basic %mass () &
- & *self %nfwNormalizationFactorPrevious &
- & /scaleRadius &
- & )
- self%maximumVelocityComputed= .true.
- end if
- nfwCircularVelocityMaximum=self%maximumVelocityPrevious
- return
- end function nfwCircularVelocityMaximum
-
- double precision function nfwRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: radiusOverScaleRadius , scaleRadius, &
- & virialRadiusOverScaleRadius
-
- if (radius > 0.0d0) then
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Compute the radial velocity dispersion if the radius has changed.
- if (radius /= self%radialVelocityDispersionRadiusPrevious) then
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale()
- radiusOverScaleRadius = radius /scaleRadius
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- if (self%velocityDispersionUseSeriesExpansion) then
- self%radialVelocityDispersionPrevious=+self%radialVelocityDispersionScaleFreeSeriesExpansion(radiusOverScaleRadius,virialRadiusOverScaleRadius) &
- & *self%darkMatterHaloScale_%velocityVirial(node)
- else
- self%radialVelocityDispersionPrevious=+self%radialVelocityDispersionScaleFree (radiusOverScaleRadius,virialRadiusOverScaleRadius) &
- & *self%darkMatterHaloScale_%velocityVirial(node)
- end if
- end if
- nfwRadialVelocityDispersion=self%radialVelocityDispersionPrevious
- else
- nfwRadialVelocityDispersion=0.0d0
- end if
- return
- end function nfwRadialVelocityDispersion
-
- double precision function nfwRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: specificAngularMomentumScaleFree
-
- ! Return immediately with zero radius for non-positive specific angular momenta.
- if (specificAngularMomentum <= 0.0d0) then
- nfwRadiusFromSpecificAngularMomentum=0.0d0
- return
- end if
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Check if scalings are already computed. Compute and store if not.
- if (.not.self%specificAngularMomentumScalingsComputed) then
- ! Flag that scale quantities are now computed.
- self%specificAngularMomentumScalingsComputed=.true.
-
- ! Get the dark matter profile.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- self%specificAngularMomentumLengthScale=darkMatterProfile%scale()
-
- ! Get the specific angular momentum scale.
- self%specificAngularMomentumScale=self%specificAngularMomentumLengthScale*self%circularVelocity(node&
- &,self%specificAngularMomentumLengthScale)
- end if
-
- ! Compute the specific angular momentum in scale free units (using the scale length for distances the sqrt(G M(r_scale) /
- ! r_scale) for velocities).
- specificAngularMomentumScaleFree=specificAngularMomentum/self%specificAngularMomentumScale
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%inverseAngularMomentum(specificAngularMomentumScaleFree)
-
- ! Interpolate to get the dimensionless radius at which this specific angular momentum is found.
- nfwRadiusFromSpecificAngularMomentum=self%nfwSpecificAngularMomentumInverse%interpolate(specificAngularMomentumScaleFree)
-
- ! Convert to a physical radius.
- nfwRadiusFromSpecificAngularMomentum=nfwRadiusFromSpecificAngularMomentum*self%specificAngularMomentumLengthScale
- return
- end function nfwRadiusFromSpecificAngularMomentum
-
- double precision function nfwRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Find the concentration parameter of this halo.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
-
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%tabulate(concentration)
-
- ! Find the rotation normalization by interpolation.
- nfwRotationNormalization=self%nfwConcentrationTable%interpolate(concentration,table&
- &=concentrationRotationNormalizationIndex)/self%darkMatterHaloScale_%radiusVirial(node)
- return
- end function nfwRotationNormalization
-
- double precision function nfwEnergy(self,node)
- !!{
- Return the energy of an NFW halo density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- class (nodeComponentBasic ), pointer :: basic
- double precision :: concentration
-
- ! Get components.
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Find the concentration parameter of this halo.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
-
- ! Ensure that the interpolations exist and extend sufficiently far.
- call self%tabulate(concentration)
-
- ! Find the energy by interpolation.
- nfwEnergy=+self %nfwConcentrationTable%interpolate (concentration,table=concentrationEnergyIndex) &
- & *self %darkMatterHaloScale_ %velocityVirial(node )**2 &
- & *basic %mass ( )
- return
- end function nfwEnergy
-
- double precision function nfwAngularMomentumScaleFree(self,concentration)
- !!{
- Returns the total angular momentum (in units of the virial mass times scale radius times [assumed constant] rotation speed)
- in an NFW dark matter profile with given {\normalfont \ttfamily concentration}. This is given by:
- \begin{equation}
- J = \left. \int_0^c 4 \pi x^3 \rho(x) \d x \right/ \int_0^c 4 \pi x^2 \rho(x) \d x,
- \end{equation}
- where $x$ is radius in units of the scale radius and $c$ is concentration. This can be evaluated to give
- \begin{equation}
- J = \left. \left[ 1 + c - 2 \ln (1+c) - {1 \over 1+c} \right] \right/ \left[ \ln(1+c)-{c\over 1+c} \right].
- \end{equation}
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration
- !$GLC attributes unused :: self
-
- nfwAngularMomentumScaleFree=(1.0d0+concentration-2.0d0*log(1.0d0+concentration)-1.0d0/(1.0d0+concentration)) &
- &/(log(1.0d0+concentration)-concentration/(1.0d0+concentration))
- return
- end function nfwAngularMomentumScaleFree
-
- double precision function nfwSpecificAngularMomentumScaleFree(self,radius)
- !!{
- Returns the specific angular momentum, normalized to unit scale length and unit velocity at the scale radius, at position
- {\normalfont \ttfamily radius} (in units of the scale radius) in an NFW profile.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusSmall=1.0d-9
- !$GLC attributes unused :: self
-
- if (radius < radiusSmall) then
- ! Use a series expansion solution for accuracy.
- nfwSpecificAngularMomentumScaleFree=+radius**1.5d0 &
- & /sqrt( &
- & + 2.0d0 &
- & *log(2.0d0) &
- & - 1.0d0 &
- & ) &
- & *( &
- & + 1.0d0 &
- & - radius &
- & *( &
- & - 2.0d0/ 3.0d0 &
- & + radius &
- & *( &
- & + 19.0d0/ 36.0d0 &
- & + radius &
- & *( &
- & - 121.0d0/ 270.0d0 &
- & +radius &
- & *( &
- & +5123.0d0/12960.0d0 &
- & ) &
- & ) &
- & ) &
- & ) &
- & )
- else
- ! Use the full solution.
- nfwSpecificAngularMomentumScaleFree=sqrt(radius*self%enclosedMassScaleFree(radius,1.0d0))
- end if
- return
- end function nfwSpecificAngularMomentumScaleFree
-
- double precision function nfwEnclosedMassScaleFree(self,radius,concentration)
- !!{
- Returns the enclosed mass (in units of the virial mass) in an NFW dark matter profile with given {\normalfont \ttfamily concentration} at the
- given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration , radius
- double precision , parameter :: minimumRadiusForExactSolution =1.0d-6
- ! Precomputed NFW normalization factor for unit radius.
- double precision , parameter :: nfwNormalizationFactorUnitRadius=log(2.0d0)-0.5d0
-
- if (radius /= self%enclosedMassScaleFreeRadiusPrevious) then
- self%enclosedMassScaleFreeRadiusPrevious=radius
- if (radius == 1.0d0 ) then
- self%enclosedMassScaleFreePrevious=nfwNormalizationFactorUnitRadius
- else if (radius >= minimumRadiusForExactSolution) then
- self%enclosedMassScaleFreePrevious=(log(1.0d0+radius)-radius/(1.0d0+radius))
- else
- self%enclosedMassScaleFreePrevious=(radius**2)*(0.5d0+radius*(-2.0d0/3.0d0+radius*(0.75d0+radius*(-0.8d0))))
- end if
- end if
- nfwEnclosedMassScaleFree=self%enclosedMassScaleFreePrevious
- ! Compute the mass profile normalization factor.
- call nfwMassNormalizationFactor(self,concentration)
- ! Evaluate the scale-free enclosed mass.
- nfwEnclosedMassScaleFree=nfwEnclosedMassScaleFree*self%nfwNormalizationFactorPrevious
- return
- end function nfwEnclosedMassScaleFree
-
- subroutine nfwMassNormalizationFactor(self,concentration)
- !!{
- Compute the normalization factor for the NFW mass profile.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration
- ! Precomputed NFW normalization factor for unit concentration.
- double precision , parameter :: nfwNormalizationFactorUnitConcentration=1.0d0/(log(2.0d0)-0.5d0)
-
- ! Check if we were called with a different concentration compared to the previous call.
- if (concentration /= self%concentrationPrevious) then
- ! We were, so recompute the normalization factor.
- if (concentration == 1.0d0) then
- self%nfwNormalizationFactorPrevious=nfwNormalizationFactorUnitConcentration
- else
- self%nfwNormalizationFactorPrevious=1.0d0/(log(1.0d0+concentration)-concentration/(1.0d0+concentration))
- end if
- self%concentrationPrevious=concentration
- end if
- return
- end subroutine nfwMassNormalizationFactor
-
- double precision function nfwRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in units of the scale radius) in an NFW dark matter profile with given {\normalfont \ttfamily
- concentration} which encloses a given density (in units of the virial mass per cubic scale radius).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: scaleRadius , densityScaleFree, &
- & virialRadiusOverScaleRadius
-
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Get scale radius if required.
- if (self%densityScalePrevious < 0.0d0 .or. density /= self%enclosedDensityPrevious) then
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- ! Compute the density scale if necessary.
- if (self%densityScalePrevious < 0.0d0) then
- ! Extract profile parameters.
- basic => node %basic ( )
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- ! Compute density normalization scale.
- self%densityScalePrevious=+scaleRadius **3 &
- & /basic %mass ( ) &
- & /self %enclosedMassScaleFree(1.0d0,virialRadiusOverScaleRadius)
- end if
- ! Compute radius enclosing density if necessary.
- if (density /= self%enclosedDensityPrevious) then
- self%enclosedDensityPrevious=density
- ! Compute scaled density.
- densityScaleFree=+ density &
- & *self%densityScalePrevious
- ! Ensure density table spans required range.
- call self%enclosedDensityTabulate(densityScaleFree)
- ! Interpolate in density table to find the required radius.
- self%enclosingDensityRadiusPrevious=self%nfwEnclosedDensityInverse%interpolate(-densityScaleFree)*scaleRadius
- end if
- end if
- nfwRadiusEnclosingDensity=self%enclosingDensityRadiusPrevious
- return
- end function nfwRadiusEnclosingDensity
-
- double precision function nfwRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in an NFW dark matter profile with given {\normalfont \ttfamily
- concentration} which encloses a given mass (in $M_\odot$).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile, treeNode
- use :: Lambert_Ws , only : Lambert_W0
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision , parameter :: massScaleFreeSmall =3.0d-4
- double precision :: scaleRadius , massScaleFree, &
- & virialRadiusOverScaleRadius
-
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Get scale radius if required.
- if (self%massScalePrevious < 0.0d0 .or. mass /= self%enclosedMassPrevious) then
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- ! Compute the mass scale if necessary.
- if (self%massScalePrevious < 0.0d0) then
- ! Extract profile parameters.
- basic => node%basic()
- virialRadiusOverScaleRadius = self%darkMatterHaloScale_%radiusVirial(node)/scaleRadius
- ! Compute the mass profile normalization factor.
- call nfwMassNormalizationFactor(self,virialRadiusOverScaleRadius)
- ! Compute mass normalization scale.
- self%massScalePrevious = 1.0d0/basic%mass()/self%nfwNormalizationFactorPrevious
- end if
- ! Compute radius enclosing mass if necessary.
- if (mass /= self%enclosedMassPrevious) then
- self%enclosedMassPrevious = mass
- ! Compute scaled mass.
- massScaleFree =+mass &
- & *self%massScalePrevious
- ! Compute radius.
- if (massScaleFree < massScaleFreeSmall) then
- ! Use a series solution for very small radii.
- self%enclosingMassRadiusPrevious=+ sqrt(2.0d0)*massScaleFree**0.5d0 &
- & + 4.0d0/ 3.0d0 *massScaleFree &
- & + 13.0d0/ 9.0d0/sqrt(2.0d0)*massScaleFree**1.5d0 &
- & + 92.0d0/ 135.0d0 *massScaleFree**2 &
- & + 313.0d0/ 540.0d0/sqrt(2.0d0)*massScaleFree**2.5d0 &
- & + 1928.0d0/ 8505.0d0 *massScaleFree**3 &
- & +56201.0d0/340200.0d0/sqrt(2.0d0)*massScaleFree**3.5d0 &
- & + 358.0d0/ 1701.0d0 *massScaleFree**4
- else
- self%enclosingMassRadiusPrevious=-( &
- & +1.0d0/Lambert_W0(-exp(-1.0d0-massScaleFree)) &
- & +1.0d0 &
- & )
- end if
- self%enclosingMassRadiusPrevious = self%enclosingMassRadiusPrevious * scaleRadius
- end if
- end if
- nfwRadiusEnclosingMass=self%enclosingMassRadiusPrevious
- return
- end function nfwRadiusEnclosingMass
-
- double precision function nfwDensityEnclosedByRadiusScaleFree(self,radius)
- !!{
- Returns the density (in units of the virial mass per cubic scale radius) in an NFW dark matter profile with given {\normalfont \ttfamily
- concentration} which is enclosed a given radius (in units of the scale radius).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: radius
-
- nfwDensityEnclosedByRadiusScaleFree=+3.0d0 &
- & *self%enclosedMassScaleFree(radius,1.0d0) &
- & /4.0d0 &
- & /Pi &
- & / radius **3
- return
- end function nfwDensityEnclosedByRadiusScaleFree
-
- subroutine nfwEnclosedDensityTabulate(self,enclosedDensityScaleFree)
- !!{
- Tabulates the enclosed density vs. radius for NFW halos.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: enclosedDensityScaleFree
- logical :: retabulate
- integer :: iRadius
-
- retabulate=.not.self%nfwEnclosedDensityTableInitialized
- ! If the table has not yet been made, compute and store the enclosed density corresponding to the minimum and maximum radii
- ! that will be tabulated by default.
- if (retabulate) then
- self%enclosedDensityMinimum=self%densityEnclosedByRadiusScaleFree(self%enclosedDensityRadiusMaximum)
- self%enclosedDensityMaximum=self%densityEnclosedByRadiusScaleFree(self%enclosedDensityRadiusMinimum)
- end if
- do while (enclosedDensityScaleFree < self%enclosedDensityMinimum)
- self%enclosedDensityRadiusMaximum=2.0d0*self%enclosedDensityRadiusMaximum
- self%enclosedDensityMinimum=self%densityEnclosedByRadiusScaleFree(self%enclosedDensityRadiusMaximum)
- retabulate=.true.
- end do
- do while (enclosedDensityScaleFree > self%enclosedDensityMaximum)
- self%enclosedDensityRadiusMinimum=0.5d0*self%enclosedDensityRadiusMinimum
- self%enclosedDensityMaximum=self%densityEnclosedByRadiusScaleFree(self%enclosedDensityRadiusMinimum)
- retabulate=.true.
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%nfwEnclosedDensityTableNumberPoints=int(log10(self%enclosedDensityRadiusMaximum/self%enclosedDensityRadiusMinimum)*dble(enclosedDensityTablePointsPerDecade))+1
- ! Create the table.
- call self%nfwEnclosedDensity%destroy( )
- call self%nfwEnclosedDensity%create (self%enclosedDensityRadiusMinimum,self%enclosedDensityRadiusMaximum,self%nfwEnclosedDensityTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%nfwEnclosedDensityTableNumberPoints
- call self%nfwEnclosedDensity%populate( &
- & -self%densityEnclosedByRadiusScaleFree(self%nfwEnclosedDensity%x(iRadius)), &
- & iRadius &
- & )
- end do
- call self%nfwEnclosedDensity%reverse(self%nfwEnclosedDensityInverse)
- ! Specify that tabulation has been made.
- self%nfwEnclosedDensityTableInitialized=.true.
- end if
- return
- end subroutine nfwEnclosedDensityTabulate
-
- double precision function nfwDensityScaleFree(self,radius,concentration)
- !!{
- Returns the density (in units such that the virial mass and scale length are unity) in an NFW dark matter profile with
- given {\normalfont \ttfamily concentration} at the given {\normalfont \ttfamily radius} (given in units of the scale radius).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration, radius
- !$GLC attributes unused :: self
-
- nfwDensityScaleFree=1.0d0/(log(1.0d0+concentration)-concentration/(1.0d0+concentration))/radius/(1.0d0+radius)**2/4.0d0/Pi
- return
- end function nfwDensityScaleFree
-
- double precision function nfwRadialVelocityDispersionScaleFree(self,radius,concentration)
- !!{
- Returns the radial velocity dispersion (in units of the virial velocity) in an NFW dark matter profile with given
- {\normalfont \ttfamily concentration} at the given {\normalfont \ttfamily radius} (given in units of the scale radius)
- using the result derived by \citeauthor{lokas_properties_2001}~(\citeyear{lokas_properties_2001}; eqn.~14). Note that
- approximate solutions are used at small and large radii.
- !!}
- use :: Dilogarithms , only : Dilogarithm
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration, radius
- double precision , parameter :: minimumRadiusForExactSolution =1.0d-2
- double precision , parameter :: maximumRadiusForExactSolution =1.0d2
- ! Precomputed NFW normalization factor for unit radius.
- double precision , parameter :: nfwNormalizationFactorUnitRadius=-8.5d0+Pi**2-6.0d0*log(2.0d0)+6.0d0*log(2.0d0)**2
- double precision :: radialVelocityDispersionSquare
- double precision :: logRadius, onePlusRadius, logOnePlusRadius
-
- if (radius == 1.0d0) then
- radialVelocityDispersionSquare=nfwNormalizationFactorUnitRadius
- else if (radius >= maximumRadiusForExactSolution) then
- logRadius = log(radius)
- radialVelocityDispersionSquare=+(- 3.0d0+ 4.0d0*logRadius)/( 16.0d0*radius ) &
- & +( 69.0d0+ 20.0d0*logRadius)/( 200.0d0*radius**2) &
- & +(- 97.0d0- 60.0d0*logRadius)/( 1200.0d0*radius**3) &
- & +( 71.0d0+ 105.0d0*logRadius)/( 3675.0d0*radius**4) &
- & +(- 1.0d0- 56.0d0*logradius)/( 3136.0d0*radius**5) &
- & +(-1271.0d0+2520.0d0*logRadius)/(211680.0d0*radius**6)
- else if (radius >= minimumRadiusForExactSolution) then
- onePlusRadius = 1.0d0+radius
- logRadius = log( radius)
- logOnePlusRadius = log(onePlusRadius)
- radialVelocityDispersionSquare=+0.5d0 &
- & * radius &
- & *onePlusRadius**2 &
- & *( &
- & +Pi**2 &
- & -logRadius &
- & -1.0d0/ radius &
- & -1.0d0/onePlusRadius**2 &
- & -6.0d0/onePlusRadius &
- & +( &
- & +1.0d0+ 1.0d0/radius**2 &
- & - 4.0d0/radius &
- & -2.0d0/onePlusRadius &
- & ) &
- & *logOnePlusRadius &
- & +3.0d0 &
- & *logOnePlusRadius**2 &
- & +6.0d0 &
- & *Dilogarithm(-radius) &
- & )
- else if (radius > 0.0d0) then
- logRadius = log(radius)
- radialVelocityDispersionSquare=+ 1.0d0/ 4.0d0*(-23.0d0 + 2.0d0*Pi**2- 2.0d0*logRadius)*radius &
- & + (-59.0d0/6.0d0 + Pi**2- logRadius)*radius**2 &
- & + 1.0d0/ 24.0d0*(-101.0d0 +12.0d0*Pi**2-12.0d0*logRadius)*radius**3 &
- & +11.0d0/ 60.0d0 *radius**4 &
- & -13.0d0/ 240.0d0 *radius**5 &
- & +37.0d0/1400.0d0 *radius**6
- else
- radialVelocityDispersionSquare=0.0d0
- end if
- nfwRadialVelocityDispersionScaleFree=sqrt(radialVelocityDispersionSquare)
- ! Compute the normalization factor.
- call nfwMassNormalizationFactor(self,concentration)
- ! Evaluate the scale-free radial velocity dispersion.
- nfwRadialVelocityDispersionScaleFree=+nfwRadialVelocityDispersionScaleFree &
- & *sqrt( &
- & +self%nfwNormalizationFactorPrevious &
- & *concentration &
- & )
- return
- end function nfwRadialVelocityDispersionScaleFree
-
- double precision function nfwRadialVelocityDispersionScaleFreeSeriesExpansion(self,radius,concentration)
- !!{
- Returns the radial velocity dispersion (in units of the virial velocity) in an NFW dark matter profile with given
- {\normalfont \ttfamily concentration} at the given {\normalfont \ttfamily radius} (given in units of the scale radius)
- using the result derived by \citeauthor{lokas_properties_2001}~(\citeyear{lokas_properties_2001}; eqn.~14). The
- analytic solution is expanded around 0, 1/2, 1, 2, and infinity. The relative error of the approximate series is less
- than $10^{-5}$.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMONFW) , intent(inout) :: self
- double precision , intent(in ) :: concentration , radius
- double precision :: radialVelocityDispersionSquare
- integer , parameter :: maximumExpansionOrder =7
- double precision , dimension(maximumExpansionOrder+1) :: coefficient , radiusPower
- double precision :: logRadius
- integer :: i
-
- if (radius == 0.0d0) then
- radialVelocityDispersionSquare=0.0d0
- else
- if (radius < 0.33d0) then
- ! Expand around 0.
- radiusPower(1)= 1.0d0
- radiusPower(2)= radius
- logRadius = log(radius)
- coefficient(1)= 0.0d0
- coefficient(2)= 1.0d0/ 4.0d0*(-23.0d0 + 2.0d0*Pi**2- 2.0d0*logRadius)
- coefficient(3)= (-59.0d0/6.0d0 + Pi**2- logRadius)
- coefficient(4)= 1.0d0/ 24.0d0*(-101.0d0 +12.0d0*Pi**2-12.0d0*logRadius)
- coefficient(5)= 11.0d0/ 60.0d0
- coefficient(6)=-13.0d0/ 240.0d0
- coefficient(7)= 37.0d0/1400.0d0
- coefficient(8)=-17.0d0/1050.0d0
- else if (radius < 0.68d0) then
- ! Expand around 1/2.
- radiusPower(1)= 1.0d0
- radiusPower(2)= radius-0.5d0
- coefficient(1)= 9.2256912491493508d-2
- coefficient(2)= 1.8995942538987498d-2
- coefficient(3)=-6.1247239215578800d-2
- coefficient(4)= 9.7544538830827322d-2
- coefficient(5)=-1.4457663797045428d-1
- coefficient(6)= 2.1545129876370470d-1
- coefficient(7)=-3.2824371986452579d-1
- coefficient(8)= 5.1242111712986012d-1
- else if (radius < 1.35d0) then
- ! Expand around 1.
- radiusPower(1)= 1.0d0
- radiusPower(2)= radius-1.0d0
- coefficient(1)= 9.3439401238895310d-2
- coefficient(2)=-6.2683780821546887d-3
- coefficient(3)=-8.2007484513808621d-3
- coefficient(4)= 1.0119593363084506d-2
- coefficient(5)=-9.2481085050239271d-3
- coefficient(6)= 7.8754354146912774d-3
- coefficient(7)=-6.5855139302751235d-3
- coefficient(8)= 5.5035102596088475d-3
- else if (radius < 2.66d0) then
- ! Expand around 2.
- radiusPower(1)= 1.0d0
- radiusPower(2)= radius-2.0d0
- coefficient(1)= 8.4126434467263518d-2
- coefficient(2)=-9.8388986218866523d-3
- coefficient(3)= 6.1288152708705594d-4
- coefficient(4)= 4.3464937545102683d-4
- coefficient(5)=-3.4479664620159904d-4
- coefficient(6)= 1.8815165134120623d-4
- coefficient(7)=-9.2066324234421410d-5
- coefficient(8)= 4.3068151103206337d-5
- else
- ! Expand around infinity.
- radiusPower(1)= 1.0d0
- radiusPower(2)= 1.0d0/radius
- logRadius = log(radius)
- coefficient(1)= 0.0d0
- coefficient(2)=(- 3.0d0+ 4.0d0*logRadius)/ 16.0d0
- coefficient(3)=( 69.0d0+ 20.0d0*logRadius)/ 200.0d0
- coefficient(4)=(- 97.0d0- 60.0d0*logRadius)/ 1200.0d0
- coefficient(5)=( 71.0d0+ 105.0d0*logRadius)/ 3675.0d0
- coefficient(6)=(- 1.0d0- 56.0d0*logRadius)/ 3136.0d0
- coefficient(7)=(-1271.0d0+2520.0d0*logRadius)/211680.0d0
- coefficient(8)=( 341.0d0- 360.0d0*logRadius)/ 43200.0d0
- end if
- do i=3, maximumExpansionOrder+1
- radiusPower(i)=radiusPower(i-1)*radiusPower(2)
- end do
- radialVelocityDispersionSquare=sum(coefficient*radiusPower)
- end if
- nfwRadialVelocityDispersionScaleFreeSeriesExpansion=sqrt(radialVelocityDispersionSquare)
- ! Compute the normalization factor.
- call nfwMassNormalizationFactor(self,concentration)
- ! Evaluate the scale-free radial velocity dispersion.
- nfwRadialVelocityDispersionScaleFreeSeriesExpansion=+nfwRadialVelocityDispersionScaleFreeSeriesExpansion &
- & *sqrt( &
- & +self%nfwNormalizationFactorPrevious &
- & *concentration &
- & )
- return
- end function nfwRadialVelocityDispersionScaleFreeSeriesExpansion
-
- double precision function nfwProfileEnergy(self,concentration)
- !!{
- Computes the total energy of an NFW profile halo of given {\normalfont \ttfamily concentration} using the methods of
- \citeauthor{cole_hierarchical_2000}~(\citeyear{cole_hierarchical_2000}; their Appendix~A), except for potential energy
- which is computed using the result derived by \citeauthor{mo_formation_1998}~(\citeyear{mo_formation_1998}; eqn.~23).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Integration , only : integrator
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: concentration
- type (integrator ) :: integratorJeans , integratorKinetic
- double precision :: jeansEquationIntegral , kineticEnergy , &
- & kineticEnergyIntegral , potentialEnergy , &
- & radiusMinimum , radiusMaximum , &
- & concentrationParameter
-
- ! Compute the potential energy.
- potentialEnergy=-0.5d0 &
- & *( &
- & +1.0d0 &
- & -1.0d0 &
- & / (1.0d0+concentration)**2 &
- & -2.0d0*log(1.0d0+concentration) &
- & / (1.0d0+concentration) &
- & ) &
- & /( &
- & + concentration &
- & / (1.0d0+concentration) &
- & -log(1.0d0+concentration) &
- & ) **2
- ! Compute the velocity dispersion at the virial radius.
- radiusMinimum = concentration
- radiusMaximum =100.0d0*concentration
- concentrationParameter= concentration
- integratorJeans =integrator (nfwJeansEquationIntegrand,toleranceRelative=1.0d-3)
- jeansEquationIntegral =integratorJeans%integrate(radiusMinimum ,radiusMaximum )
- ! Compute the kinetic energy.
- radiusMinimum =0.0d0
- radiusMaximum =concentration
- concentrationParameter=concentration
- integratorKinetic =integrator (nfwKineticEnergyIntegrand,toleranceRelative=1.0d-3)
- kineticEnergyIntegral =integratorKinetic%integrate(radiusMinimum ,radiusMaximum )
- kineticEnergy =2.0d0*Pi*(jeansEquationIntegral*concentration**3+kineticEnergyIntegral)
- ! Compute the total energy.
- nfwProfileEnergy=(potentialEnergy+kineticEnergy)*concentration
- return
-
- contains
-
- double precision function nfwKineticEnergyIntegrand(radius)
- !!{
- Integrand for NFW profile kinetic energy.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- nfwKineticEnergyIntegrand=self%EnclosedMassScaleFree(radius,concentrationParameter) &
- & *self%densityScaleFree (radius,concentrationParameter) &
- & *radius
- return
- end function nfwKineticEnergyIntegrand
-
- double precision function nfwJeansEquationIntegrand(radius)
- !!{
- Integrand for NFW profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- nfwJeansEquationIntegrand=self%enclosedMassScaleFree(radius,concentrationParameter) &
- & *self%densityScaleFree (radius,concentrationParameter) &
- & /radius**2
- return
- end function nfwJeansEquationIntegrand
-
- end function nfwProfileEnergy
-
- double precision function nfwKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the NFW density profile at the specified {\normalfont \ttfamily waveNumber} (given in Mpc$^{-1}$), using the
- expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- use :: Exponential_Integrals, only : Cosine_Integral , Sine_Integral
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
- class (nodeComponentDarkMatterProfile) , pointer :: darkMatterProfile
- double precision :: concentration , radiusScale, &
- & waveNumberScaleFree
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
-
- ! Compute the concentration parameter.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the dimensionless wavenumber.
- waveNumberScaleFree=waveNumber*radiusScale
-
- ! Compute the Fourier transformed profile.
- nfwKSpace= ( &
- & +sin( waveNumberScaleFree)*(Sine_Integral ((1.0d0+concentration)*waveNumberScaleFree)-Sine_Integral (waveNumberScaleFree)) &
- & -sin(concentration*waveNumberScaleFree)/(1.0d0+concentration)/waveNumberScaleFree &
- & +cos( waveNumberScaleFree)*(Cosine_Integral((1.0d0+concentration)*waveNumberScaleFree)-Cosine_Integral(waveNumberScaleFree)) &
- & ) &
- & /(log(1.0d0+concentration)-concentration/(1.0d0+concentration))
- return
- end function nfwKSpace
-
- double precision function nfwFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the NFW density profile at the specified {\normalfont \ttfamily time} (given in Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration , freefallTimeScaleFree, &
- & radiusScale , timeScale , &
- & velocityScale
-
- ! For non-positive freefall times, return a zero freefall radius immediately.
- if (time <= 0.0d0) then
- nfwFreefallRadius=0.0d0
- return
- end if
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
-
- ! Get the concentration.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the virial velocity.
- velocityScale=self%darkMatterHaloScale_%velocityVirial(node)
-
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & /sqrt(concentration/(log(1.0d0+concentration)-concentration/(1.0d0+concentration)))
-
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
-
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree)
-
- ! Interpolate to get the freefall radius.
- nfwFreefallRadius=self%nfwFreefallInverse%interpolate(freefallTimeScaleFree)*radiusScale
- return
- end function nfwFreefallRadius
-
- double precision function nfwFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the NFW density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, treeNode
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMONFW ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
- double precision :: concentration , freefallTimeScaleFree, &
- & radiusScale , timeScale , &
- & velocityScale
-
- ! For non-positive freefall times, return the limiting value for small radii.
- if (time <= 0.0d0) then
- nfwFreefallRadiusIncreaseRate=0.0d0
- return
- end if
-
- ! Get components.
- darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
-
- ! Get the scale radius.
- radiusScale=darkMatterProfile%scale()
-
- ! Get the concentration.
- concentration=self%darkMatterHaloScale_%radiusVirial(node)/radiusScale
-
- ! Get the virial velocity.
- velocityScale=self%darkMatterHaloScale_%velocityVirial(node)
-
- ! Compute time scale.
- timeScale=+Mpc_per_km_per_s_To_Gyr &
- & *radiusScale &
- & /velocityScale &
- & /sqrt(concentration/(log(1.0d0+concentration)-concentration/(1.0d0+concentration)))
-
- ! Compute dimensionless time.
- freefallTimeScaleFree=time/timeScale
-
- ! Ensure table is sufficiently extensive.
- call self%freefallTabulate(freefallTimeScaleFree)
-
- ! Interpolate to get the freefall radius growth rate.
- nfwFreefallRadiusIncreaseRate=self%nfwFreefallInverse%interpolateGradient(freefallTimeScaleFree)*radiusScale/timeScale
- return
- end function nfwFreefallRadiusIncreaseRate
-
- subroutine nfwFreefallTabulate(self,freefallTimeScaleFree)
- !!{
- Tabulates the freefall time vs. freefall radius for NFW halos.
- !!}
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: freefallTimeScaleFree
- logical :: retabulate
- integer :: iRadius
-
- retabulate=.not.self%nfwFreefallTableInitialized
- ! If the table has not yet been made, compute and store the freefall corresponding to the minimum and maximum
- ! radii that will be tabulated by default.
- if (retabulate) then
- self%freefallTimeMinimum=self%freefallTimeScaleFree(self%freefallRadiusMinimum)
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusMaximum)
- end if
- do while (freefallTimeScaleFree < self%freefallTimeMinimum)
- self%freefallRadiusMinimum=0.5d0*self%freefallRadiusMinimum
- self%freefallTimeMinimum=self%freefallTimeScaleFree(self%freefallRadiusMinimum)
- retabulate=.true.
- end do
- do while (freefallTimeScaleFree > self%freefallTimeMaximum)
- self%freefallRadiusMaximum=2.0d0*self%freefallRadiusMaximum
- self%freefallTimeMaximum=self%freefallTimeScaleFree(self%freefallRadiusMaximum)
- retabulate=.true.
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%nfwFreefallTableNumberPoints=int(log10(self%freefallRadiusMaximum/self%freefallRadiusMinimum)*dble(freefallTablePointsPerDecade))+1
- ! Create the table.
- call self%nfwFreefall%destroy( )
- call self%nfwFreefall%create (self%freefallRadiusMinimum,self%freefallRadiusMaximum,self%nfwFreefallTableNumberPoints)
- ! Loop over radii and populate tables.
- do iRadius=1,self%nfwFreefallTableNumberPoints
- call self%nfwFreefall%populate( &
- & self%freefallTimeScaleFree(self%nfwFreefall%x(iRadius)), &
- & iRadius &
- & )
- end do
- call self%nfwFreefall%reverse(self%nfwFreefallInverse)
- ! Specify that tabulation has been made.
- self%nfwFreefallTableInitialized=.true.
- end if
- return
- end subroutine nfwFreefallTabulate
-
- double precision function nfwFreefallTimeScaleFree(self,radius)
- !!{
- Compute the freefall time in a scale-free NFW halo.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- class (darkMatterProfileDMONFW), intent(inout) :: self
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusSmall=4.0d-6
- type (integrator ) :: integrator_
- double precision :: radiusEnd , radiusStart
- !$GLC attributes unused :: self
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionNFW ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMONFW ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
+ !![
+
+ !!]
- if (radius > radiusSmall) then
- ! Use the full solution.
- radiusStart =radius
- radiusEnd =0.0d0
- integrator_ =integrator (nfwFreefallTimeScaleFreeIntegrand,toleranceRelative=1.0d-3)
- nfwFreefallTimeScaleFree=integrator_%integrate(radiusEnd ,radiusStart )
- else
- ! Use an approximation here, found by taking series expansions of the logarithms in the integrand and keeping only the
- ! first order terms.
- nfwFreefallTimeScaleFree=2.0d0*sqrt(radius)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionNFW :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionNFW)
+ basic => node%basic ()
+ darkMatterProfile => node%darkMatterProfile()
+ !![
+
+
+ massDistributionNFW( &
+ & mass =basic %mass ( ), &
+ & virialRadius =self %darkMatterHaloScale_%radiusVirial (node), &
+ & scaleLength =darkMatterProfile%scale ( ), &
+ & componentType= componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionNFW( &
+ & useSeriesApproximation=self%velocityDispersionUseSeriesExpansion &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
-
- contains
-
- double precision function nfwFreefallTimeScaleFreeIntegrand(radius)
- !!{
- Integrand function used for finding the free-fall time in NFW halos.
- !!}
- implicit none
- double precision, intent(in ) :: radius
- double precision, parameter :: radiusSmall =1.0d-6
- double precision, parameter :: radiusSmallFraction=1.0d-3
- double precision :: x
-
- if (radius < radiusSmall) then
- ! Use a series approximation for small radii.
- nfwFreefallTimeScaleFreeIntegrand=log(1.0d0+radiusStart)/radiusStart-1.0d0+radius*(0.5d0-radius/3.0d0)
- else if (radius > radiusStart*(1.0d0-radiusSmallFraction)) then
- ! Use a series approximation for radii close to the initial radius.
- x=1.0d0-radius/radiusStart
- nfwFreefallTimeScaleFreeIntegrand=+(1.0d0/(1.0d0+radiusStart)-log(1.0d0+radiusStart)/radiusStart)*x &
- & +( &
- & +0.5d0*radiusStart/(1.0d0+radiusStart)**2 &
- & +(radiusStart-(1.0d0+radiusStart)*log(1.0d0+radiusStart)) &
- & /radiusStart &
- & /(1.0d0+radiusStart) &
- & )*x**2
- else
- ! Use full expression for larger radii.
- nfwFreefallTimeScaleFreeIntegrand=log(1.0d0+radiusStart)/radiusStart-log(1.0d0+radius)/radius
- end if
- nfwFreefallTimeScaleFreeIntegrand=1.0d0/sqrt(-2.0d0*nfwFreefallTimeScaleFreeIntegrand)
- return
- end function nfwFreefallTimeScaleFreeIntegrand
-
- end function nfwFreefallTimeScaleFree
+ end function nfwGet
diff --git a/source/dark_matter_profiles_DMO.Penarrubia2010.F90 b/source/dark_matter_profiles_DMO.Penarrubia2010.F90
index 6ab3c16b1f..c9e1567b5d 100644
--- a/source/dark_matter_profiles_DMO.Penarrubia2010.F90
+++ b/source/dark_matter_profiles_DMO.Penarrubia2010.F90
@@ -24,13 +24,14 @@
!![
- A dark matter profile DMO class which implements the \cite{penarrubia_impact_2010} density profile.
+ A dark matter profile DMO class which builds \refClass{massDistributionZhao1996} to implement the
+ \cite{penarrubia_impact_2010} density profile.
-
+
-
+
!!]
@@ -39,26 +40,16 @@
A dark matter halo profile class implementing \cite{penarrubia_impact_2010} dark matter halos.
!!}
private
- double precision :: betaStripped , muRadius , &
- & etaRadius , muVelocity , &
- & etaVelocity , ratioRadiusMaximumRadiusScaleStripped , &
- & ratioRadiusMaximumRadiusScaleUnstripped , ratioVelocityMaximumVelocityScaleUnstripped , &
- & ratioVelocityMaximumVelocityScaleStripped , scaleRadiusPrevious , &
- & normalizationPrevious
- type (darkMatterProfileDMOZhao1996), pointer :: darkMatterProfileStripped => null(), darkMatterProfileUnstripped => null()
- integer (kind_int8 ) :: uniqueIDPrevious
+ double precision :: betaStripped , muRadius , &
+ & etaRadius , muVelocity , &
+ & etaVelocity , ratioRadiusMaximumRadiusScaleStripped , &
+ & ratioRadiusMaximumRadiusScaleUnstripped
+ type (massDistributionZhao1996), pointer :: massDistributionStripped => null(), massDistributionUnstripped => null()
contains
- !![
-
-
-
- !!]
- final :: penarrubia2010Destructor
- procedure :: autoHook => penarrubia2010AutoHook
- procedure :: calculationReset => penarrubia2010CalculationReset
- procedure :: exponents => penarrubia2010Exponents
- procedure :: scaleRadius => penarrubia2010ScaleRadius
- procedure :: normalization => penarrubia2010Normalization
+ final :: penarrubia2010Destructor
+ procedure :: exponents => penarrubia2010Exponents
+ procedure :: scaleRadius => penarrubia2010ScaleRadius
+ procedure :: normalization => penarrubia2010Normalization
end type darkMatterProfileDMOPenarrubia2010
interface darkMatterProfileDMOPenarrubia2010
@@ -130,6 +121,11 @@ function penarrubia2010ConstructorParameters(parameters) result(self)
The parameter $\eta$ of the \cite{penarrubia_impact_2010} tidal track for $V_\mathrm{max}$.
+
+ etaVelocity
+
+ The parameter $\eta$ of the \cite{penarrubia_impact_2010} tidal track for $V_\mathrm{max}$.
+
!!]
self=darkMatterProfileDMOPenarrubia2010(alpha,beta,gamma,betaStripped,muRadius,etaRadius,muVelocity,etaVelocity,darkMatterHaloScale_)
@@ -144,8 +140,7 @@ function penarrubia2010ConstructorInternal(alpha,beta,gamma,betaStripped,muRadiu
!!{
Generic constructor for the {\normalfont \ttfamily penarrubia2010} dark matter halo profile class.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile
- use :: Kind_Numbers , only : kind_int8
+ use :: Mass_Distributions, only : massDistributionZhao1996
implicit none
type (darkMatterProfileDMOPenarrubia2010) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
@@ -153,86 +148,35 @@ function penarrubia2010ConstructorInternal(alpha,beta,gamma,betaStripped,muRadiu
& gamma , betaStripped, &
& muRadius , etaRadius , &
& muVelocity , etaVelocity
- type (treeNode ), pointer :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
!![
!!]
- ! Compute the mapping between scale radius and radius of peak velocity in the scale-free stripped and unstripped profiles.
- node => treeNode ( )
- basic => node %basic (autoCreate=.true.)
- darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
- call basic %timeSet (1.0d0)
- call basic %timeLastIsolatedSet(1.0d0)
- call basic %massSet (1.0d0)
- call darkMatterProfile%scaleSet (1.0d0)
- allocate(self%darkMatterProfileStripped )
- allocate(self%darkMatterProfileUnstripped)
+ allocate(self%massDistributionStripped )
+ allocate(self%massDistributionUnstripped)
!![
-
-
+
+
!!]
- self%ratioRadiusMaximumRadiusScaleStripped =+self%darkMatterProfileStripped %radiusCircularVelocityMaximum(node )
- self%ratioRadiusMaximumRadiusScaleUnstripped =+self%darkMatterProfileUnstripped%radiusCircularVelocityMaximum(node )
- self%ratioVelocityMaximumVelocityScaleStripped =+self%darkMatterProfileStripped % circularVelocityMaximum(node ) &
- & /self%darkMatterProfileStripped % circularVelocity (node,radius=1.0d0)
- self%ratioVelocityMaximumVelocityScaleUnstripped=+self%darkMatterProfileUnstripped% circularVelocityMaximum(node ) &
- & /self%darkMatterProfileUnstripped% circularVelocity (node,radius=1.0d0)
- call node%destroy()
- deallocate(node)
- ! Initialize state.
- self%specialCase =specialCaseGeneral
- self%scaleRadiusPrevious =-1.0d0
- self%normalizationPrevious=-1.0d0
- self%uniqueIDPrevious =-1_kind_int8
+ self%ratioRadiusMaximumRadiusScaleStripped =+self%massDistributionStripped %radiusRotationCurveMaximum()
+ self%ratioRadiusMaximumRadiusScaleUnstripped=+self%massDistributionUnstripped%radiusRotationCurveMaximum()
return
end function penarrubia2010ConstructorInternal
- subroutine penarrubia2010AutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOPenarrubia2010), intent(inout) :: self
-
- call calculationResetEvent%attach(self,penarrubia2010CalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOPenarrubia2010')
- return
- end subroutine penarrubia2010AutoHook
-
subroutine penarrubia2010Destructor(self)
!!{
Destructor for the {\normalfont \ttfamily penarrubia2010} dark matter halo profile class.
!!}
implicit none
type(darkMatterProfileDMOPenarrubia2010), intent(inout) :: self
-
+
!![
-
-
+
+
!!]
return
end subroutine penarrubia2010Destructor
-
- subroutine penarrubia2010CalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOPenarrubia2010), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%scaleRadiusPrevious =-1.0d0
- self%normalizationPrevious=-1.0d0
- self%uniqueIDPrevious =uniqueID
- return
- end subroutine penarrubia2010CalculationReset
-
+
subroutine penarrubia2010Exponents(self,node,alpha,beta,gamma)
!!{
Compute the exponents of the {\normalfont \ttfamily penarrubia2010} dark matter halo profile.
@@ -260,7 +204,7 @@ subroutine penarrubia2010Exponents(self,node,alpha,beta,gamma)
return
end subroutine penarrubia2010Exponents
- double precision function penarrubia2010ScaleRadius(self,node)
+ double precision function penarrubia2010ScaleRadius(self,node) result(radiusScale)
!!{
Compute the scale radius of the {\normalfont \ttfamily penarrubia2010} dark matter halo profile.
!!}
@@ -273,79 +217,71 @@ double precision function penarrubia2010ScaleRadius(self,node)
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
double precision :: fractionMassBound , fractionRadiusMaximum, &
& ratioRadiusMaximumRadiusScale
-
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%scaleRadiusPrevious < 0.0d0) then
- basic => node %basic ()
- satellite => node %satellite ()
- darkMatterProfile => node %darkMatterProfile()
- fractionMassBound = +satellite%boundMass () &
- & /basic %mass ()
- fractionRadiusMaximum = +2.0d0 **self%muRadius &
- & * fractionMassBound**self%etaRadius &
- & /( &
- & +1.0d0 &
- & +fractionMassBound &
- & ) **self%muRadius
- if (fractionMassBound >= fractionMassTransition) then
- ratioRadiusMaximumRadiusScale=self%ratioRadiusMaximumRadiusScaleUnstripped
- else
- ratioRadiusMaximumRadiusScale=self%ratioRadiusMaximumRadiusScaleStripped
- end if
- self%scaleRadiusPrevious=+ fractionRadiusMaximum &
- & *self %ratioRadiusMaximumRadiusScaleUnstripped &
- & / ratioRadiusMaximumRadiusScale &
- & *darkMatterProfile%scale ()
+
+ basic => node %basic ()
+ satellite => node %satellite ()
+ darkMatterProfile => node %darkMatterProfile()
+ fractionMassBound = +satellite%boundMass () &
+ & /basic %mass ()
+ fractionRadiusMaximum = +2.0d0 **self%muRadius &
+ & * fractionMassBound**self%etaRadius &
+ & /( &
+ & +1.0d0 &
+ & +fractionMassBound &
+ & ) **self%muRadius
+ if (fractionMassBound >= fractionMassTransition) then
+ ratioRadiusMaximumRadiusScale=self%ratioRadiusMaximumRadiusScaleUnstripped
+ else
+ ratioRadiusMaximumRadiusScale=self%ratioRadiusMaximumRadiusScaleStripped
end if
- penarrubia2010ScaleRadius=self%scaleRadiusPrevious
+ radiusScale=+ fractionRadiusMaximum &
+ & *self %ratioRadiusMaximumRadiusScaleUnstripped &
+ & / ratioRadiusMaximumRadiusScale &
+ & *darkMatterProfile%scale ()
return
end function penarrubia2010ScaleRadius
-
+
double precision function penarrubia2010Normalization(self,node)
!!{
- Compute the normalization of the {\normalfont \ttfamily penarrubia2010} dark matter halo profile.
+ Compute the mass normalization of the {\normalfont \ttfamily penarrubia2010} dark matter halo profile.
!!}
use :: Galacticus_Nodes, only : nodeComponentSatellite, nodeComponentBasic, nodeComponentDarkMatterProfile
implicit none
- class (darkMatterProfileDMOPenarrubia2010), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentSatellite ), pointer :: satellite
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: fractionMassBound , fractionVelocityMaximum, &
- & ratioVelocityMaximumVelocityScale, massScale , &
- & massScaleOriginal
+ class(darkMatterProfileDMOPenarrubia2010 ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ class(nodeComponentBasic ), pointer :: basic
+ class (nodeComponentSatellite ), pointer :: satellite
+ class (nodeComponentDarkMatterProfile), pointer :: darkMatterProfile
+ double precision :: fractionMassBound , fractionVelocityMaximum, &
+ & radiusScale , radiusVirial , &
+ & velocityRotationFactor
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%normalizationPrevious < 0.0d0) then
- basic => node %basic ()
- satellite => node %satellite ()
- darkMatterProfile => node %darkMatterProfile()
- fractionMassBound = +satellite%boundMass () &
- & /basic %mass ()
- fractionVelocityMaximum = +2.0d0 **self%muVelocity &
- & * fractionMassBound**self%etaVelocity &
- & /( &
- & +1.0d0 &
- & +fractionMassBound &
- & ) **self%muVelocity
- if (fractionMassBound >= fractionMassTransition) then
- ratioVelocityMaximumVelocityScale=self%ratioVelocityMaximumVelocityScaleUnstripped
- else
- ratioVelocityMaximumVelocityScale=self%ratioVelocityMaximumVelocityScaleStripped
- end if
- massScaleOriginal=self%darkMatterProfileUnstripped%enclosedMass(node,darkMatterProfile%scale())
- massScale=+massScaleOriginal &
- & *self %scaleRadius (node ) &
- & /darkmatterProfile%scale ( ) &
- & *self %ratioVelocityMaximumVelocityScaleUnstripped **2 &
- & / ratioVelocityMaximumVelocityScale **2 &
- & * fractionVelocityMaximum **2
- self%normalizationPrevious=+ massScale &
- & /self%massUnnormalized(node,radiusScaleFree=1.0d0) &
- & /self%scaleRadius (node )**3
+ basic => node %basic ()
+ satellite => node %satellite ()
+ darkMatterProfile => node %darkMatterProfile()
+ fractionMassBound = +satellite%boundMass () &
+ & /basic %mass ()
+ fractionVelocityMaximum = +2.0d0 **self%muVelocity &
+ & * fractionMassBound**self%etaVelocity &
+ & /( &
+ & +1.0d0 &
+ & +fractionMassBound &
+ & ) **self%muVelocity
+ radiusScale =darkMatterProfile %scale ( )
+ radiusVirial =self %darkMatterHaloScale_%radiusVirial(node)
+ if (fractionMassBound >= fractionMassTransition) then
+ velocityRotationFactor=+self%massDistributionUnstripped% rotationCurve (radius=radiusVirial/self%scaleRadius(node)) &
+ & /self%massDistributionUnstripped%velocityRotationCurveMaximum( )
+ else
+ velocityRotationFactor=+self%massDistributionStripped % rotationCurve (radius=radiusVirial/self%scaleRadius(node)) &
+ & /self%massDistributionStripped %velocityRotationCurveMaximum( )
end if
- penarrubia2010Normalization=self%normalizationPrevious
+ penarrubia2010Normalization=+basic%mass() &
+ & *fractionVelocityMaximum**2 &
+ & *velocityRotationFactor **2 &
+ & /( &
+ & +self%massDistributionUnstripped%rotationCurve (radius=radiusVirial/radiusScale) &
+ & /self%massDistributionUnstripped%velocityRotationCurveMaximum( ) &
+ & )**2
return
end function penarrubia2010Normalization
-
diff --git a/source/dark_matter_profiles_DMO.SIDM.F90 b/source/dark_matter_profiles_DMO.SIDM.F90
deleted file mode 100644
index 14ee425998..0000000000
--- a/source/dark_matter_profiles_DMO.SIDM.F90
+++ /dev/null
@@ -1,145 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An abstract dark matter halo profile class for SIDM profiles.
- !!}
-
- use :: Dark_Matter_Particles, only : darkMatterParticleClass
-
- !![
-
-
- Abstract dark matter halo profile for self-interacting dark matter particles. Provides a method to compute the
- characteristic radius for interactions, $r_1$, defined as (e.g. Jiang et al. 2020):
- \begin{equation}
- \frac{4}{\sqrt{\pi}} \rho_\mathrm{dm}(r_1) v_\mathrm{rms}(r_1) \frac{\sigma}{m} = \frac{1}{t_\mathrm{age}},
- \end{equation}
- where the left-hand side is the scattering rate per particle, with $\rho_\mathrm{dm}(r)$ being the dark matter density
- profile, $v_\mathrm{rms}$ the average relative velocity between DM particles (which is approximated by the 1D velocity
- dispersion), and $\sigma/m$ is the self-interaction cross-section per unit mass.
-
-
- !!]
- type, abstract, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOSIDM
- !!{
- An abstract dark matter halo profile class for SIDM profiles.
- !!}
- private
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (darkMatterParticleClass ), pointer :: darkMatterParticle_ => null()
- integer (kind=kind_int8 ) :: uniqueIDPreviousSIDM
- double precision :: radiusInteractivePrevious
- contains
- !![
-
-
-
- !!]
- procedure :: radiusInteraction => sidmRadiusInteraction
- end type darkMatterProfileDMOSIDM
-
- ! Submodule-scope variables used in root finding.
- class (darkMatterProfileDMOSIDM), pointer :: self_
- type (treeNode ), pointer :: node_
- double precision :: timeAge_, crossSection_
- !$omp threadprivate(self_,node_,timeAge_,crossSection_)
-
-contains
-
- double precision function sidmRadiusInteraction(self,node,timeAge)
- !!{
- Returns the characteristic interaction radius (in Mpc) of the self-interacting dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeComponentBasic
- use :: Numerical_Constants_Prefixes , only : centi , kilo
- use :: Numerical_Constants_Astronomical, only : megaParsec , massSolar
- use :: Root_Finder , only : rootFinder , rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- implicit none
- class (darkMatterProfileDMOSIDM), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ), optional :: timeAge
- class (nodeComponentBasic ) , pointer :: basic
- type (rootFinder ), save :: finder
- logical , save :: finderInitialized=.false.
- double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-3
- !$omp threadprivate(finder,finderInitialized)
-
- if (node%uniqueID() /= self%uniqueIDPreviousSIDM .or. self%radiusInteractivePrevious < 0.0d0) then
- self_ => self
- node_ => node
- if (present(timeAge)) then
- timeAge_ = timeAge
- else
- basic => node %basic ()
- timeAge_ = basic%time ()
- end if
- select type (darkMatterParticle_ => self%darkMatterParticle_)
- class is (darkMatterParticleSelfInteractingDarkMatter)
- crossSection_=+darkMatterParticle_%crossSectionSelfInteraction() &
- & *centi **2 &
- & /megaParsec**2 &
- & *kilo &
- & *massSolar
- class default
- call Error_Report('expected self-interacting dark matter particle'//{introspection:location})
- end select
- if (.not.finderInitialized) then
- finder=rootFinder( &
- & rootFunction =sidmRadiusInteractionRoot, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
- call finder%rangeExpand( &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- finderInitialized=.true.
- end if
- self%radiusInteractivePrevious=finder%find (rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- self%uniqueIDPreviousSIDM =node %uniqueID( )
- end if
- sidmRadiusInteraction=self%radiusInteractivePrevious
- return
- end function sidmRadiusInteraction
-
- double precision function sidmRadiusInteractionRoot(radius)
- !!{
- Root function used in seeking the characteristic interaction radius in self-interacting dark matter profiles.
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- double precision, intent(in ) :: radius
-
- sidmRadiusInteractionRoot=+4.0d0 &
- & /sqrt(Pi) &
- & /Mpc_per_km_per_s_To_Gyr &
- & *self_%darkMatterProfileDMO_%density (node_,radius) &
- & *self_%darkMatterProfileDMO_%radialVelocityDispersion(node_,radius) &
- & *crossSection_ &
- & -1.0d0 &
- & /timeAge_
- return
- end function sidmRadiusInteractionRoot
-
diff --git a/source/dark_matter_profiles_DMO.SIDM.coreNFW.F90 b/source/dark_matter_profiles_DMO.SIDM.coreNFW.F90
index 07f82dfd8a..beb70edc54 100644
--- a/source/dark_matter_profiles_DMO.SIDM.coreNFW.F90
+++ b/source/dark_matter_profiles_DMO.SIDM.coreNFW.F90
@@ -18,17 +18,21 @@
!! along with Galacticus. If not, see .
!!{
- An implementation of a cored-NFW dark matter halo profile to approximate the effects of SIDM based on the model of Jiang et al. (2022).
+ An implementation of a cored-NFW dark matter halo profile to approximate the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}.
!!}
- use :: Dark_Matter_Particles, only : darkMatterParticleClass
-
+ use :: Dark_Matter_Particles , only : darkMatterParticleClass
+ use :: Dark_Matter_Halo_Scales, only : darkmatterHaloScaleClass
+
!![
- Cored-NFW dark matter halo profile to approximate the effects of SIDM based on the model of Jiang et al. (2022).
+
+ Cored-NFW dark matter halo profiles to approximate the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}
+ are built via \refClass{} objects.
+
!!]
- type, extends(darkMatterProfileDMOSIDM) :: darkMatterProfileDMOSIDMCoreNFW
+ type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOSIDMCoreNFW
!!{
A dark matter halo profile class implementing a cored-NFW dark matter halo profile to approximate the effects of SIDM based
on the model of Jiang et al. (2022). The profile is defined by the enclosed mass, with (Jiang et al. 2022):
@@ -39,35 +43,13 @@
$\alpha = ${\normalfont \ttfamily [factorRadiusCore]}.
!!}
private
- double precision :: factorRadiusCore
+ class (darkMatterParticleClass ), pointer :: darkMatterParticle_ => null()
+ class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ double precision :: factorRadiusCore
contains
- !![
-
-
-
-
- !!]
- final :: sidmCoreNFWDestructor
- procedure :: autoHook => sidmCoreNFWAutoHook
- procedure :: calculationReset => sidmCoreNFWCalculationReset
- procedure :: radiusCore => sidmCoreNFWRadiusCore
- procedure :: density => sidmCoreNFWDensity
- procedure :: densityLogSlope => sidmCoreNFWDensityLogSlope
- procedure :: radiusEnclosingDensity => sidmCoreNFWRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => sidmCoreNFWRadiusEnclosingMass
- procedure :: radialMoment => sidmCoreNFWRadialMoment
- procedure :: enclosedMass => sidmCoreNFWEnclosedMass
- procedure :: potential => sidmCoreNFWPotential
- procedure :: circularVelocity => sidmCoreNFWCircularVelocity
- procedure :: circularVelocityMaximum => sidmCoreNFWCircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => sidmCoreNFWRadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => sidmCoreNFWRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => sidmCoreNFWRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => sidmCoreNFWRotationNormalization
- procedure :: energy => sidmCoreNFWEnergy
- procedure :: kSpace => sidmCoreNFWKSpace
- procedure :: freefallRadius => sidmCoreNFWFreefallRadius
- procedure :: freefallRadiusIncreaseRate => sidmCoreNFWFreefallRadiusIncreaseRate
+ final :: sidmCoreNFWDestructor
+ procedure :: get => sidmCoreNFWGet
end type darkMatterProfileDMOSIDMCoreNFW
interface darkMatterProfileDMOSIDMCoreNFW
@@ -88,8 +70,8 @@ function sidmCoreNFWConstructorParameters(parameters) result(self)
implicit none
type (darkMatterProfileDMOSIDMCoreNFW) :: self
type (inputParameters ), intent(inout) :: parameters
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterParticleClass ), pointer :: darkMatterParticle_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: factorRadiusCore
!![
@@ -100,14 +82,14 @@ function sidmCoreNFWConstructorParameters(parameters) result(self)
The factor $\alpha$ appearing in the definition of the core radius, $r_\mathrm{c}=\alpha r_1$ where $r_1$ is the radius at which an SIDM particle has had, on average, 1 interaction.
-
+
!!]
self=darkMatterProfileDMOSIDMCoreNFW(factorRadiusCore,darkMatterHaloScale_,darkMatterParticle_)
!![
-
+
!!]
return
end function sidmCoreNFWConstructorParameters
@@ -119,8 +101,8 @@ function sidmCoreNFWConstructorInternal(factorRadiusCore,darkMatterHaloScale_,da
use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
implicit none
type (darkMatterProfileDMOSIDMCoreNFW) :: self
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
double precision , intent(in ) :: factorRadiusCore
!![
@@ -148,403 +130,90 @@ function sidmCoreNFWConstructorInternal(factorRadiusCore,darkMatterHaloScale_,da
!!]
end select
- self%genericLastUniqueID =-1_kind_int8
- self%uniqueIDPreviousSIDM=-1_kind_int8
return
end function sidmCoreNFWConstructorInternal
- subroutine sidmCoreNFWAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
-
- call calculationResetEvent%attach(self,sidmCoreNFWCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOSIDMCoreNFW')
- return
- end subroutine sidmCoreNFWAutoHook
-
subroutine sidmCoreNFWDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily sidmCoreNFW} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
!![
-
+
!!]
- if (calculationResetEvent%isAttached(self,sidmCoreNFWCalculationReset)) call calculationResetEvent%detach(self,sidmCoreNFWCalculationReset)
return
end subroutine sidmCoreNFWDestructor
- subroutine sidmCoreNFWCalculationReset(self,node,uniqueID)
+ function sidmCoreNFWGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Reset the dark matter profile calculation.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Kind_Numbers, only : kind_int8
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalSIDMCoreNFW, kinematicsDistributionCollisionless, massDistributionNFW, nonAnalyticSolversNumerical
implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%genericLastUniqueID =uniqueID
- self%uniqueIDPreviousSIDM =uniqueID
- self%radiusInteractivePrevious =-1.0d0
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine sidmCoreNFWCalculationReset
-
- double precision function sidmCoreNFWRadiusCore(self,node)
- !!{
- Returns the core radius (in Mpc) of the ``coreNFW'' approximation to the self-interacting dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmCoreNFWRadiusCore=+self%factorRadiusCore &
- & *self%radiusInteraction(node)
- return
- end function sidmCoreNFWRadiusCore
-
- double precision function sidmCoreNFWDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusFractionalLarge=10.0d0
- double precision :: radiusFractional , radiusCore
-
- radiusCore =+self%radiusCore(node)
- radiusFractional=+ radius &
- & / radiusCore
- if (radiusFractional < radiusFractionalLarge) then
- ! Use the full solution for sufficiently small radii.
- sidmCoreNFWDensity=+self%darkMatterProfileDMO_%density(node,radius) &
- & *tanh( &
- & +radiusFractional &
- & ) &
- & +self%darkMatterProfileDMO_%enclosedMass(node,radius) &
- & /4.0d0 &
- & /Pi &
- & / radiusFractional**2 &
- & / radiusCore **3 &
- & /cosh( &
- & +radiusFractional &
- & )**2
- else
- ! For large fractional radii avoid floating point overflow by approximating cosh(x) ~ 1/2/exp(-x).
- sidmCoreNFWDensity=+self%darkMatterProfileDMO_%density(node,radius) &
- & *tanh( &
- & +radiusFractional &
- & ) &
- & +self%darkMatterProfileDMO_%enclosedMass(node,radius) &
- & /4.0d0 &
- & /Pi &
- & / radiusFractional**2 &
- & / radiusCore **3 &
- & *4.0d0 &
- & *exp( &
- & -2.0d0 &
- & * radiusFractional &
- & )
- end if
- return
- end function sidmCoreNFWDensity
-
- double precision function sidmCoreNFWDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusCore, massEnclosedNFW , &
- & densityNFW, densityLogSlopeNFW
-
- radiusCore =+self%radiusCore (node )
- massEnclosedNFW =+self%darkMatterProfileDMO_%enclosedMass (node,radius)
- densityNFW =+self%darkMatterProfileDMO_%density (node,radius)
- densityLogSlopeNFW =+self%darkMatterProfileDMO_%densityLogSlope(node,radius)
- sidmCoreNFWDensityLogSlope=+( &
- & -2.0d0 &
- & *massEnclosedNFW &
- & *( &
- & +radiusCore &
- & +radius &
- & *tanh( &
- & +radius &
- & /radiusCore &
- & ) &
- & ) &
- & +radiusCore*radius &
- & *( &
- & +4.0d0 &
- & *Pi &
- & *radius**2 &
- & *densityNFW &
- & +2.0d0 &
- & *Pi &
- & *radius**2 &
- & *( &
- & +2.0d0 &
- & *densityNFW &
- & +( &
- & +radiusCore &
- & *sinh( &
- & +2.0d0 &
- & *radius &
- & /radiusCore &
- & ) &
- & *densityLogSlopeNFW*densityNFW &
- & ) &
- & /radius &
- & ) &
- & ) &
- & ) &
- & /( &
- & +radiusCore &
- & *( &
- & +massEnclosedNFW &
- & +2.0d0 &
- & *Pi &
- & *radiusCore &
- & *radius **2 &
- & *sinh( &
- & +2.0d0 &
- & *radius &
- & /radiusCore &
- & ) &
- & *densityNFW &
- & ) &
- & )
- return
- end function sidmCoreNFWDensityLogSlope
-
- double precision function sidmCoreNFWEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- sidmCoreNFWEnclosedMass=+ self%darkMatterProfileDMO_%enclosedMass(node,radius) &
- & *tanh( &
- & + radius &
- & /self%radiusCore (node ) &
- & )
- return
- end function sidmCoreNFWEnclosedMass
-
- double precision function sidmCoreNFWRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- sidmCoreNFWRadiusEnclosingDensity=self%radiusEnclosingDensityNumerical(node,density)
- return
- end function sidmCoreNFWRadiusEnclosingDensity
-
- double precision function sidmCoreNFWRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- sidmCoreNFWRadiusEnclosingMass=self%radiusEnclosingMassNumerical(node,mass)
- return
- end function sidmCoreNFWRadiusEnclosingMass
-
- double precision function sidmCoreNFWRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- sidmCoreNFWRadialMoment=self%radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- return
- end function sidmCoreNFWRadialMoment
-
- double precision function sidmCoreNFWPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- sidmCoreNFWPotential=self%potentialNumerical(node,radius,status)
- return
- end function sidmCoreNFWPotential
-
- double precision function sidmCoreNFWCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- sidmCoreNFWCircularVelocity=self%circularVelocityNumerical(node,radius)
- return
- end function sidmCoreNFWCircularVelocity
-
- double precision function sidmCoreNFWCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmCoreNFWCircularVelocityMaximum=self%circularVelocityMaximumNumerical(node)
- return
- end function sidmCoreNFWCircularVelocityMaximum
-
- double precision function sidmCoreNFWRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmCoreNFWRadiusCircularVelocityMaximum=self%radiusCircularVelocityMaximumNumerical(node)
- return
- end function sidmCoreNFWRadiusCircularVelocityMaximum
-
- double precision function sidmCoreNFWRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- sidmCoreNFWRadialVelocityDispersion=self%radialVelocityDispersionNumerical(node,radius)
- return
- end function sidmCoreNFWRadialVelocityDispersion
-
- double precision function sidmCoreNFWRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- sidmCoreNFWRadiusFromSpecificAngularMomentum=self%radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- return
- end function sidmCoreNFWRadiusFromSpecificAngularMomentum
-
- double precision function sidmCoreNFWRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmCoreNFWRotationNormalization=self%rotationNormalizationNumerical(node)
- return
- end function sidmCoreNFWRotationNormalization
-
- double precision function sidmCoreNFWEnergy(self,node)
- !!{
- Return the energy of a sidmCoreNFW halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmCoreNFWEnergy=self%energyNumerical(node)
- return
- end function sidmCoreNFWEnergy
-
- double precision function sidmCoreNFWKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the sidmCoreNFW density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- sidmCoreNFWKSpace=self%kSpaceNumerical(node,waveNumber)
- return
- end function sidmCoreNFWKSpace
-
- double precision function sidmCoreNFWFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the sidmCoreNFW density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- sidmCoreNFWFreefallRadius=self%freefallRadiusNumerical(node,time)
- return
- end function sidmCoreNFWFreefallRadius
-
- double precision function sidmCoreNFWFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the sidmCoreNFW density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMCoreNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOSIDMCoreNFW ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ class (nodeComponentBasic ), pointer :: basic
+ !![
+
+ !!]
- sidmCoreNFWFreefallRadiusIncreaseRate=self%freefallRadiusIncreaseRateNumerical(node,time)
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalSIDMCoreNFW :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalSIDMCoreNFW)
+ massDistributionDecorated => self%darkMatterProfileDMO_%get (node,weightBy,weightIndex)
+ basic => node %basic( )
+ select type (massDistributionDecorated)
+ class is (massDistributionNFW)
+ !![
+
+
+ massDistributionSphericalSIDMCoreNFW( &
+ & factorRadiusCore =self %factorRadiusCore , &
+ & timeAge =basic%time (), &
+ & nonAnalyticSolver = nonAnalyticSolversNumerical , &
+ & massDistribution_ = massDistributionDecorated , &
+ & darkMatterParticle_=self %darkMatterParticle_ , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function sidmCoreNFWFreefallRadiusIncreaseRate
+ end function sidmCoreNFWGet
diff --git a/source/dark_matter_profiles_DMO.SIDM.isothermal.F90 b/source/dark_matter_profiles_DMO.SIDM.isothermal.F90
index e003844cee..f29de2c518 100644
--- a/source/dark_matter_profiles_DMO.SIDM.isothermal.F90
+++ b/source/dark_matter_profiles_DMO.SIDM.isothermal.F90
@@ -18,102 +18,29 @@
!! along with Galacticus. If not, see .
!!{
- An implementation of dark matter halo profiles for self-interacting dark matter following the ``isothermal'' model of Jiang et al. (2022).
+ An implementation of dark matter halo profiles for self-interacting dark matter following the ``isothermal'' model of \cite{iang_semi-analytic_2023}.
!!}
- use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Numerical_Interpolation, only : interpolator
- use :: Numerical_ODE_Solvers , only : odeSolver
-
+ use :: Dark_Matter_Particles, only : darkMatterParticleClass
+
!![
- Dark matter halo profiles for self-interacting dark matter following the ``isothermal'' model of Jiang et al. (2022). This
- model assumes that the dark matter within the interaction radius, $r_1$, has thermalized and can therefore be described by a
- constant velocity dispersion, $\sigma_0$. Under this assumption the spherical Jeans equation has a solution of the form:
- \begin{equation}
- \rho(r) = \rho_0 \exp\left[-\frac{\phi(r)}{\sigma_0^2}\right],
- \end{equation}
- where $\rho(r)$ is the density $\rho_0$ is the density at $r=0$, and the gravitational potential satisfies (Jiang et al. 2022):
- \begin{equation}
- \nabla^2 \phi(r) = 4 \pi \mathrm{G} \rho_0 \exp \left( - \frac{\phi(r)}{\sigma_0^2} \right).
- \end{equation}
- This second-order differential equation is solved using the boundary conditions $\phi(r=0)=0$ and
- $\mathrm{d}\phi/\mathrm{d}r(r=0)=0$. The values of $\rho_0$ and $\sigma_0$ are then found by minimizing a function
- \begin{equation}
- \delta^2(\rho_0,\sigma_0) = \left[ \frac{\rho(r_1)}{\rho^\prime(r_1)} - 1 \right]^2 + \left[ \frac{M(r_1)}{M^\prime(r_1)} - 1 \right]^2,
- \end{equation}
- where $M(r)$ is the mass contained within radius $r$, and primes indicate the profile prior to SIDM thermalization.
-
- This can be expressed in a convenient dimensionless form. We define $x=r/r_1$, $y=\rho/\rho_1$, $z=\sigma/\sigma_1$, where
- \begin{equation}
- \sigma_1^2 = \frac{4 \pi}{3} \mathrm{G} \rho_1 r_1^2 \xi,
- \end{equation}
- and we define $\xi$ through the relation:
- \begin{equation}
- M_1 = \xi \frac{4 \pi}{3} \rho_1 r_1^3.
- \end{equation}
- Using these definitions we can define a dimensionless potential, $\Phi(r) = \phi(r) / \sigma_1^2$. The above differential
- equation can then be written as
- \begin{equation}
- \nabla^{\prime 2} \Phi = \frac{3}{\xi} y_0 \exp\left[ - \frac{\Phi}{z_0^2} \right] ,
- \end{equation}
- where $\nabla^{\prime 2}$ indicates the Laplacian with respect to coordinate $x$. Written in this form it is straightforward
- to see that this equation has three parameters, $\xi$, $y_0$, and $z_0$. The value of $\xi$ is determined from the initial
- (pre-thermalization) density profile. We then have two constraints at $x=1$, namely $y=1$ and $m=M/M_1=1$. We can solve for
- the values of $y_0$ and $z_0$ which satisfy these constraints for a given $\xi$. As a result, we can tabulate solutions
- $y_0(\xi)$ and $z_0(\xi)$ which are applicable to any initial density profile and depend only on the effective slope of the
- density profile inside $r_1$, since if $\rho \propto r^\alpha$ then $\xi = 1/(1+\alpha/3)$, such that $\alpha=0$ (the
- largest physically-allowed value of $\alpha$) implies $\xi=1$.
+ Dark matter halo profiles for self-interacting dark matter following the ``isothermal'' model of
+ \cite{iang_semi-analytic_2023} are built via the \refClass{massDistributionSphericalSIDMIsothermal} class.
!!]
- type, extends(darkMatterProfileDMOSIDM) :: darkMatterProfileDMOSIDMIsothermal
+ type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOSIDMIsothermal
!!{
- A dark matter halo profile class implementing profiles for self-interacting dark matter following the ``isothermal'' model of Jiang et al. (2022).
+ A dark matter halo profile class implementing profiles for self-interacting dark matter following the ``isothermal'' model of \cite{iang_semi-analytic_2023}.
!!}
private
- integer (kind=kind_int8) :: uniqueIDPrevious
- double precision :: velocityDispersionCentral , radiusInteraction_ , &
- & densityInteraction , massInteraction , &
- & velocityDispersionInteraction
- type (interpolator ), allocatable :: densityCentralDimensionless , velocityDispersionCentralDimensionless, &
- & interpolatorRadiiDimensionless, interpolatorXi
- double precision :: xiTabulatedMinimum , xiTabulatedMaximum
- double precision , allocatable, dimension( : ,:) :: densityProfileDimensionless , massProfileDimensionless
- double precision , allocatable, dimension( : ) :: radiiDimensionless
- integer (c_size_t ) :: indexXi
- double precision , dimension(0:1 ) :: factorsXi
+ class(darkMatterParticleClass ), pointer :: darkMatterParticle_ => null()
+ class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
contains
- !![
-
-
-
-
-
- !!]
- final :: sidmIsothermalDestructor
- procedure :: autoHook => sidmIsothermalAutoHook
- procedure :: calculationReset => sidmIsothermalCalculationReset
- procedure :: density => sidmIsothermalDensity
- procedure :: densityLogSlope => sidmIsothermalDensityLogSlope
- procedure :: radiusEnclosingDensity => sidmIsothermalRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => sidmIsothermalRadiusEnclosingMass
- procedure :: radialMoment => sidmIsothermalRadialMoment
- procedure :: enclosedMass => sidmIsothermalEnclosedMass
- procedure :: potential => sidmIsothermalPotential
- procedure :: circularVelocity => sidmIsothermalCircularVelocity
- procedure :: circularVelocityMaximum => sidmIsothermalCircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => sidmIsothermalRadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => sidmIsothermalRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => sidmIsothermalRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => sidmIsothermalRotationNormalization
- procedure :: energy => sidmIsothermalEnergy
- procedure :: kSpace => sidmIsothermalKSpace
- procedure :: freefallRadius => sidmIsothermalFreefallRadius
- procedure :: freefallRadiusIncreaseRate => sidmIsothermalFreefallRadiusIncreaseRate
- procedure :: computeSolution => sidmIsothermalComputeSolution
- procedure :: tabulateSolutions => sidmIsothermalTabulateSolutions
+ final :: sidmIsothermalDestructor
+ procedure :: get => sidmIsothermalGet
end type darkMatterProfileDMOSIDMIsothermal
interface darkMatterProfileDMOSIDMIsothermal
@@ -124,15 +51,6 @@
module procedure sidmIsothermalConstructorInternal
end interface darkMatterProfileDMOSIDMIsothermal
- ! Number of properties in ODE.
- integer (c_size_t ), parameter :: propertyCount=2
-
- ! Submodule-scope variables.
- double precision :: xi_ , y0_, &
- & z0_
- type (odeSolver), allocatable :: odeSolver_
- !$omp threadprivate(xi_,y0_,z0_,odeSolver_)
-
contains
function sidmIsothermalConstructorParameters(parameters) result(self)
@@ -143,37 +61,33 @@ function sidmIsothermalConstructorParameters(parameters) result(self)
implicit none
type (darkMatterProfileDMOSIDMIsothermal) :: self
type (inputParameters ), intent(inout) :: parameters
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(darkMatterParticleClass ), pointer :: darkMatterParticle_
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
!![
-
!!]
- self=darkMatterProfileDMOSIDMIsothermal(darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterParticle_)
+ self=darkMatterProfileDMOSIDMIsothermal(darkMatterProfileDMO_,darkMatterParticle_)
!![
-
!!]
return
end function sidmIsothermalConstructorParameters
- function sidmIsothermalConstructorInternal(darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterParticle_) result(self)
+ function sidmIsothermalConstructorInternal(darkMatterProfileDMO_,darkMatterParticle_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily sidmIsothermal} dark matter profile class.
!!}
use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
implicit none
type (darkMatterProfileDMOSIDMIsothermal) :: self
- class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class(darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
!![
-
+
!!]
! Validate the dark matter particle type.
@@ -182,667 +96,89 @@ function sidmIsothermalConstructorInternal(darkMatterProfileDMO_,darkMatterHaloS
! This is as expected.
class default
call Error_Report('SIDM isothermal dark matter profile expects a self-interacting dark matter particle'//{introspection:location})
- end select
- self%xiTabulatedMinimum =+huge(0.0d0)
- self%xiTabulatedMaximum =-huge(0.0d0)
- self%uniqueIDPrevious =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%uniqueIDPreviousSIDM=-1_kind_int8
+ end select
return
end function sidmIsothermalConstructorInternal
- subroutine sidmIsothermalAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
-
- call calculationResetEvent%attach(self,sidmIsothermalCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOSIDMIsothermal')
- return
- end subroutine sidmIsothermalAutoHook
-
subroutine sidmIsothermalDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily sidmIsothermal} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
!![
-
!!]
- if (calculationResetEvent%isAttached(self,sidmIsothermalCalculationReset)) call calculationResetEvent%detach(self,sidmIsothermalCalculationReset)
return
end subroutine sidmIsothermalDestructor
- subroutine sidmIsothermalCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%uniqueIDPrevious =uniqueID
- self%genericLastUniqueID =uniqueID
- self%uniqueIDPreviousSIDM =uniqueID
- self%velocityDispersionCentral =-1.0d0
- self%radiusInteractivePrevious =-1.0d0
- self%radiusInteraction_ =-1.0d0
- self%densityInteraction =-1.0d0
- self%massInteraction =-1.0d0
- self%velocityDispersionInteraction =-1.0d0
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine sidmIsothermalCalculationReset
-
- subroutine sidmIsothermalTabulateSolutions(self,xiRequired)
- !!{
- Tabulate solutions for $y_0(\xi)$, $z_0(\xi)$.
- !!}
- use :: Display , only : displayIndent , displayUnindent , displayMessage, verbosityLevelWorking, &
- & displayCounter , displayCounterClear
- use :: File_Utilities , only : Directory_Make , File_Exists , File_Lock , File_Path , &
- & File_Unlock , lockDescriptor
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear
- use :: Multidimensional_Minimizer, only : multiDMinimizer
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- double precision , intent(in ) :: xiRequired
- integer , parameter :: countXiPerUnit = 100
- integer , parameter :: countRadii =1000
- double precision , parameter :: Y0Minimum =1.0d+0, Y0Maximum =1.0d+6
- double precision , parameter :: Z0Minimum =0.1d+0, Z0Maximum =3.0d+0
- double precision , parameter :: xiMinimum =1.1d+0, xiMaximum =1.0d+1
- double precision , parameter :: x1 =1.0d+0
- double precision , parameter :: odeToleranceAbsolute=1.0d-9, odeToleranceRelative=1.0d-9
- double precision , dimension(propertyCount+1 ) :: properties , propertyScales
- double precision , dimension(propertyCount ) :: locationMinimum
- double precision , dimension( : ), allocatable :: xi , y0 , &
- & z0
- double precision :: x
- type (multiDMinimizer ) , allocatable :: minimizer_
- integer :: countXi , count
- integer (c_size_t ) :: i , j , &
- & iteration
- logical :: converged , retabulate
- type (varying_string ) :: fileName
- type (hdf5Object ) :: file
- type (lockDescriptor ) :: fileLock
- character (len=16 ) :: labelXiMinimum , labelXiMaximum
-
- ! Return immediately if solutions have been tabulated with sufficient extent already.
- if ( &
- & xiRequired >= self%xiTabulatedMinimum &
- & .and. &
- & xiRequired <= self%xiTabulatedMaximum &
- & ) return
- ! Deallocate existing table if necessary.
- if (allocated(self%radiiDimensionless )) deallocate(self%radiiDimensionless )
- if (allocated(self%densityProfileDimensionless )) deallocate(self%densityProfileDimensionless )
- if (allocated(self%massProfileDimensionless )) deallocate(self%massProfileDimensionless )
- if (allocated(self%interpolatorXi )) deallocate(self%interpolatorXi )
- if (allocated(self%interpolatorRadiiDimensionless )) deallocate(self%interpolatorRadiiDimensionless )
- if (allocated(self%densityCentralDimensionless )) deallocate(self%densityCentralDimensionless )
- if (allocated(self%velocityDispersionCentralDimensionless)) deallocate(self%velocityDispersionCentralDimensionless)
- ! By default assume that we do need to retabulate.
- retabulate=.true.
- ! Construct a file name for the table.
- fileName=inputPath(pathTypeDataDynamic)// &
- & 'darkMatter/' // &
- & self%objectType() // &
- & '.hdf5'
- call Directory_Make(char(File_Path(char(fileName))))
- if (File_Exists(fileName)) then
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
- ! Restore tables from file.
- !$ call hdf5Access%set()
- call file%openFile (char(fileName) )
- call file%readDataset('xi' , xi )
- call file%readDataset('radii' ,self%radiiDimensionless )
- call file%readDataset('y0' , y0 )
- call file%readDataset('z0' , z0 )
- call file%readDataset('densityProfileDimensionless',self%densityProfileDimensionless)
- call file%readDataset('massProfileDimensionless' ,self%massProfileDimensionless )
- call file%close ( )
- !$ call hdf5Access%unset()
- self%xiTabulatedMinimum=xi( 1 )
- self%xiTabulatedMaximum=xi(size(xi))
- ! Check if the table is sufficient.
- retabulate= xiRequired < self%xiTabulatedMinimum &
- & .or. &
- & xiRequired > self%xiTabulatedMaximum
- call File_Unlock(fileLock)
- end if
- ! Retabulate now if necessary.
- if (retabulate) then
- if (allocated( xi )) deallocate( xi )
- if (allocated( y0 )) deallocate( y0 )
- if (allocated( z0 )) deallocate( z0 )
- if (allocated(self%radiiDimensionless )) deallocate(self%radiiDimensionless )
- if (allocated(self%densityProfileDimensionless)) deallocate(self%densityProfileDimensionless)
- if (allocated(self%massProfileDimensionless )) deallocate(self%massProfileDimensionless )
- ! Set extent for tabulation.
- self%xiTabulatedMinimum=min(1.0d0*xiRequired,xiMinimum)
- self%xiTabulatedMaximum=max(1.1d0*xiRequired,xiMaximum)
- write (labelXiMinimum,'(f5.2)') self%xiTabulatedMinimum
- write (labelXiMaximum,'(f5.2)') self%xiTabulatedMaximum
- call displayIndent ('tabulating isothermal SIDM density profile solutions' ,verbosityLevelWorking)
- call displayMessage('range: '//trim(adjustl(labelXiMinimum))//' < ξ < '//trim(adjustl(labelXiMaximum))//'',verbosityLevelWorking)
- ! Construct ranges of the parameter ξ to span.
- countXi=int((self%xiTabulatedMaximum-self%xiTabulatedMinimum)*dble(countXiPerUnit))+1
- allocate( xi ( countXi))
- allocate( y0 ( countXi))
- allocate( z0 ( countXi))
- allocate(self%radiiDimensionless (countRadii ))
- allocate(self%densityProfileDimensionless(countRadii,countXi))
- allocate(self%massProfileDimensionless (countRadii,countXi))
- xi =Make_Range(self%xiTabulatedMinimum,self%xiTabulatedMaximum,countXi ,rangeTypeLinear)
- self%radiiDimensionless=Make_Range( 0.0d0 , 1.0d0 ,countRadii,rangeTypeLinear)
- ! Set absolute property scales for ODE solving.
- propertyScales=1.0d0
- ! Start parallel region to solve for halo structure at each value of ξ.
- count=0
- call displayCounter(count,isNew=.true.,verbosity=verbosityLevelWorking)
- !$omp parallel private(i,j,x,properties,locationMinimum,iteration,converged,minimizer_)
- !! Allocate and construct objects needed by each thread.
- allocate(odeSolver_)
- allocate(minimizer_)
- odeSolver_=odeSolver (propertyCount+1,sidmIsothermalDimensionlessODEs ,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative,scale=propertyScales)
- minimizer_=multiDMinimizer(propertyCount ,sidmIsothermalDimensionlessFitMetric )
- !$omp do schedule(dynamic)
- do i=1,countXi
- xi_=xi(i)
- ! Seek the low-density solution.
- call minimizer_%set(x=[0.0d0,1.0d0],stepSize=[0.01d0,0.01d0])
- iteration=0
- converged=.false.
- do while (.not.converged .and. iteration < 100)
- call minimizer_%iterate()
- iteration=iteration+1
- converged=minimizer_%testSize(toleranceAbsolute=1.0d-12)
- end do
- locationMinimum=minimizer_%x()
- y0(i)=exp(locationMinimum(1))
- z0(i)= locationMinimum(2)
- ! Tabulate solutions for density and mass.
- do j=1,countRadii
- x =0.0d0
- properties=0.0d0
- call odeSolver_%solve(x,self%radiiDimensionless(j),properties)
- self%densityProfileDimensionless(j,i)=+y0(i) &
- & *exp( &
- & -properties(1) &
- & /z0(i) **2 &
- & )
- self%massProfileDimensionless (j,i)=+ properties(3)
- end do
- !$omp atomic
- count=count+1
- call displayCounter(int(100.0d0*dble(count)/dble(countXi)),isNew=.false.,verbosity=verbosityLevelWorking)
- end do
- !$omp end do
- call displayCounterClear(verbosityLevelWorking)
- deallocate(odeSolver_)
- deallocate(minimizer_)
- !$omp end parallel
- ! Write the data to file.
- call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
- !$ call hdf5Access%set()
- call file%openFile (char( fileName ) ,overWrite=.true.,readOnly=.false.)
- call file%writeDataset( xi ,'xi' )
- call file%writeDataset( self%radiiDimensionless ,'radii' )
- call file%writeDataset( y0 ,'y0' )
- call file%writeDataset( z0 ,'z0' )
- call file%writeDataset( self%densityProfileDimensionless ,'densityProfileDimensionless' )
- call file%writeDataset( self%massProfileDimensionless ,'massProfileDimensionless' )
- call file%close ( )
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- call displayUnindent('done',verbosityLevelWorking)
- end if
- ! Build the interpolators.
- allocate(self%interpolatorXi )
- allocate(self%interpolatorRadiiDimensionless )
- allocate(self%densityCentralDimensionless )
- allocate(self%velocityDispersionCentralDimensionless)
- self%densityCentralDimensionless =interpolator( xi ,y0)
- self%velocityDispersionCentralDimensionless=interpolator( xi ,z0)
- self%interpolatorXi =interpolator( xi )
- self%interpolatorRadiiDimensionless =interpolator(self%radiiDimensionless )
- return
- end subroutine sidmIsothermalTabulateSolutions
-
- double precision function sidmIsothermalDimensionlessFitMetric(propertiesCentral)
- !!{
- Evaluate the fit metric.
- !!}
- implicit none
- double precision, intent(in ), dimension(:) :: propertiesCentral
- double precision, parameter :: x1 =1.0d0
- double precision , dimension(propertyCount+1) :: properties
- double precision :: x , y1, &
- & m1
-
- ! Extract current parameters to submodule-scope.
- y0_=exp(propertiesCentral(1))
- z0_= propertiesCentral(2)
- ! Solve the ODE to x₁.
- x =0.0d0
- properties=0.0d0
- call odeSolver_%solve(x,x1,properties)
- ! Extract density and mass at x₁.
- y1 =+y0_ &
- & *exp( &
- & -properties(1) &
- & /z0_ **2 &
- & )
- m1 =+ properties(3)
- ! Evaluate the fit metric.
- sidmIsothermalDimensionlessFitMetric=+(y1-1.0d0)**2 &
- & +(m1-1.0d0)**2
- return
- end function sidmIsothermalDimensionlessFitMetric
-
- integer function sidmIsothermalDimensionlessODEs(x,properties,propertiesRateOfChange)
- !!{
- Define the dimensionless ODE system to solve for isothermal self-interacting dark matter cores.
- !!}
- use :: Interface_GSL, only : GSL_Success
- implicit none
- double precision, intent(in ) :: x
- double precision, intent(in ), dimension(:) :: properties
- double precision, intent( out), dimension(:) :: propertiesRateOfChange
- double precision :: y
-
- ! Compute the dimensionless density.
- y =+y0_ &
- & *exp( &
- & -max(properties(1),0.0d0) &
- & /z0_**2 &
- & )
- ! Evaluate the ODE.
- propertiesRateOfChange (1)=+properties(2)
- propertiesRateOfChange (2)=+3.0d0 &
- & /xi_ &
- & *y
- if (x > 0.0d0) &
- & propertiesRateOfChange(2)=+propertiesRateOfChange(2) &
- & -2.0d0 &
- & *properties (2) &
- & /x
- propertiesRateOfChange (3)=+3.0d0 &
- & /xi_ &
- & *x**2 &
- & *y
- sidmIsothermalDimensionlessODEs=GSL_Success
- return
- end function sidmIsothermalDimensionlessODEs
-
- subroutine sidmIsothermalComputeSolution(self,node)
- !!{
- Compute a solution for the isothermal core of an SIDM halo.
- !!}
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer , parameter :: countTable =1000
- double precision , parameter :: odeToleranceAbsolute =1.0d-3, odeToleranceRelative =1.0d-3
- double precision :: densityCentral , velocityDispersionCentral , &
- & densityInteraction , massInteraction , &
- & radiusInteraction , xi , &
- & velocityDispersionInteraction
-
- ! Find the interaction radius.
- radiusInteraction =self%radiusInteraction (node )
- ! Properties of the original density profile at the interaction radius.
- densityInteraction =self%darkMatterProfileDMO_%density (node,radiusInteraction)
- massInteraction =self%darkMatterProfileDMO_%enclosedMass (node,radiusInteraction)
- ! Find the velocity dispersion scale to be applied to the dimensionless solutions.
- velocityDispersionInteraction=sqrt(gravitationalConstantGalacticus*massInteraction/radiusInteraction)
- ! Compute the ξ parameter.
- xi =+massInteraction &
- & *3.0d0 &
- & /4.0d0 &
- & /Pi &
- & /densityInteraction &
- & /radiusInteraction **3
- ! Ensure dimensionless solutions have been tabulated.
- call self%tabulateSolutions(xi)
- ! Find the properties at the halo center.
- densityCentral =self%densityCentralDimensionless %interpolate(xi)*densityInteraction
- velocityDispersionCentral =self%velocityDispersionCentralDimensionless%interpolate(xi)*velocityDispersionInteraction
- ! Store properties of current profile.
- self%radiusInteraction_ =radiusInteraction
- self%densityInteraction =densityInteraction
- self%massInteraction =massInteraction
- self%velocityDispersionInteraction=velocityDispersionInteraction
- self%velocityDispersionCentral =velocityDispersionCentral
- ! Compute interpolating factors in ξ.
- call self%interpolatorXi%linearFactors(xi,self%indexXi,self%factorsXi)
- return
- end subroutine sidmIsothermalComputeSolution
-
- double precision function sidmIsothermalDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- integer (c_size_t ) :: i , j, &
- & indexRadius
- double precision , dimension(0:1) :: factorsRadius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalDensity=self%darkMatterProfileDMO_%density(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- call self%interpolatorRadiiDimensionless%linearFactors(radius/self%radiusInteraction_,indexRadius,factorsRadius)
- sidmIsothermalDensity=0.0d0
- do i=0,1
- do j=0,1
- sidmIsothermalDensity=+ sidmIsothermalDensity &
- & +self%densityProfileDimensionless(indexRadius+i,self%indexXi+j) &
- & * factorsRadius ( i ) &
- & *self%factorsXi ( j)
- end do
- end do
- sidmIsothermalDensity=+ sidmIsothermalDensity &
- & *self%densityInteraction
- end if
- return
- end function sidmIsothermalDensity
-
- double precision function sidmIsothermalDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- integer (c_size_t ) :: indexRadius
- double precision , dimension(0:1) :: factorsRadius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- call self%interpolatorRadiiDimensionless%linearFactors(radius/self%radiusInteraction_,indexRadius,factorsRadius)
- if (indexRadius > 1) then
- sidmIsothermalDensityLogSlope=+log(self%densityProfileDimensionless(indexRadius+1,self%indexXi+0)/self%densityProfileDimensionless(indexRadius+0,self%indexXi+0)) &
- & /log(self%radiiDimensionless (indexRadius+1 )/self%radiiDimensionless (indexRadius+0 ))
- else
- sidmIsothermalDensityLogSlope=+0.0d0
- end if
- end if
- return
- end function sidmIsothermalDensityLogSlope
-
- double precision function sidmIsothermalEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- integer (c_size_t ) :: i , j, &
- & indexRadius
- double precision , dimension(0:1) :: factorsRadius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalEnclosedMass=self%darkMatterProfileDMO_%enclosedMass(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- call self%interpolatorRadiiDimensionless%linearFactors(radius/self%radiusInteraction_,indexRadius,factorsRadius)
- sidmIsothermalEnclosedMass=0.0d0
- do i=0,1
- do j=0,1
- sidmIsothermalEnclosedMass=+ sidmIsothermalEnclosedMass &
- & +self%massProfileDimensionless (indexRadius+i,self%indexXi+j) &
- & * factorsRadius ( i ) &
- & *self%factorsXi ( j)
- end do
- end do
- sidmIsothermalEnclosedMass=+ sidmIsothermalEnclosedMass &
- & *self%massInteraction
- end if
- return
- end function sidmIsothermalEnclosedMass
-
- double precision function sidmIsothermalRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- sidmIsothermalRadiusEnclosingDensity=self%radiusEnclosingDensityNumerical(node,density)
- return
- end function sidmIsothermalRadiusEnclosingDensity
-
- double precision function sidmIsothermalRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- sidmIsothermalRadiusEnclosingMass=self%radiusEnclosingMassNumerical(node,mass)
- return
- end function sidmIsothermalRadiusEnclosingMass
-
- double precision function sidmIsothermalRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- sidmIsothermalRadialMoment=self%radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- return
- end function sidmIsothermalRadialMoment
-
- double precision function sidmIsothermalPotential(self,node,radius,status)
+ function sidmIsothermalGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalSIDMIsothermal, kinematicsDistributionSIDMIsothermal, nonAnalyticSolversNumerical, massDistributionSpherical
implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalPotential=self%darkMatterProfileDMO_%potential(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalPotential=+ self%darkMatterProfileDMO_%potential (node,self%radiusInteraction_) &
- & - self %velocityDispersionCentral **2 &
- & *log( &
- & +self %density (node, radius ) &
- & /self %density (node,self%radiusInteraction_) &
- & )
- end if
- return
- end function sidmIsothermalPotential
-
- double precision function sidmIsothermalCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- sidmIsothermalCircularVelocity=self%circularVelocityNumerical(node,radius)
- return
- end function sidmIsothermalCircularVelocity
-
- double precision function sidmIsothermalCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalCircularVelocityMaximum=self%circularVelocityMaximumNumerical(node)
- return
- end function sidmIsothermalCircularVelocityMaximum
-
- double precision function sidmIsothermalRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is acheived in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalRadiusCircularVelocityMaximum=self%radiusCircularVelocityMaximumNumerical(node)
- return
- end function sidmIsothermalRadiusCircularVelocityMaximum
-
- double precision function sidmIsothermalRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > self%radiusInteraction(node)) then
- sidmIsothermalRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- if (self%velocityDispersionCentral <= 0.0d0 ) call self%computeSolution (node )
- sidmIsothermalRadialVelocityDispersion=self%velocityDispersionCentral
- end if
- return
- end function sidmIsothermalRadialVelocityDispersion
-
- double precision function sidmIsothermalRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- sidmIsothermalRadiusFromSpecificAngularMomentum=self%radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- return
- end function sidmIsothermalRadiusFromSpecificAngularMomentum
-
- double precision function sidmIsothermalRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalRotationNormalization=self%rotationNormalizationNumerical(node)
- return
- end function sidmIsothermalRotationNormalization
-
- double precision function sidmIsothermalEnergy(self,node)
- !!{
- Return the energy of a sidmIsothermal halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- sidmIsothermalEnergy=self%energyNumerical(node)
- return
- end function sidmIsothermalEnergy
-
- double precision function sidmIsothermalKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the sidmIsothermal density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- sidmIsothermalKSpace=self%kSpaceNumerical(node,waveNumber)
- return
- end function sidmIsothermalKSpace
-
- double precision function sidmIsothermalFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the sidmIsothermal density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- sidmIsothermalFreefallRadius=self%freefallRadiusNumerical(node,time)
- return
- end function sidmIsothermalFreefallRadius
-
- double precision function sidmIsothermalFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the sidmIsothermal density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOSIDMIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionSIDMIsothermal), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOSIDMIsothermal ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ class (nodeComponentBasic ), pointer :: basic
+ !![
+
+ !!]
- sidmIsothermalFreefallRadiusIncreaseRate=self%freefallRadiusIncreaseRateNumerical(node,time)
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalSIDMIsothermal :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalSIDMIsothermal)
+ massDistributionDecorated => self%darkMatterProfileDMO_%get (node,weightBy,weightIndex)
+ basic => node %basic( )
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalSIDMIsothermal( &
+ & timeAge =basic%time (), &
+ & nonAnalyticSolver = nonAnalyticSolversNumerical , &
+ & massDistribution_ = massDistributionDecorated , &
+ & darkMatterParticle_=self %darkMatterParticle_ , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionSIDMIsothermal( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function sidmIsothermalFreefallRadiusIncreaseRate
+ end function sidmIsothermalGet
diff --git a/source/dark_matter_profiles_DMO.Zhao1996.F90 b/source/dark_matter_profiles_DMO.Zhao1996.F90
index e55a525244..a4cfba5c8b 100644
--- a/source/dark_matter_profiles_DMO.Zhao1996.F90
+++ b/source/dark_matter_profiles_DMO.Zhao1996.F90
@@ -21,28 +21,10 @@
An implementation of \cite{zhao_analytical_1996} dark matter halo profiles.
!!}
- !![
-
- specialCase
- Special cases for {\normalfont \ttfamily zhao1996} dark matter halo profile class.
-
-
-
-
-
-
- !!]
-
!![
- A dark matter profile DMO class which implements the \cite{zhao_analytical_1996} density profile
- \begin{equation}
- \rho_\mathrm{dark matter}(r) \propto \left({r\over r_\mathrm{s}}\right)^{-\gamma} \left(1+\left[{r\over r_\mathrm{s}}\right]^\alpha \right)^{-(\beta-\gamma)/\alpha},
- \end{equation}
- normalized such that the total mass of the \gls{node} is enclosed with the virial radius and with the scale length
- $r_\mathrm{s}$. Numerical solutions are implemented for the case of general $(\alpha,\beta,\gamma)$, with some analytic
- solution for certain special cases.
+ A dark matter profile DMO class which builds \refClass{massDistributionZhao1996} objects.
!!]
@@ -51,43 +33,22 @@
A dark matter halo profile class implementing \cite{zhao_analytical_1996} dark matter halos.
!!}
private
- type (enumerationSpecialCaseType) :: specialCase
- double precision :: alpha , beta, &
- & gamma
- contains
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ double precision :: alpha , beta, &
+ & gamma
+ contains
!![
-
-
-
-
-
+
+
+
!!]
- final :: zhao1996Destructor
- procedure :: autoHook => zhao1996AutoHook
- procedure :: calculationReset => zhao1996CalculationReset
- procedure :: exponents => zhao1996Exponents
- procedure :: scaleRadius => zhao1996ScaleRadius
- procedure :: normalization => zhao1996Normalization
- procedure :: massUnnormalized => zhao1996MassUnnormalized
- procedure :: density => zhao1996Density
- procedure :: densityLogSlope => zhao1996DensityLogSlope
- procedure :: enclosedMass => zhao1996EnclosedMass
- procedure :: radiusEnclosingMass => zhao1996RadiusEnclosingMass
- procedure :: radiusEnclosingDensity => zhao1996RadiusEnclosingDensity
- procedure :: potential => zhao1996Potential
- procedure :: circularVelocity => zhao1996CircularVelocity
- procedure :: circularVelocityMaximum => zhao1996CircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => zhao1996RadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => zhao1996RadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => zhao1996RadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => zhao1996RotationNormalization
- procedure :: energy => zhao1996Energy
- procedure :: kSpace => zhao1996KSpace
- procedure :: freefallRadius => zhao1996FreefallRadius
- procedure :: freefallRadiusIncreaseRate => zhao1996FreefallRadiusIncreaseRate
- procedure :: radialMoment => zhao1996RadialMoment
+ final :: zhao1996Destructor
+ procedure :: get => zhao1996Get
+ procedure :: exponents => zhao1996Exponents
+ procedure :: scaleRadius => zhao1996ScaleRadius
+ procedure :: normalization => zhao1996Normalization
end type darkMatterProfileDMOZhao1996
interface darkMatterProfileDMOZhao1996
@@ -98,12 +59,6 @@
module procedure zhao1996ConstructorInternal
end interface darkMatterProfileDMOZhao1996
- ! Sub-module scope variables used in numerical solutions.
- class (darkMatterProfileDMOZhao1996), pointer :: self_
- type (treeNode ), pointer :: node_
- double precision :: time_, radiusFreefall_
- !$omp threadprivate(self_,node_,time_,radiusFreefall_)
-
contains
function zhao1996ConstructorParameters(parameters) result(self)
@@ -148,7 +103,6 @@ function zhao1996ConstructorInternal(alpha,beta,gamma,darkMatterHaloScale_) resu
!!{
Generic constructor for the {\normalfont \ttfamily zhao1996} dark matter halo profile class.
!!}
- use :: Numerical_Comparison, only : Values_Agree
implicit none
type (darkMatterProfileDMOZhao1996) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
@@ -158,100 +112,86 @@ function zhao1996ConstructorInternal(alpha,beta,gamma,darkMatterHaloScale_) resu
!!]
- ! Detect special cases.
- if ( &
- & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(gamma,1.0d0,absTol=1.0d-6) &
- & ) then
- ! The "NFW" profile.
- self%specialCase=specialCaseNFW
- else if ( &
- & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(gamma,0.0d0,absTol=1.0d-6) &
- & ) then
- ! The "cored NFW" profile.
- self%specialCase=specialCaseCoredNFW
- else if ( &
- & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(gamma,0.5d0,absTol=1.0d-6) &
- & ) then
- ! The "γ=1/2 NFW" profile.
- self%specialCase=specialCaseGamma0_5NFW
- else if ( &
- & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
- & .and. &
- & Values_Agree(gamma,1.5d0,absTol=1.0d-6) &
- & ) then
- ! The "γ=3/2 NFW" profile.
- self%specialCase=specialCaseGamma1_5NFW
- else
- ! Use general solutions.
- self%specialCase=specialCaseGeneral
- end if
return
end function zhao1996ConstructorInternal
- subroutine zhao1996AutoHook(self)
+ function zhao1996Get(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Attach to the calculation reset event.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionZhao1996, kinematicsDistributionZhao1996
implicit none
- class(darkMatterProfileDMOZhao1996), intent(inout) :: self
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionZhao1996 ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOZhao1996 ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ double precision :: alpha , beta , &
+ & gamma , scaleRadius, &
+ & mass
+ !![
+
+ !!]
- call calculationResetEvent%attach(self,zhao1996CalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOZhao1996')
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionZhao1996 :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionZhao1996)
+ mass =self%normalization(node)
+ scaleRadius=self%scaleRadius (node)
+ call self%exponents(node,alpha,beta,gamma)
+ !![
+
+
+ massDistributionZhao1996( &
+ & mass = mass , &
+ & radiusOuter =self %darkMatterHaloScale_%radiusVirial (node), &
+ & scaleLength = scaleRadius , &
+ & alpha = alpha , &
+ & beta = beta , &
+ & gamma = gamma , &
+ & componentType= componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionZhao1996( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end subroutine zhao1996AutoHook
+ end function zhao1996Get
subroutine zhao1996Destructor(self)
!!{
Destructor for the {\normalfont \ttfamily zhao1996} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOZhao1996), intent(inout) :: self
!![
!!]
- if (calculationResetEvent%isAttached(self,zhao1996CalculationReset)) call calculationResetEvent%detach(self,zhao1996CalculationReset)
return
end subroutine zhao1996Destructor
- subroutine zhao1996CalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%genericLastUniqueID =uniqueID
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine zhao1996CalculationReset
-
subroutine zhao1996Exponents(self,node,alpha,beta,gamma)
!!{
Compute the exponents of the {\normalfont \ttfamily zhao1996} dark matter halo profile.
@@ -282,913 +222,18 @@ double precision function zhao1996ScaleRadius(self,node)
zhao1996ScaleRadius = darkMatterProfile%scale ()
return
end function zhao1996ScaleRadius
-
+
double precision function zhao1996Normalization(self,node)
!!{
- Returns the normalization of the dark matter profile of {\normalfont \ttfamily node}.
+ Compute the mass normalization of the {\normalfont \ttfamily zhao1996} dark matter halo profile.
!!}
use :: Galacticus_Nodes, only : nodeComponentBasic
implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- double precision :: radiusScale, radiusVirialScaleFree
-
- basic => node %basic ( )
- radiusScale = self %scaleRadius (node )
- radiusVirialScaleFree = +self %darkMatterHaloScale_%radiusVirial (node ) &
- & / radiusScale
- zhao1996Normalization = +basic %mass ( ) &
- & /self %massUnnormalized(node,radiusVirialScaleFree) &
- & / radiusScale **3
- return
- end function zhao1996Normalization
-
- double precision function zhao1996MassUnnormalized(self,node,radiusScaleFree)
- !!{
- Returns the unnormalized mass in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radiusScaleFree}.
- !!}
- use :: Error , only : Error_Report
- use :: Numerical_Constants_Math, only : Pi
- use :: Hypergeometric_Functions, only : Hypergeometric_2F1
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radiusScaleFree
- double precision , parameter :: radiusScaleFreeTiny=1.0d-3
- double precision :: alpha , beta, &
- & gamma
-
- select case (self%specialCase%ID)
- case (specialCaseGeneral%ID)
- call self%exponents(node,alpha,beta,gamma)
- zhao1996MassUnnormalized=+4.0d0 &
- & *Pi &
- & *radiusScaleFree**(3.0d0-gamma) &
- & *Hypergeometric_2F1([(3.0d0-gamma)/alpha,(beta-gamma)/alpha],[1.0d0+(3.0d0-gamma)/alpha],-radiusScaleFree**alpha) &
- & / (3.0d0-gamma)
- case (specialCaseNFW%ID)
- if (radiusScaleFree node%basic ( )
- radiusScale = self%scaleRadius (node)
- radiusScaleFree = + radius &
- & / radiusScale
- select case (self%specialCase%ID)
- case (specialCaseGeneral%ID)
- zhao1996Potential = -4.0d0 &
- & *Pi &
- & *( &
- & +1.0 &
- & -radiusScaleFree**(2.0d0-gamma) &
- & *( &
- & +Hypergeometric_2F1([(2.0d0-gamma)/alpha,(beta-gamma)/alpha],[(2.0d0+alpha-gamma)/alpha],-radiusScaleFree**alpha)/(2.0d0-gamma) &
- & -Hypergeometric_2F1([(3.0d0-gamma)/alpha,(beta-gamma)/alpha],[(3.0d0+alpha-gamma)/alpha],-radiusScaleFree**alpha)/(3.0d0-gamma) &
- & ) &
- & ) &
- & *gravitationalConstantGalacticus &
- & *self%normalization(node) &
- & *self%scaleRadius (node)**2
- case (specialCaseNFW%ID)
- zhao1996Potential = -4.0d0 &
- & *Pi &
- & *log(1.0d0+radiusScaleFree) &
- & / radiusScaleFree &
- & *gravitationalConstantGalacticus &
- & *self%normalization(node) &
- & *self%scaleRadius (node)**2
- case (specialCaseCoredNFW%ID)
- zhao1996Potential = -4.0d0 &
- & *Pi &
- & *( &
- & +0.5d0 &
- & * radiusScaleFree &
- & / (1.0d0+radiusScaleFree) &
- & +log(1.0d0+radiusScaleFree) &
- & / radiusScaleFree &
- & ) &
- & *gravitationalConstantGalacticus &
- & *self%normalization(node) &
- & *self%scaleRadius (node)**2
- case (specialCaseGamma0_5NFW%ID)
- zhao1996Potential = -4.0d0 &
- & *Pi &
- & *( &
- & +1.0d0 &
- & -2.0d0 &
- & /3.0d0 &
- & *sqrt( &
- & + radiusScaleFree &
- & /(1.0d0+radiusScaleFree) &
- & ) &
- & -2.0d0 &
- & /sqrt( &
- & + radiusScaleFree &
- & *(1.0d0+radiusScaleFree) &
- & ) &
- & +2.0d0 &
- & *asinh(sqrt( radiusScaleFree)) &
- & / radiusScaleFree &
- & ) &
- & *gravitationalConstantGalacticus &
- & *self%normalization(node) &
- & *self%scaleRadius (node)**2
- case (specialCaseGamma1_5NFW%ID)
- zhao1996Potential = -4.0d0 &
- & *Pi &
- & *( &
- & +1.0d0 &
- & -2.0d0 &
- & *sqrt( &
- & + radiusScaleFree &
- & /(1.0d0+radiusScaleFree) &
- & ) &
- & -2.0d0 &
- & /sqrt( &
- & + radiusScaleFree &
- & *(1.0d0+radiusScaleFree) &
- & ) &
- & +2.0d0 &
- & *asinh(sqrt( radiusScaleFree)) &
- & / radiusScaleFree &
- & ) &
- & *gravitationalConstantGalacticus &
- & *self%normalization(node) &
- & *self%scaleRadius (node)**2
- case default
- zhao1996Potential=+0.0d0
- call Error_Report('unknown special case'//{introspection:location})
- end select
- return
- end function zhao1996Potential
-
- double precision function zhao1996CircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- zhao1996CircularVelocity=sqrt(gravitationalConstantGalacticus*self%enclosedMass(node,radius)/radius)
- else
- zhao1996CircularVelocity=0.0d0
- end if
- return
- end function zhao1996CircularVelocity
-
- double precision function zhao1996CircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOZhao1996 ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- zhao1996CircularVelocityMaximum=self%circularVelocityMaximumNumerical(node)
- return
- end function zhao1996CircularVelocityMaximum
-
- double precision function zhao1996RadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity occurs in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOZhao1996 ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- zhao1996RadiusCircularVelocityMaximum=self%radiusCircularVelocityMaximumNumerical(node)
- return
- end function zhao1996RadiusCircularVelocityMaximum
-
- double precision function zhao1996RadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Error , only : Error_Report
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Polylogarithms , only : Polylogarithm_2
- implicit none
- class (darkMatterProfileDMOZhao1996 ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusScaleFreeTiny =1.0d-3, radiusScaleFreeLarge=1.0d2
- double precision :: radiusScale , radiusScaleFree , &
- & velocityDispersionSquaredScaleFree
-
- select case (self%specialCase%ID)
- case (specialCaseGeneral%ID)
- zhao1996RadialVelocityDispersion=self%radialVelocityDispersionNumerical(node,radius)
- case (specialCaseNFW%ID)
- radiusScale = self%scaleRadius(node)
- radiusScaleFree=+ radius &
- & / radiusScale
- if (radiusScaleFree < radiusScaleFreeTiny ) then
- ! Use series solution for small radii.
- velocityDispersionSquaredScaleFree=+11.0d0*Pi/15.0d0*radiusScaleFree**4 &
- & + Pi/ 6.0d0*radiusScaleFree**3*(-101.0d0+12.0d0*Pi**2-12.0d0*log(radiusScaleFree)) &
- & + 2.0d0*Pi/ 3.0d0*radiusScaleFree**2*(- 59.0d0+ 6.0d0*Pi**2- 6.0d0*log(radiusScaleFree)) &
- & + Pi *radiusScaleFree *(- 23.0d0+ 2.0d0*Pi**2- 2.0d0*log(radiusScaleFree))
- else if (radiusScaleFree > radiusScaleFreeLarge) then
- ! Use series solution for large radii.
- velocityDispersionSquaredScaleFree=+Pi*(- 3.0d0+ 4.0d0*log(radiusScaleFree))/( 4.0d0*radiusScaleFree ) &
- & +Pi*(+ 69.0d0+ 20.0d0*log(radiusScaleFree))/( 50.0d0*radiusScaleFree**2) &
- & +Pi*(- 97.0d0- 60.0d0*log(radiusScaleFree))/( 300.0d0*radiusScaleFree**3) &
- & +Pi*(+284.0d0+420.0d0*log(radiusScaleFree))/(3675.0d0*radiusScaleFree**4)
- else
- ! Use full solution.
- velocityDispersionSquaredScaleFree=+( &
- & +2.0d0 &
- & *Pi &
- & *( &
- & +radiusScaleFree &
- & *( &
- & -1.0d0 &
- & +radiusScaleFree &
- & *( &
- & - 9.0d0 &
- & - 7.0d0*radiusScaleFree &
- & +Pi**2*(+1.0d0+radiusScaleFree)**2 &
- & ) &
- & ) &
- & +radiusScaleFree**4*log(+1.0d0+1.0d0/radiusScaleFree) &
- & + log(+1.0d0+ radiusScaleFree) &
- & +radiusScaleFree &
- & *( &
- & -( &
- & + radiusScaleFree*(+1.0d0+2.0d0*radiusScaleFree) *log( +radiusScaleFree) &
- & ) &
- & + log(+1.0d0+radiusScaleFree) &
- & *( &
- & -2.0d0-4.0d0*radiusScaleFree*(+2.0d0+ radiusScaleFree) &
- & +3.0d0 *radiusScaleFree*(+1.0d0+ radiusScaleFree)**2*log(+1.0d0+radiusScaleFree) &
- & ) &
- & ) &
- & + 6.0d0 &
- & * radiusScaleFree **2 &
- & * (+1.0d0+radiusScaleFree)**2 &
- & *PolyLogarithm_2( -radiusScaleFree) &
- & ) &
- & ) &
- & /radiusScaleFree
- end if
- zhao1996RadialVelocityDispersion=+sqrt( &
- & + gravitationalConstantGalacticus &
- & *self%normalization (node) &
- & * radiusScale **2 &
- & * velocityDispersionSquaredScaleFree &
- & )
- case (specialCaseCoredNFW%ID)
- radiusScale = self%scaleRadius(node)
- radiusScaleFree=+ radius &
- & / radiusScale
- if (radiusScaleFree < radiusScaleFreeTiny ) then
- ! Use series solution for small radii.
- velocityDispersionSquaredScaleFree=+(119.0d0*Pi-12.0d0*Pi**3)/ 6.0d0 &
- & +(119.0d0*Pi-12.0d0*Pi**3)/ 2.0d0*radiusScaleFree &
- & +(353.0d0*Pi-36.0d0*Pi**3)/ 6.0d0*radiusScaleFree**2 &
- & +(121.0d0*Pi-12.0d0*Pi**3)/ 6.0d0*radiusScaleFree**3 &
- & - 9.0d0*Pi**4 /20.0d0*radiusScaleFree**4
- else if (radiusScaleFree > radiusScaleFreeLarge) then
- ! Use series solution for large radii.
- velocityDispersionSquaredScaleFree=+Pi*(- 5.0d0+ 4.0d0*log(radiusScaleFree))/( 4.0d0*radiusScaleFree ) &
- & +Pi*(+ 177.0d0+ 60.0d0*log(radiusScaleFree))/( 100.0d0*radiusScaleFree**2) &
- & +Pi*(- 157.0d0- 60.0d0*log(radiusScaleFree))/( 300.0d0*radiusScaleFree**3) &
- & +Pi*(+5857.0d0+1260.0d0*log(radiusScaleFree))/(14700.0d0*radiusScaleFree**4)
- else
- ! Use full solution.
- velocityDispersionSquaredScaleFree=+( &
- & +( &
- & +Pi &
- & *( &
- & +95.0d0 &
- & -12.0d0*Pi**2 &
- & *(1.0d0+ radiusScaleFree)**4 &
- & + 2.0d0*radiusScaleFree &
- & *( &
- & + 130.0d0 &
- & + 9.0d0*radiusScaleFree &
- & *( &
- & + 13.0d0 &
- & + 4.0d0*radiusScaleFree &
- & ) &
- & ) &
- & ) &
- & ) &
- & /6.0d0 &
- & / (1.0d0+ radiusScaleFree )**4 &
- & +2.0d0*Pi*log (1.0d0+ radiusScaleFree ) &
- & * (2.0d0+9.0d0*radiusScaleFree+6.0d0*radiusScaleFree**2) &
- & / radiusScaleFree &
- & / (1.0d0+ radiusScaleFree )**2 &
- & - 6.0d0*Pi*log (1.0d0+ radiusScaleFree )**2 &
- & -12.0d0*Pi*Polylogarithm_2( - radiusScaleFree ) &
- & ) &
- & * (1.0d0+ radiusScaleFree )**3
- end if
- zhao1996RadialVelocityDispersion=+sqrt( &
- & + gravitationalConstantGalacticus &
- & *self%normalization (node) &
- & * radiusScale **2 &
- & * velocityDispersionSquaredScaleFree &
- & )
- case (specialCaseGamma0_5NFW%ID)
- radiusScale = self%scaleRadius(node)
- radiusScaleFree=+ radius &
- & / radiusScale
- if (radiusScaleFree < radiusScaleFreeTiny ) then
- ! Use series solution for small radii.
- velocityDispersionSquaredScaleFree=+16.0d0*Pi/ 3.0d0*sqrt(radiusScaleFree )*(- 11.0d0+ 16.0d0*log(2.0d0)) &
- & +16.0d0*Pi/ 15.0d0* radiusScaleFree**1.5d0*(-139.0d0+ 200.0d0*log(2.0d0)) &
- & + 2.0d0*Pi/ 7.0d0* radiusScaleFree**2.5d0*(-387.0d0+ 560.0d0*log(2.0d0)) &
- & + 4.0d0*Pi/189.0d0* radiusScaleFree**3.5d0*(-887.0d0+1260.0d0*log(2.0d0))
- else if (radiusScaleFree > radiusScaleFreeLarge) then
- ! Use series solution for large radii.
- velocityDispersionSquaredScaleFree=+Pi*(- 29.0d0+ 24.0d0*log(2.0d0)+ 12.0d0*log(radiusScaleFree))/( 12.0d0*radiusScaleFree ) &
- & +Pi*(+107.0d0+120.0d0*log(2.0d0)+ 60.0d0*log(radiusScaleFree))/( 120.0d0*radiusScaleFree**2) &
- & +Pi*(- 11.0d0- 40.0d0*log(2.0d0)- 20.0d0*log(radiusScaleFree))/( 96.0d0*radiusScaleFree**3) &
- & +Pi*(+ 57.0d0+280.0d0*log(2.0d0)+140.0d0*log(radiusScaleFree))/(1344.0d0*radiusScaleFree**4)
- else
- ! Use full solution.
- velocityDispersionSquaredScaleFree=+( &
- & +8.0d0 &
- & *Pi &
- & *( &
- & - 6.0d0*sqrt(radiusScaleFree *(+1.0d0+radiusScaleFree)) &
- & -38.0d0*sqrt(radiusScaleFree**3*(+1.0d0+radiusScaleFree)) &
- & -57.0d0*sqrt(radiusScaleFree**5*(+1.0d0+radiusScaleFree)) &
- & -24.0d0*sqrt(radiusScaleFree**7*(+1.0d0+radiusScaleFree)) &
- & - 6.0d0 &
- & *(+1.0d0+ radiusScaleFree )**2 &
- & *(+1.0d0+2.0d0*radiusScaleFree ) &
- & *(-1.0d0+8.0d0*radiusScaleFree*(1.0d0+radiusScaleFree)) &
- & *asinh(sqrt(radiusScaleFree)) &
- & -24.0d0 &
- & * radiusScaleFree **1.5d0 &
- & *(+1.0d0+radiusScaleFree)**3.5d0 &
- & *log(radiusScaleFree/(16.0d0*(1.0d0+radiusScaleFree))) &
- & +24.0d0 &
- & *( &
- & + sqrt(radiusScaleFree**3*(1.0d0+radiusScaleFree)) &
- & +3.0d0*sqrt(radiusScaleFree**5*(1.0d0+radiusScaleFree)) &
- & +3.0d0*sqrt(radiusScaleFree**7*(1.0d0+radiusScaleFree)) &
- & + sqrt(radiusScaleFree**9*(1.0d0+radiusScaleFree)) &
- & ) &
- & *( &
- & +log( radiusScaleFree) &
- & +log(+1.0d0+radiusScaleFree) &
- & ) &
- & ) &
- & ) &
- & /( &
- & +9.0d0 &
- & * radiusScaleFree &
- & *(+1.0d0+radiusScaleFree) &
- & )
- end if
- zhao1996RadialVelocityDispersion=+sqrt( &
- & + gravitationalConstantGalacticus &
- & *self%normalization (node) &
- & * radiusScale **2 &
- & * velocityDispersionSquaredScaleFree &
- & )
- case (specialCaseGamma1_5NFW%ID)
- radiusScale = self%scaleRadius(node)
- radiusScaleFree=+ radius &
- & / radiusScale
- if (radiusScaleFree < radiusScaleFreeTiny ) then
- ! Use series solution for small radii.
- velocityDispersionSquaredScaleFree=+8.0d0*Pi/ 3.0d0*sqrt(radiusScaleFree ) &
- & - Pi/3150.0d0* radiusScaleFree**3.5d0 *(-19861.0d0+60480.0d0*log(2.0d0)-7560.0d0*log(radiusScaleFree)) &
- & - Pi/ 175.0d0* radiusScaleFree**2.5d0 *(- 8683.0d0+13440.0d0*log(2.0d0)-1680.0d0*log(radiusScaleFree)) &
- & -4.0d0*Pi/ 75.0d0* radiusScaleFree**1.5d0 *(- 817.0d0+ 960.0d0*log(2.0d0)- 120.0d0*log(radiusScaleFree))
- else if (radiusScaleFree > radiusScaleFreeLarge) then
- ! Use series solution for large radii.
- velocityDispersionSquaredScaleFree=+Pi*(- 7.0d0+ 8.0d0*log(2.0d0)+ 4.0d0*log(radiusScaleFree))/( 4.0d0*radiusScaleFree ) &
- & +Pi*(+ 147.0d0+ 120.0d0*log(2.0d0)+ 60.0d0*log(radiusScaleFree))/( 200.0d0*radiusScaleFree**2) &
- & +Pi*(- 79.0d0- 840.0d0*log(2.0d0)- 420.0d0*log(radiusScaleFree))/( 2400.0d0*radiusScaleFree**3) &
- & +Pi*(-3589.0d0+7560.0d0*log(2.0d0)+3780.0d0*log(radiusScaleFree))/(33600.0d0*radiusScaleFree**4)
- else
- ! Use full solution.
- velocityDispersionSquaredScaleFree=+( &
- & +8.0d0 &
- & *Pi &
- & * radiusScaleFree **1.5d0 &
- & *(+1.0d0+radiusScaleFree)**1.5d0 &
- & *( &
- & +( &
- & + 2.0d0 &
- & *( &
- & + 1.0d0 &
- & + 2.0d0*radiusScaleFree &
- & *(-1.0d0+4.0d0*radiusScaleFree+8.0d0*radiusScaleFree**2) &
- & ) &
- & *asinh(sqrt(radiusScaleFree)) &
- & ) &
- & /sqrt(radiusScaleFree**5*(1.0d0+radiusScaleFree)) &
- & +( &
- & -2.0d0 &
- & +radiusScaleFree &
- & *( &
- & + 5.0d0 &
- & +12.0d0*radiusScaleFree &
- & -32.0d0*radiusScaleFree *(+1.0d0+radiusScaleFree)*log(2.0d0) &
- & ) &
- & + 4.0d0*radiusScaleFree**2*(+1.0d0+radiusScaleFree) &
- & *( &
- & + log( radiusScaleFree) &
- & -5.0d0*log(+1.0d0+radiusScaleFree) &
- & ) &
- & ) &
- & /( &
- & + radiusScaleFree**2*(+1.0d0+radiusScaleFree) &
- & ) &
- & ) &
- & ) &
- & /5.0d0
- end if
- zhao1996RadialVelocityDispersion=+sqrt( &
- & + gravitationalConstantGalacticus &
- & *self%normalization (node) &
- & * radiusScale **2 &
- & * velocityDispersionSquaredScaleFree &
- & )
- case default
- zhao1996RadialVelocityDispersion=+0.0d0
- call Error_Report('unknown special case'//{introspection:location})
- end select
- return
- end function zhao1996RadialVelocityDispersion
-
- double precision function zhao1996RadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily
- specificAngularMomentum} (given in units of km s$^{-1}$ Mpc)
- !!}
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- zhao1996RadiusFromSpecificAngularMomentum=self%radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- return
- end function zhao1996RadiusFromSpecificAngularMomentum
-
- double precision function zhao1996RotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision :: radiusVirial
-
- radiusVirial =+self%darkMatterHaloScale_%radiusVirial(node )
- zhao1996RotationNormalization=+self %radialMoment(node,moment=2.0d0,radiusMaximum=radiusVirial) &
- & /self %radialMoment(node,moment=3.0d0,radiusMaximum=radiusVirial)
- return
- end function zhao1996RotationNormalization
-
- double precision function zhao1996Energy(self,node)
- !!{
- Return the energy of an Zhao1996 halo density profile.
- !!}
- implicit none
class(darkMatterProfileDMOZhao1996), intent(inout) :: self
type (treeNode ), intent(inout) :: node
+ class(nodeComponentBasic ), pointer :: basic
- zhao1996Energy=self%energyNumerical(node)
- return
- end function zhao1996Energy
-
- double precision function zhao1996KSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the Zhao1996 density profile at the specified {\normalfont \ttfamily waveNumber} (given in Mpc$^{-1}$), using the
- expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- zhao1996KSpace=self%kSpaceNumerical(node,wavenumber)
- return
- end function zhao1996KSpace
-
- double precision function zhao1996FreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the Zhao1996 density profile at the specified {\normalfont \ttfamily time} (given in Gyr).
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-3
- type (rootFinder ) :: finder
-
- self_ => self
- node_ => node
- time_ = time
- finder = rootFinder( &
- & rootFunction =rootRadiusFreefall , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
- & )
- zhao1996FreefallRadius=finder%find(rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- return
- end function zhao1996FreefallRadius
-
- double precision function rootRadiusFreefall(radiusFreefall)
- !!{
- Root function used in finding the radius corresponding to a given freefall time.
- !!}
- use :: Numerical_Integration, only : integrator
- implicit none
- double precision , intent(in ) :: radiusFreefall
- type (integrator) :: integrator_
-
- radiusFreefall_ =+radiusFreefall
- integrator_ = integrator (integrandTimeFreefall,toleranceRelative=1.0d-3)
- rootRadiusFreefall=+integrator_ %integrate(0.0d0 ,radiusFreefall ) &
- & -time_
- return
- end function rootRadiusFreefall
-
- double precision function integrandTimeFreefall(radius)
- !!{
- Integrand for freefall time in the halo.
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- double precision, intent(in ) :: radius
- double precision :: potentialDifference
-
- potentialDifference=-self_%potential(node_,radiusFreefall_) &
- & +self_%potential(node_,radius )
- if (potentialDifference < 0.0d0) then
- integrandTimeFreefall=+Mpc_per_km_per_s_To_Gyr &
- & /sqrt( &
- & -2.0d0 &
- & *potentialDifference &
- & )
- else
- ! Avoid floating point errors arising from rounding errors.
- integrandTimeFreefall=0.0d0
- end if
- return
- end function integrandTimeFreefall
-
- double precision function zhao1996FreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the Zhao1996 density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- use :: Numerical_Differentiation, only : differentiator
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- double precision , parameter :: timeLogarithmicStep=0.1d0
- type (differentiator ) :: differentiator_
-
- self_ => self
- node_ => node
- differentiator_ = differentiator (freefallRadiusEvaluate )
- zhao1996FreefallRadiusIncreaseRate = +differentiator_%derivative(log(time) ,timeLogarithmicStep) &
- & / time
- return
- end function zhao1996FreefallRadiusIncreaseRate
-
- double precision function freefallRadiusEvaluate(timeLogarithmic)
- !!{
- GSL-callable function to evaluate the freefall radius of the dark matter profile.
- !!}
- implicit none
- double precision, intent(in ), value :: timeLogarithmic
-
- freefallRadiusEvaluate=self_%freefallRadiusNumerical(node_,exp(timeLogarithmic))
- return
- end function freefallRadiusEvaluate
-
- double precision function zhao1996RadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Error, only : Error_Report
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- double precision :: radiusScale , radiusScaleFree , &
- & radialMomentUpper, radialMomentLower, &
- & alpha , beta , &
- & gamma
-
- call self%exponents(node,alpha,beta,gamma)
- radiusScale = self %scaleRadius (node)
- if (present(radiusMinimum)) then
- radiusScaleFree =+radiusMinimum &
- & /radiusScale
- radialMomentLower= radialMomentIndefinite(radiusScaleFree)
- else
- radialMomentLower=0.0d0
- if (alpha <= 0.0d0 .or. 1.0d0+moment <= gamma) call Error_Report('radial moment is undefined'//{introspection:location})
- end if
- if (present(radiusMaximum)) then
- radiusScaleFree =+radiusMaximum &
- & /radiusScale
- radialMomentUpper= radialMomentIndefinite(radiusScaleFree)
- else
- radialMomentUpper=0.0d0
- call Error_Report('radial moment is not implemented'//{introspection:location})
- end if
- zhao1996RadialMoment=+( &
- & +radialMomentUpper &
- & -radialMomentLower &
- & ) &
- & *self%normalization(node) &
- & *radiusScale**(moment+1.0d0)
+ basic => node %basic()
+ zhao1996Normalization = basic%mass ()
return
-
- contains
-
- double precision function radialMomentIndefinite(radiusScaleFree)
- !!{
- Compute the indefinite radial moment.
- !!}
- use :: Hypergeometric_Functions, only : Hypergeometric_2F1
- implicit none
- double precision, intent(in ) :: radiusScaleFree
-
- radialMomentIndefinite=+radiusScaleFree** (1.0d0+moment-gamma) &
- & / (1.0d0+moment-gamma) &
- & *Hypergeometric_2F1([(1.0d0+moment-gamma)/alpha,(beta-gamma)/alpha],[1.0d0+(1.0d0+moment-gamma)/alpha],-radiusScaleFree**alpha)
- return
- end function radialMomentIndefinite
-
- end function zhao1996RadialMoment
-
- double precision function zhao1996RadiusEnclosingDensity(self,node,density)
- !!{
- Compute the radius enclosing a given density for {\normalfont \ttfamily Zhao1996} dark matter halo profiles.
- !!}
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- zhao1996RadiusEnclosingDensity=self%radiusEnclosingDensityNumerical(node,density)
- return
- end function zhao1996RadiusEnclosingDensity
-
- double precision function zhao1996RadiusEnclosingMass(self,node,mass)
- !!{
- Compute the radius enclosing a given mass for {\normalfont \ttfamily Zhao1996} dark matter halo profiles.
- !!}
- implicit none
- class (darkMatterProfileDMOZhao1996), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- zhao1996RadiusEnclosingMass=self%radiusEnclosingMassNumerical(node,mass)
- return
- end function zhao1996RadiusEnclosingMass
+ end function zhao1996Normalization
diff --git a/source/dark_matter_profiles_DMO.accelerator.F90 b/source/dark_matter_profiles_DMO.accelerator.F90
index b2f350f613..45118438c0 100644
--- a/source/dark_matter_profiles_DMO.accelerator.F90
+++ b/source/dark_matter_profiles_DMO.accelerator.F90
@@ -21,9 +21,6 @@
An accelerator class for dark matter halo profiles.
!!}
- use :: Binary_Search_Trees, only : binaryTree
- use :: Kind_Numbers , only : kind_int8
-
!![
@@ -36,38 +33,11 @@
An accelerator class for the dark matter halo profile class.
!!}
private
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- integer (kind_int8 ), dimension(2) :: uniqueIDPrevious
- type (binaryTree ), dimension(2) :: treeMassEnclosed
- integer :: treePrevious
- double precision :: toleranceRelative , factorRadiusMaximum, &
- & factorRadiusLogarithmicMaximum
+ class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ double precision :: toleranceRelative , factorRadiusMaximum
contains
- !![
-
-
-
- !!]
- final :: acceleratorDestructor
- procedure :: autoHook => acceleratorAutoHook
- procedure :: calculationReset => acceleratorCalculationReset
- procedure :: density => acceleratorDensity
- procedure :: densityLogSlope => acceleratorDensityLogSlope
- procedure :: radiusEnclosingDensity => acceleratorRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => acceleratorRadiusEnclosingMass
- procedure :: radialMoment => acceleratorRadialMoment
- procedure :: enclosedMass => acceleratorEnclosedMass
- procedure :: potential => acceleratorPotential
- procedure :: circularVelocity => acceleratorCircularVelocity
- procedure :: radiusCircularVelocityMaximum => acceleratorRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => acceleratorCircularVelocityMaximum
- procedure :: radialVelocityDispersion => acceleratorRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => acceleratorRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => acceleratorRotationNormalization
- procedure :: energy => acceleratorEnergy
- procedure :: kSpace => acceleratorKSpace
- procedure :: freefallRadius => acceleratorFreefallRadius
- procedure :: freefallRadiusIncreaseRate => acceleratorFreefallRadiusIncreaseRate
+ final :: acceleratorDestructor
+ procedure :: get => acceleratorGet
end type darkMatterProfileDMOAccelerator
interface darkMatterProfileDMOAccelerator
@@ -108,7 +78,7 @@ function acceleratorConstructorParameters(parameters) result(self)
self=darkMatterProfileDMOAccelerator(toleranceRelative,factorRadiusMaximum,darkMatterProfileDMO_)
!![
-
+
!!]
return
end function acceleratorConstructorParameters
@@ -125,343 +95,84 @@ function acceleratorConstructorInternal(toleranceRelative,factorRadiusMaximum,da
!!]
- self%uniqueIDPrevious =-1_kind_int8
- self%treePrevious =+1
- self%factorRadiusLogarithmicMaximum=+log(sqrt(factorRadiusMaximum))
return
end function acceleratorConstructorInternal
- subroutine acceleratorAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOAccelerator), intent(inout) :: self
-
- call calculationResetEvent%attach(self,acceleratorCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOAccelerator')
- return
- end subroutine acceleratorAutoHook
-
subroutine acceleratorDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily accelerator} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOAccelerator), intent(inout) :: self
!![
!!]
- if (calculationResetEvent%isAttached(self,acceleratorCalculationReset)) call calculationResetEvent%detach(self,acceleratorCalculationReset)
return
end subroutine acceleratorDestructor
- subroutine acceleratorCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- integer :: i
-
- ! Trees are maintained for two nodes - this is often advantageous as queries are often made for satellite and host nodes
- ! together. If the current node is one for which we currently have a tree, invalidate that tree. Otherwise, if the current
- ! node is a satellite in the current host node, place it into the second tree, otherwise, into the first.
- if (uniqueID == self%uniqueIDPrevious(1)) then
- i =+1
- else if (uniqueID == self%uniqueIDPrevious(2)) then
- i =+2
- else
- if (node%isSatellite() .and. node%parent%uniqueID() == self%uniqueIDPrevious(1)) then
- i=2
- else
- i=1
- end if
- end if
- self%uniqueIDPrevious(i)=uniqueID
- self%treePrevious =i
- if (associated(self%treeMassEnclosed(i)%root)) deallocate(self%treeMassEnclosed(i)%root)
- return
- end subroutine acceleratorCalculationReset
-
- double precision function acceleratorDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorDensity=self%darkMatterProfileDMO_%density(node,radius)
- return
- end function acceleratorDensity
-
- double precision function acceleratorDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope(node,radius)
- return
- end function acceleratorDensityLogSlope
-
- double precision function acceleratorRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- acceleratorRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity(node,density)
- return
- end function acceleratorRadiusEnclosingDensity
-
- double precision function acceleratorRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- acceleratorRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass(node,mass)
- return
- end function acceleratorRadiusEnclosingMass
-
- double precision function acceleratorRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- acceleratorRadialMoment=self%darkMatterProfileDMO_%radialMoment(node,moment,radiusMinimum,radiusMaximum)
- return
- end function acceleratorRadialMoment
-
- double precision function acceleratorEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Binary_Search_Trees, only : binaryTreeNode
- use :: Numerical_Comparison, only : Values_Agree
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (binaryTreeNode ), pointer :: left1 , left2 , &
- & right1 , right2
- double precision :: massEnclosed1 , massEnclosed2, &
- & radiusLogarithmic
- logical :: found
- integer :: i
-
- if (node%uniqueID() == self%uniqueIDPrevious(1)) then
- i=1
- else if (node%uniqueID() == self%uniqueIDPrevious(2)) then
- i=2
- else
- call self%calculationReset(node,node%uniqueID())
- i=self%treePrevious
- end if
- found=.false.
- radiusLogarithmic=log(radius)
- call self%treeMassEnclosed(i)%bracket(radiusLogarithmic,left1,right1)
- if (associated(left1).and.associated(right1)) then
- if (associated(left1,right1)) then
- acceleratorEnclosedMass=exp(left1%value)
- found =.true.
- else
- if ( &
- & +radiusLogarithmic- left1%key < self%factorRadiusLogarithmicMaximum &
- & .and. &
- & -radiusLogarithmic+right1%key < self%factorRadiusLogarithmicMaximum &
- & ) then
- left2 => left1%predecessor()
- right2 => right1% successor()
- if (associated(left2).and.associated(right2)) then
- massEnclosed1=(radiusLogarithmic-left1%key)*(right1%value-left1%value)/(right1%key-left1%key)+left1%value
- massEnclosed2=(radiusLogarithmic-left2%key)*(right2%value-left2%value)/(right2%key-left2%key)+left2%value
- if (Values_Agree(massEnclosed1,massEnclosed2,relTol=self%toleranceRelative)) then
- acceleratorEnclosedMass=exp(massEnclosed1)
- found =.true.
- end if
- end if
- end if
- end if
- end if
- if (.not.found) then
- acceleratorEnclosedMass=self%darkMatterProfileDMO_%enclosedMass(node,radius)
- call self%treeMassEnclosed(i)%insert(radiusLogarithmic,log(acceleratorEnclosedMass))
- end if
- return
- end function acceleratorEnclosedMass
-
- double precision function acceleratorPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- acceleratorPotential=self%darkMatterProfileDMO_%potential(node,radius,status)
- return
- end function acceleratorPotential
-
- double precision function acceleratorCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorCircularVelocity=self%darkMatterProfileDMO_%circularVelocity(node,radius)
- return
- end function acceleratorCircularVelocity
-
- double precision function acceleratorRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum(node)
- return
- end function acceleratorRadiusCircularVelocityMaximum
-
- double precision function acceleratorCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum(node)
- return
- end function acceleratorCircularVelocityMaximum
-
- double precision function acceleratorRadialVelocityDispersion(self,node,radius)
+ function acceleratorGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalAccelerator, kinematicsDistributionCollisionless, massDistributionSpherical, nonAnalyticSolversNumerical
implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- acceleratorRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- return
- end function acceleratorRadialVelocityDispersion
-
- double precision function acceleratorRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- acceleratorRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum(node,specificAngularMomentum)
- return
- end function acceleratorRadiusFromSpecificAngularMomentum
-
- double precision function acceleratorRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization(node)
- return
- end function acceleratorRotationNormalization
-
- double precision function acceleratorEnergy(self,node)
- !!{
- Return the energy of a accelerator halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- acceleratorEnergy=self%darkMatterProfileDMO_%energy(node)
- return
- end function acceleratorEnergy
-
- double precision function acceleratorKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the accelerator density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- acceleratorKSpace=self%darkMatterProfileDMO_%kSpace(node,waveNumber)
- return
- end function acceleratorKSpace
-
- double precision function acceleratorFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the accelerator density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- acceleratorFreefallRadius=self%darkMatterProfileDMO_%freefallRadius(node,time)
- return
- end function acceleratorFreefallRadius
-
- double precision function acceleratorFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the accelerator density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOAccelerator), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOAccelerator ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ !![
+
+ !!]
- acceleratorFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate(node,time)
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalAccelerator :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAccelerator)
+ massDistributionDecorated => self%darkMatterProfileDMO_%get(node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalAccelerator( &
+ & toleranceRelative =self%toleranceRelative , &
+ & factorRadiusMaximum=self%factorRadiusMaximum , &
+ & massDistribution_ = massDistributionDecorated , &
+ & nonAnalyticSolver = nonAnalyticSolversNumerical, &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function acceleratorFreefallRadiusIncreaseRate
+ end function acceleratorGet
diff --git a/source/dark_matter_profiles_DMO.accretion_flow.DiemerKravtsov2014.F90 b/source/dark_matter_profiles_DMO.accretion_flow.DiemerKravtsov2014.F90
new file mode 100644
index 0000000000..0789b1a707
--- /dev/null
+++ b/source/dark_matter_profiles_DMO.accretion_flow.DiemerKravtsov2014.F90
@@ -0,0 +1,294 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An implementation of a dark matter density profile which includes the accretion flow surrounding the halo.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmological_Density_Field, only : cosmologicalMassVarianceClass, criticalOverdensityClass
+
+ !![
+
+
+ An accretion flow class which models the accretion flow using the fitting function of
+ \cite{diemer_dependence_2014}. Specifically, \refClass{massDistributionDiemerKravtsov2014} objects are built with
+ parameters chosen using fits to the redshift and $\nu$ dependencies of the fitting parameters $b_\mathrm{e}$ and
+ $s_\mathrm{e}$ chosen to match the results of their figure~18.
+
+
+ !!]
+ type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOAccretionFlowDiemerKravtsov2014
+ !!{
+ A dark matter halo profile class which implements a dark matter density profile which includes the accretion flow using the
+ fitting function of \cite{diemer_dependence_2014}.
+ !!}
+ private
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
+ class (cosmologicalMassVarianceClass), pointer :: cosmologicalMassVariance_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ double precision :: b0 , s0 , &
+ & bz , sz , &
+ & bnu , snu
+ contains
+ final :: accretionFlowDiemerKravtsov2014Destructor
+ procedure :: get => accretionFlowDiemerKravtsov2014Get
+ end type darkMatterProfileDMOAccretionFlowDiemerKravtsov2014
+
+ interface darkMatterProfileDMOAccretionFlowDiemerKravtsov2014
+ !!{
+ Constructors for the {\normalfont \ttfamily accretionFlowDiemerKravtsov2014} dark matter halo profile class.
+ !!}
+ module procedure accretionFlowDiemerKravtsov2014ConstructorParameters
+ module procedure accretionFlowDiemerKravtsov2014ConstructorInternal
+ end interface darkMatterProfileDMOAccretionFlowDiemerKravtsov2014
+
+contains
+
+ function accretionFlowDiemerKravtsov2014ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily accretionFlowDiemerKravtsov2014} dark matter halo profile class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowDiemerKravtsov2014) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
+ double precision :: b0 , s0 , &
+ & bz , sz , &
+ & bnu , snu
+
+ !![
+
+ b0
+
+ +1.1250d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $b_0$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+ bz
+
+ +0.625d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $b_z$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+ bnu
+
+ -0.2250d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $b_\nu$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+ s0
+
+ +1.3925d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $s_0$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+ sz
+
+ -0.199d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $s_z$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+ snu
+
+ +0.0875d0
+ Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
+ The parameter $s_\nu$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
+
+
+
+
+
+ !!]
+ self=darkMatterProfileDMOAccretionFlowDiemerKravtsov2014(b0,bz,bnu,s0,sz,snu,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_)
+ !![
+
+
+
+
+
+ !!]
+ return
+ end function accretionFlowDiemerKravtsov2014ConstructorParameters
+
+ function accretionFlowDiemerKravtsov2014ConstructorInternal(b0,bz,bnu,s0,sz,snu,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily accretionFlowDiemerKravtsov2014} dark matter profile class.
+ !!}
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowDiemerKravtsov2014) :: self
+ class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
+ double precision , intent(in ) :: b0 , s0 , &
+ & bz , sz , &
+ & bnu , snu
+ !![
+
+ !!]
+
+ return
+ end function accretionFlowDiemerKravtsov2014ConstructorInternal
+
+ subroutine accretionFlowDiemerKravtsov2014Destructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily accretionFlowDiemerKravtsov2014} dark matter profile class.
+ !!}
+ implicit none
+ type(darkMatterProfileDMOAccretionFlowDiemerKravtsov2014), intent(inout) :: self
+
+ !![
+
+
+
+
+ !!]
+ return
+ end subroutine accretionFlowDiemerKravtsov2014Destructor
+
+ function accretionFlowDiemerKravtsov2014Get(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSpherical , massDistributionSphericalAccretionFlow, massDistributionDiemerKravtsov2014, kinematicsDistributionCollisionless, &
+ & nonAnalyticSolversNumerical
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (massDistributionClass ), pointer :: massDistributionAccretionFlow_, massDistributionVirialized_
+ type (kinematicsDistributionCollisionless ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOAccretionFlowDiemerKravtsov2014), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ double precision :: time , mass , &
+ & radius200Mean , densityMean , &
+ & nu , redshift , &
+ & b , s , &
+ & peakHeight , radiusTransition
+ !![
+
+ !!]
+
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get the virialized mass distribution.
+ massDistributionVirialized_ => self%darkMatterProfileDMO_%get(node)
+ select type (massDistributionVirialized_)
+ class is (massDistributionSpherical)
+ ! Create the accretion flow mass distribution.
+ allocate(massDistributionDiemerKravtsov2014 :: massDistributionAccretionFlow_)
+ select type(massDistributionAccretionFlow_)
+ type is (massDistributionDiemerKravtsov2014)
+ ! Extract basic quantities for the halo.
+ basic => node %basic()
+ time = basic%time ()
+ mass = basic%mass ()
+ ! Evaluate the control parameters.
+ redshift=+self%cosmologyFunctions_ %redshiftFromExpansionFactor( &
+ & self%cosmologyFunctions_%expansionFactor (time=time ) &
+ & )
+ nu =+self%criticalOverdensity_ %value (time=time,mass=mass,node=node) &
+ & /self%cosmologicalMassVariance_%rootVariance (time=time,mass=mass )
+ ! Evaluate the parameters of the fitting function. These fits were derived by Andrew Benson by constructing simple functional
+ ! forms which fit the plots in figure 18 of Diemer & Kravtsov (2014). There is no guarantee that these fits will perform
+ ! sensibly outside the range of that plot (and, of course, they are only approximate even within the range of that plot).
+ b=+self%b0*(1.0+redshift)**self%bz*nu**self%bnu
+ s=+self%s0*(1.0+redshift)**self%sz*nu**self%snu
+ ! Find the radius enclosing 200 times the mean density.
+ densityMean = self %cosmologyFunctions_ %matterDensityEpochal ( time )
+ radius200Mean = massDistributionVirialized_ %radiusEnclosingDensity(+200.0d0*densityMean)
+ ! Construct the accretion flow mass distribution. Note that we do not include the background density of the universe
+ ! here, as (being uniform) it should have no effect on halo dynamics.
+ !![
+
+
+ massDistributionDiemerKravtsov2014( &
+ & densityMean =densityMean , &
+ & radius200Mean=radius200Mean , &
+ & includeMean =.false. , &
+ & b =b , &
+ & s =s , &
+ & componentType=componentTypeDarkHalo, &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ ! Combine the virialized and accretion flow mass distributions.
+ allocate(massDistributionSphericalAccretionFlow :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAccretionFlow)
+ ! Compute the transition radius following Diemer & Kravtsov (2014; equation 6).
+ peakHeight =+self%criticalOverdensity_ %value (time=time,mass=mass) &
+ & /self%cosmologicalMassVariance_%rootVariance(time=time,mass=mass)
+ radiusTransition=+( &
+ & +1.90d0 &
+ & -0.18d0 &
+ & *peakHeight &
+ & ) &
+ & *radius200Mean
+ !![
+
+
+ massDistributionSphericalAccretionFlow( &
+ & radiusTransition =radiusTransition , &
+ & nonAnalyticSolver =nonAnalyticSolversNumerical , &
+ & massDistribution_ =massDistributionVirialized_ , &
+ & massDistributionAccretionFlow_=massDistributionAccretionFlow_, &
+ & componentType =componentTypeDarkHalo , &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ end select
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless()
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+
+
+ !!]
+ return
+ end function accretionFlowDiemerKravtsov2014Get
diff --git a/source/dark_matter_profiles_DMO.accretion_flow.F90 b/source/dark_matter_profiles_DMO.accretion_flow.F90
deleted file mode 100644
index f4cbb4698e..0000000000
--- a/source/dark_matter_profiles_DMO.accretion_flow.F90
+++ /dev/null
@@ -1,717 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An implementation of a dark matter density profile which includes the accretion flow surrounding the halo.
- !!}
-
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmological_Density_Field, only : cosmologicalMassVarianceClass, criticalOverdensityClass
-
- !![
-
-
- An implementation of a dark matter density profile which includes the accretion flow surrounding the halo. The density
- profile is modeled as
- \begin{equation}
- \rho(r) = f_\mathrm{trans}(r) \rho_\mathrm{halo}(r) + \rho_\mathrm{accretion}(r),
- \end{equation}
- where $\rho_\mathrm{halo}(r)$ is the halo density profile (provided by a \refClass{darkMatterProfileDMOClass} object) and
- $\rho_\mathrm{accretion}(r)$ is the accretion flow density profile (provided by a \refClass{accretionFlowsClass} object),
- and $f_\mathrm{trans}(r)$ is the transition function as defined by equation~(7) of \cite{diemer_dependence_2014}.
-
- Note that some \refClass{accretionFlowsClass} objects make use of an \refClass{darkMatterProfileClass} object. As such, to
- avoid an infinite recursive loop, it is recommended to use an explicit, separate \refClass{darkMatterProfileClass} for the
- relevant \refClass{accretionFlowsClass} object when using this implementation.
-
-
- !!]
- type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOAccretionFlow
- !!{
- A dark matter halo profile class which implements a dark matter density profile which includes the accretion flow surrounding the halo.
- !!}
- private
- class(cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class(criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
- class(cosmologicalMassVarianceClass), pointer :: cosmologicalMassVariance_ => null()
- class(* ), pointer :: accretionFlows_ => null()
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- contains
- !![
-
-
-
- !!]
- final :: accretionFlowDestructor
- procedure :: autoHook => accretionFlowAutoHook
- procedure :: calculationReset => accretionFlowCalculationReset
- procedure :: density => accretionFlowDensity
- procedure :: densityLogSlope => accretionFlowDensityLogSlope
- procedure :: radiusEnclosingDensity => accretionFlowRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => accretionFlowRadiusEnclosingMass
- procedure :: radialMoment => accretionFlowRadialMoment
- procedure :: enclosedMass => accretionFlowEnclosedMass
- procedure :: potential => accretionFlowPotential
- procedure :: circularVelocity => accretionFlowCircularVelocity
- procedure :: radiusCircularVelocityMaximum => accretionFlowRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => accretionFlowCircularVelocityMaximum
- procedure :: radialVelocityDispersion => accretionFlowRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => accretionFlowRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => accretionFlowRotationNormalization
- procedure :: energy => accretionFlowEnergy
- procedure :: kSpace => accretionFlowKSpace
- procedure :: freefallRadius => accretionFlowFreefallRadius
- procedure :: freefallRadiusIncreaseRate => accretionFlowFreefallRadiusIncreaseRate
- procedure :: deepCopy => accretionFlowDeepCopy
- procedure :: deepCopyReset => accretionFlowDeepCopyReset
- procedure :: deepCopyFinalize => accretionFlowDeepCopyFinalize
- end type darkMatterProfileDMOAccretionFlow
-
- interface darkMatterProfileDMOAccretionFlow
- !!{
- Constructors for the {\normalfont \ttfamily accretionFlow} dark matter halo profile class.
- !!}
- module procedure accretionFlowConstructorParameters
- module procedure accretionFlowConstructorInternal
- end interface darkMatterProfileDMOAccretionFlow
-
-contains
-
- function accretionFlowConstructorParameters(parameters) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily accretionFlow} dark matter halo profile class which takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameter , inputParameters
- use :: Functions_Global, only : accretionFlowsConstruct_, accretionFlowsDestruct_
- implicit none
- type (darkMatterProfileDMOAccretionFlow) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (* ), pointer :: accretionFlows_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (criticalOverdensityClass ), pointer :: criticalOverdensity_
- class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
- double precision :: toleranceRelativePotential
-
- !![
-
- toleranceRelativePotential
- 1.0d-6
-
- The relative tolerance to use in numerical solutions for the potential in dark-matter-only density profiles.
-
-
-
-
-
-
-
- !!]
- call accretionFlowsConstruct_(parameters,accretionFlows_)
- self=darkMatterProfileDMOAccretionFlow(toleranceRelativePotential,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,accretionFlows_,darkMatterProfileDMO_,darkMatterHaloScale_)
- !![
-
-
-
-
-
-
-
-
- !!]
- if (associated(accretionFlows_)) call accretionFlowsDestruct_(accretionFlows_)
- return
- end function accretionFlowConstructorParameters
-
- function accretionFlowConstructorInternal(toleranceRelativePotential,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,accretionFlows_,darkMatterProfileDMO_,darkMatterHaloScale_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily accretionFlow} dark matter profile class.
- !!}
- implicit none
- type (darkMatterProfileDMOAccretionFlow) :: self
- class (* ), intent(in ), target :: accretionFlows_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
- class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
- double precision , intent(in ) :: toleranceRelativePotential
- !![
-
- !!]
-
- return
- end function accretionFlowConstructorInternal
-
- subroutine accretionFlowAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
-
- call calculationResetEvent%attach(self,accretionFlowCalculationReset,openMPThreadBindingAllLevels)
- return
- end subroutine accretionFlowAutoHook
-
- subroutine accretionFlowDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily accretionFlow} dark matter halo profile class.
- !!}
- use :: Functions_Global, only : accretionFlowsDestruct_
- implicit none
- type(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
-
- !![
-
-
-
-
-
-
- !!]
- if (associated(self%accretionFlows_)) call accretionFlowsDestruct_(self%accretionFlows_)
- return
- end subroutine accretionFlowDestructor
-
- subroutine accretionFlowCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%genericLastUniqueID =uniqueID
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine accretionFlowCalculationReset
-
- double precision function accretionFlowDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Functions_Global, only : accretionFlowsDensity_
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- double precision :: radius200Mean , density200Mean , &
- & radiusTransition, fractionTransition, &
- & peakHeight
-
- if (node%isSatellite()) then
- ! No accretion flow for satellites.
- accretionFlowDensity = +self%darkMatterProfileDMO_%density(node,radius)
- else
- ! Include accretion flow for non-satellites
- basic => node%basic()
- peakHeight = +self%criticalOverdensity_ %value (time=basic%time(),mass=basic%mass() ) &
- & /self%cosmologicalMassVariance_%rootVariance (time=basic%time(),mass=basic%mass() )
- density200Mean = +200.0d0 &
- & *self%cosmologyFunctions_ %matterDensityEpochal (time=basic%time() )
- radius200Mean = +self%darkMatterProfileDMO_ %radiusEnclosingDensity( node , density200Mean)
- radiusTransition = +( &
- & +1.90d0 &
- & -0.18d0 &
- & *peakHeight &
- & ) &
- & *radius200Mean
- fractionTransition = +1.0d0 &
- & /( &
- & +1.0d0 &
- & +( &
- & +radius &
- & /radiusTransition &
- & )**4 &
- & )**2
- accretionFlowDensity = +self%darkMatterProfileDMO_%density ( node,radius) &
- & * fractionTransition &
- & +accretionFlowsDensity_ (self%accretionFlows_,node,radius)
- end if
- return
- end function accretionFlowDensity
-
- double precision function accretionFlowDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily
- node} at the given {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- accretionFlowDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope (node,radius)
- else
- accretionFlowDensityLogSlope=self %densityLogSlopeNumerical(node,radius)
- end if
- return
- end function accretionFlowDensityLogSlope
-
- double precision function accretionFlowRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (node%isSatellite()) then
- accretionFlowRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- accretionFlowRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function accretionFlowRadiusEnclosingDensity
-
- double precision function accretionFlowRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- if (node%isSatellite()) then
- accretionFlowRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- accretionFlowRadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function accretionFlowRadiusEnclosingMass
-
- double precision function accretionFlowRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the radial moment of the density profile.
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (node%isSatellite()) then
- accretionFlowRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- accretionFlowRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function accretionFlowRadialMoment
-
- double precision function accretionFlowEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- accretionFlowEnclosedMass=self%darkMatterProfileDMO_%enclosedMass (node,radius)
- else
- accretionFlowEnclosedMass=self %enclosedMassNumerical(node,radius)
- end if
- return
- end function accretionFlowEnclosedMass
-
- double precision function accretionFlowPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- if (node%isSatellite()) then
- accretionFlowPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- accretionFlowPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function accretionFlowPotential
-
- double precision function accretionFlowCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- accretionFlowCircularVelocity=sqrt( &
- & +gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & / radius &
- & )
- else
- accretionFlowCircularVelocity=0.0d0
- end if
- return
- end function accretionFlowCircularVelocity
-
- double precision function accretionFlowRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- accretionFlowRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- accretionFlowRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function accretionFlowRadiusCircularVelocityMaximum
-
- double precision function accretionFlowCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- accretionFlowCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- accretionFlowCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function accretionFlowCircularVelocityMaximum
-
- double precision function accretionFlowRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- accretionFlowRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion (node,radius)
- else
- accretionFlowRadialVelocityDispersion=self %radialVelocityDispersionNumerical(node,radius)
- end if
- return
- end function accretionFlowRadialVelocityDispersion
-
- double precision function accretionFlowRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (node%isSatellite()) then
- accretionFlowRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- accretionFlowRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function accretionFlowRadiusFromSpecificAngularMomentum
-
- double precision function accretionFlowRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision :: radiusVirial
-
- radiusVirial =+self%darkMatterHaloScale_%radiusVirial(node )
- accretionFlowRotationNormalization=+self %radialMoment(node,moment=2.0d0,radiusMinimum=0.0d0,radiusMaximum=radiusVirial) &
- & /self %radialMoment(node,moment=3.0d0,radiusMinimum=0.0d0,radiusMaximum=radiusVirial)
- return
- end function accretionFlowRotationNormalization
-
- double precision function accretionFlowEnergy(self,node)
- !!{
- Return the energy of a accretionFlow halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- accretionFlowEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- accretionFlowEnergy=self %energyNumerical(node)
- end if
- return
- end function accretionFlowEnergy
-
- double precision function accretionFlowKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the accretionFlow density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$), using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (node%isSatellite()) then
- accretionFlowKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- accretionFlowKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function accretionFlowKSpace
-
- double precision function accretionFlowFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the accretionFlow density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (node%isSatellite()) then
- accretionFlowFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- accretionFlowFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function accretionFlowFreefallRadius
-
- double precision function accretionFlowFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the accretionFlow density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOAccretionFlow), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (node%isSatellite()) then
- accretionFlowFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- accretionFlowFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
- return
- end function accretionFlowFreefallRadiusIncreaseRate
-
- subroutine accretionFlowDeepCopyReset(self)
- !!{
- Perform a deep copy reset of the object.
- !!}
- use :: Functions_Global, only : accretionFlowsDeepCopyReset_
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
-
- self%copiedSelf => null()
- if (associated(self%cosmologyParameters_ )) call self%cosmologyParameters_ %deepCopyReset()
- if (associated(self%cosmologyFunctions_ )) call self%cosmologyFunctions_ %deepCopyReset()
- if (associated(self%criticalOverdensity_ )) call self%criticalOverdensity_ %deepCopyReset()
- if (associated(self%cosmologicalMassVariance_)) call self%cosmologicalMassVariance_%deepCopyReset()
- if (associated(self%darkMatterHaloScale_ )) call self%darkMatterHaloScale_ %deepCopyReset()
- if (associated(self%darkMatterProfileDMO_ )) call self%darkMatterProfileDMO_ %deepCopyReset()
- if (associated(self%accretionFlows_ )) call accretionFlowsDeepCopyReset_(self%accretionFlows_)
- return
- end subroutine accretionFlowDeepCopyReset
-
- subroutine accretionFlowDeepCopyFinalize(self)
- !!{
- Finalize a deep reset of the object.
- !!}
- use :: Functions_Global, only : accretionFlowsDeepCopyFinalize_
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout) :: self
-
- if (associated(self%cosmologyParameters_ )) call self%cosmologyParameters_ %deepCopyFinalize()
- if (associated(self%cosmologyFunctions_ )) call self%cosmologyFunctions_ %deepCopyFinalize()
- if (associated(self%criticalOverdensity_ )) call self%criticalOverdensity_ %deepCopyFinalize()
- if (associated(self%cosmologicalMassVariance_)) call self%cosmologicalMassVariance_%deepCopyFinalize()
- if (associated(self%darkMatterHaloScale_ )) call self%darkMatterHaloScale_ %deepCopyFinalize()
- if (associated(self%darkMatterProfileDMO_ )) call self%darkMatterProfileDMO_ %deepCopyFinalize()
- if (associated(self%accretionFlows_ )) call accretionFlowsDeepCopyFinalize_(self%accretionFlows_)
- return
- end subroutine accretionFlowDeepCopyFinalize
-
- subroutine accretionFlowDeepCopy(self,destination)
- !!{
- Perform a deep copy of the object.
- !!}
- use :: Error , only : Error_Report
- use :: Functions_Global, only : accretionFlowsDeepCopy_
- implicit none
- class(darkMatterProfileDMOAccretionFlow), intent(inout), target :: self
- class(darkMatterProfileDMOClass ), intent(inout) :: destination
-
- call self%darkMatterProfileDMOClass%deepCopy(destination)
- select type (destination)
- type is (darkMatterProfileDMOAccretionFlow)
- nullify(destination%cosmologyParameters_)
- if (associated(self%cosmologyParameters_)) then
- if (associated(self%cosmologyParameters_%copiedSelf)) then
- select type(s => self%cosmologyParameters_%copiedSelf)
- class is (cosmologyParametersClass)
- destination%cosmologyParameters_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%cosmologyParameters_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%cosmologyParameters_,mold=self%cosmologyParameters_)
- call self%cosmologyParameters_%deepCopy(destination%cosmologyParameters_)
- self%cosmologyParameters_%copiedSelf => destination%cosmologyParameters_
- call destination%cosmologyParameters_%autoHook()
- end if
- end if
- nullify(destination%cosmologyFunctions_)
- if (associated(self%cosmologyFunctions_)) then
- if (associated(self%cosmologyFunctions_%copiedSelf)) then
- select type(s => self%cosmologyFunctions_%copiedSelf)
- class is (cosmologyFunctionsClass)
- destination%cosmologyFunctions_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%cosmologyFunctions_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%cosmologyFunctions_,mold=self%cosmologyFunctions_)
- call self%cosmologyFunctions_%deepCopy(destination%cosmologyFunctions_)
- self%cosmologyFunctions_%copiedSelf => destination%cosmologyFunctions_
- call destination%cosmologyFunctions_%autoHook()
- end if
- end if
- nullify(destination%criticalOverdensity_)
- if (associated(self%criticalOverdensity_)) then
- if (associated(self%criticalOverdensity_%copiedSelf)) then
- select type(s => self%criticalOverdensity_%copiedSelf)
- class is (criticalOverdensityClass)
- destination%criticalOverdensity_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%criticalOverdensity_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%criticalOverdensity_,mold=self%criticalOverdensity_)
- call self%criticalOverdensity_%deepCopy(destination%criticalOverdensity_)
- self%criticalOverdensity_%copiedSelf => destination%criticalOverdensity_
- call destination%criticalOverdensity_%autoHook()
- end if
- end if
- nullify(destination%cosmologicalMassVariance_)
- if (associated(self%cosmologicalMassVariance_)) then
- if (associated(self%cosmologicalMassVariance_%copiedSelf)) then
- select type(s => self%cosmologicalMassVariance_%copiedSelf)
- class is (cosmologicalMassVarianceClass)
- destination%cosmologicalMassVariance_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%cosmologicalMassVariance_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%cosmologicalMassVariance_,mold=self%cosmologicalMassVariance_)
- call self%cosmologicalMassVariance_%deepCopy(destination%cosmologicalMassVariance_)
- self%cosmologicalMassVariance_%copiedSelf => destination%cosmologicalMassVariance_
- call destination%cosmologicalMassVariance_%autoHook()
- end if
- end if
- nullify(destination%darkMatterHaloScale_)
- if (associated(self%darkMatterHaloScale_)) then
- if (associated(self%darkMatterHaloScale_%copiedSelf)) then
- select type(s => self%darkMatterHaloScale_%copiedSelf)
- class is (darkMatterHaloScaleClass)
- destination%darkMatterHaloScale_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%darkMatterHaloScale_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%darkMatterHaloScale_,mold=self%darkMatterHaloScale_)
- call self%darkMatterHaloScale_%deepCopy(destination%darkMatterHaloScale_)
- self%darkMatterHaloScale_%copiedSelf => destination%darkMatterHaloScale_
- call destination%darkMatterHaloScale_%autoHook()
- end if
- end if
- nullify(destination%darkMatterProfileDMO_)
- if (associated(self%darkMatterProfileDMO_)) then
- if (associated(self%darkMatterProfileDMO_%copiedSelf)) then
- select type(s => self%darkMatterProfileDMO_%copiedSelf)
- class is (darkMatterProfileDMOClass)
- destination%darkMatterProfileDMO_ => s
- class default
- call Error_Report('copiedSelf has incorrect type'//{introspection:location})
- end select
- call self%darkMatterProfileDMO_%copiedSelf%referenceCountIncrement()
- else
- allocate(destination%darkMatterProfileDMO_,mold=self%darkMatterProfileDMO_)
- call self%darkMatterProfileDMO_%deepCopy(destination%darkMatterProfileDMO_)
- self%darkMatterProfileDMO_%copiedSelf => destination%darkMatterProfileDMO_
- call destination%darkMatterProfileDMO_%autoHook()
- end if
- end if
- nullify(destination%accretionFlows_)
- if (associated(self%accretionFlows_)) then
- allocate(destination%accretionFlows_,mold=self%accretionFlows_)
- call accretionFlowsDeepCopy_(self%accretionFlows_,destination%accretionFlows_)
- end if
- class default
- call Error_Report('destination and source types do not match'//{introspection:location})
- end select
- return
- end subroutine accretionFlowDeepCopy
diff --git a/source/dark_matter_profiles_DMO.accretion_flow.Shi2016.F90 b/source/dark_matter_profiles_DMO.accretion_flow.Shi2016.F90
new file mode 100644
index 0000000000..124536d56d
--- /dev/null
+++ b/source/dark_matter_profiles_DMO.accretion_flow.Shi2016.F90
@@ -0,0 +1,262 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An implementation of a dark matter density profile which includes the accretion flow surrounding the halo.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmological_Density_Field , only : cosmologicalMassVarianceClass , criticalOverdensityClass
+ use :: Spherical_Collapse_Solvers , only : sphericalCollapseSolverClass
+ use :: Dark_Matter_Halo_Mass_Accretion_Histories, only : darkMatterHaloMassAccretionHistoryClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+
+ !![
+
+
+ A dark matter profile class which builds \refClass{massDistributionShi2016} objects to model accretion flows using the
+ model of \cite{shi_outer_2016}.
+
+
+ !!]
+ type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOAccretionFlowShi2016
+ !!{
+ A dark matter halo profile class which implements a dark matter density profile which includes the accretion flow using the
+ 2-halo correlation function.
+ !!}
+ private
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
+ class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
+ class (darkMatterHaloMassAccretionHistoryClass), pointer :: darkMatterHaloMassAccretionHistory_ => null()
+ class (sphericalCollapseSolverClass ), pointer :: sphericalCollapseSolver_ => null()
+ double precision :: scaleFactorVelocity
+ contains
+ final :: accretionFlowShi2016Destructor
+ procedure :: get => accretionFlowShi2016Get
+ end type darkMatterProfileDMOAccretionFlowShi2016
+
+ interface darkMatterProfileDMOAccretionFlowShi2016
+ !!{
+ Constructors for the {\normalfont \ttfamily accretionFlowShi2016} dark matter halo profile class.
+ !!}
+ module procedure accretionFlowShi2016ConstructorParameters
+ module procedure accretionFlowShi2016ConstructorInternal
+ end interface darkMatterProfileDMOAccretionFlowShi2016
+
+contains
+
+ function accretionFlowShi2016ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily accretionFlowShi2016} dark matter halo profile class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowShi2016) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class (sphericalCollapseSolverClass ), pointer :: sphericalCollapseSolver_
+ class (darkMatterHaloMassAccretionHistoryClass ), pointer :: darkMatterHaloMassAccretionHistory_
+ double precision :: scaleFactorVelocity
+
+ !![
+
+ scaleFactorVelocity
+
+ 1.0d0
+ A scale factor to be applied to inflow velocities.
+
+
+
+
+
+
+
+
+ !!]
+ self=darkMatterProfileDMOAccretionFlowShi2016(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterHaloMassAccretionHistory_,sphericalCollapseSolver_)
+ !![
+
+
+
+
+
+
+
+
+ !!]
+ return
+ end function accretionFlowShi2016ConstructorParameters
+
+ function accretionFlowShi2016ConstructorInternal(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterHaloMassAccretionHistory_,sphericalCollapseSolver_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily accretionFlowShi2016} dark matter profile class.
+ !!}
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowShi2016) :: self
+ class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class (darkMatterHaloMassAccretionHistoryClass ), intent(in ), target :: darkMatterHaloMassAccretionHistory_
+ class (sphericalCollapseSolverClass ), intent(in ), target :: sphericalCollapseSolver_
+ double precision , intent(in ) :: scaleFactorVelocity
+ !![
+
+ !!]
+
+ return
+ end function accretionFlowShi2016ConstructorInternal
+
+ subroutine accretionFlowShi2016Destructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily accretionFlowShi2016} dark matter profile class.
+ !!}
+ implicit none
+ type(darkMatterProfileDMOAccretionFlowShi2016), intent(inout) :: self
+
+ !![
+
+
+
+
+
+
+ !!]
+ return
+ end subroutine accretionFlowShi2016Destructor
+
+ function accretionFlowShi2016Get(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSpherical , massDistributionSphericalAccretionFlow, massDistributionShi2016, kinematicsDistributionShi2016, &
+ & nonAnalyticSolversNumerical
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Tables , only : table1D
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (massDistributionClass ), pointer :: massDistributionAccretionFlow_ , massDistributionVirialized_
+ type (kinematicsDistributionShi2016 ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOAccretionFlowShi2016), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ class (table1D ), allocatable :: ratioRadiusTurnaroundVirialTable
+ double precision :: time , mass , &
+ & densityMean , radius200Mean , &
+ & peakHeight , radiusTransition , &
+ & massAccretionRate , radiusVirial , &
+ & ratioRadiusTurnaroundVirial
+ !![
+
+ !!]
+
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Combine the virialized and accretion flow mass distributions.
+ allocate(massDistributionSphericalAccretionFlow :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAccretionFlow)
+ ! Get the virialized mass distribution.
+ massDistributionVirialized_ => self%darkMatterProfileDMO_%get(node)
+ select type (massDistributionVirialized_)
+ class is (massDistributionSpherical)
+ ! Find the radius enclosing 200 times the mean density.
+ densityMean = self %cosmologyFunctions_ %matterDensityEpochal ( time )
+ radius200Mean = massDistributionVirialized_ %radiusEnclosingDensity(+200.0d0*densityMean)
+ ! Compute the transition radius following Diemer & Kravtsov (2014; equation 6).
+ peakHeight =+self%criticalOverdensity_ %value (time=time,mass=mass) &
+ & /self%cosmologicalMassVariance_%rootVariance(time=time,mass=mass)
+ radiusTransition=+( &
+ & +1.90d0 &
+ & -0.18d0 &
+ & *peakHeight &
+ & ) &
+ & *radius200Mean
+ ! Create the accretion flow mass distribution.
+ allocate(massDistributionShi2016 :: massDistributionAccretionFlow_)
+ select type(massDistributionAccretionFlow_)
+ type is (massDistributionShi2016)
+ ! Extract basic quantities for the halo.
+ basic => node %basic ( )
+ time = basic %time ( )
+ mass = basic %mass ( )
+ radiusVirial = self %darkMatterHaloScale_ %radiusVirial (node )
+ massAccretionRate = self %darkMatterHaloMassAccretionHistory_%massAccretionRate(node,time)
+ call self%sphericalCollapseSolver_%radiusTurnaround(time,tableStore=.false.,radiusTurnaround_=ratioRadiusTurnaroundVirialTable)
+ ratioRadiusTurnaroundVirial=ratioRadiusTurnaroundVirialTable%interpolate(time)
+ !![
+
+
+ massDistributionShi2016( &
+ & mass =mass , &
+ & massAccretionRate =massAccretionRate , &
+ & radiusVirial =radiusVirial , &
+ & ratioRadiusTurnaroundVirial=ratioRadiusTurnaroundVirial, &
+ & time =time , &
+ & scaleFactorVelocity =self%scaleFactorVelocity , &
+ & cosmologyFunctions_ =self%cosmologyFunctions_ , &
+ & componentType =componentTypeDarkHalo , &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ !![
+
+
+ massDistributionSphericalAccretionFlow( &
+ & radiusTransition =radiusTransition , &
+ & nonAnalyticSolver =nonAnalyticSolversNumerical , &
+ & massDistribution_ =massDistributionVirialized_ , &
+ & massDistributionAccretionFlow_=massDistributionAccretionFlow_, &
+ & componentType =componentTypeDarkHalo , &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ end select
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+
+
+ !!]
+ return
+ end function accretionFlowShi2016Get
diff --git a/source/dark_matter_profiles_DMO.accretion_flow.correlation_function.F90 b/source/dark_matter_profiles_DMO.accretion_flow.correlation_function.F90
new file mode 100644
index 0000000000..53f8fcc967
--- /dev/null
+++ b/source/dark_matter_profiles_DMO.accretion_flow.correlation_function.F90
@@ -0,0 +1,293 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An implementation of a dark matter density profile which includes the accretion flow surrounding the halo.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmological_Density_Field , only : cosmologicalMassVarianceClass , criticalOverdensityClass
+ use :: Correlation_Functions_Two_Point, only : correlationFunctionTwoPointClass
+ use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBiasClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Linear_Growth , only : linearGrowthClass
+
+ !![
+
+
+ An accretion flow class which models the accretion flow using the 2-halo correlation function by building
+ \refClass{massDistributionCorrelationFunction} objects.
+
+
+ !!]
+ type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOAccretionFlowCorrelationFunction
+ !!{
+ A dark matter halo profile class which implements a dark matter density profile which includes the accretion flow using the
+ 2-halo correlation function.
+ !!}
+ private
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
+ class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
+ class (correlationFunctionTwoPointClass), pointer :: correlationFunctionTwoPoint_ => null()
+ class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null()
+ class (linearGrowthClass ), pointer :: linearGrowth_ => null()
+ double precision :: scaleFactorVelocity
+ contains
+ final :: accretionFlowCorrelationFunctionDestructor
+ procedure :: get => accretionFlowCorrelationFunctionGet
+ end type darkMatterProfileDMOAccretionFlowCorrelationFunction
+
+ interface darkMatterProfileDMOAccretionFlowCorrelationFunction
+ !!{
+ Constructors for the {\normalfont \ttfamily accretionFlowCorrelationFunction} dark matter halo profile class.
+ !!}
+ module procedure accretionFlowCorrelationFunctionConstructorParameters
+ module procedure accretionFlowCorrelationFunctionConstructorInternal
+ end interface darkMatterProfileDMOAccretionFlowCorrelationFunction
+
+contains
+
+ function accretionFlowCorrelationFunctionConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily accretionFlowCorrelationFunction} dark matter halo profile class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowCorrelationFunction) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (criticalOverdensityClass ), pointer :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
+ class (correlationFunctionTwoPointClass ), pointer :: correlationFunctionTwoPoint_
+ class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class (linearGrowthClass ), pointer :: linearGrowth_
+ double precision :: scaleFactorVelocity
+
+ !![
+
+ scaleFactorVelocity
+
+ 1.0d0
+ A scale factor to be applied to inflow velocities.
+
+
+
+
+
+
+
+
+
+ !!]
+ self=darkMatterProfileDMOAccretionFlowCorrelationFunction(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterHaloBias_,correlationFunctionTwoPoint_,darkMatterProfileDMO_,darkMatterHaloScale_,linearGrowth_)
+ !![
+
+
+
+
+
+
+
+
+
+ !!]
+ return
+ end function accretionFlowCorrelationFunctionConstructorParameters
+
+ function accretionFlowCorrelationFunctionConstructorInternal(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterHaloBias_,correlationFunctionTwoPoint_,darkMatterProfileDMO_,darkMatterHaloScale_,linearGrowth_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily accretionFlowCorrelationFunction} dark matter profile class.
+ !!}
+ implicit none
+ type (darkMatterProfileDMOAccretionFlowCorrelationFunction) :: self
+ class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
+ class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
+ class (correlationFunctionTwoPointClass ), intent(in ), target :: correlationFunctionTwoPoint_
+ class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class (linearGrowthClass ), intent(in ), target :: linearGrowth_
+ double precision, intent(in ) :: scaleFactorVelocity
+ !![
+
+ !!]
+
+ return
+ end function accretionFlowCorrelationFunctionConstructorInternal
+
+ subroutine accretionFlowCorrelationFunctionDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily accretionFlowCorrelationFunction} dark matter profile class.
+ !!}
+ implicit none
+ type(darkMatterProfileDMOAccretionFlowCorrelationFunction), intent(inout) :: self
+
+ !![
+
+
+
+
+
+
+
+ !!]
+ return
+ end subroutine accretionFlowCorrelationFunctionDestructor
+
+ function accretionFlowCorrelationFunctionGet(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSpherical , massDistributionSphericalAccretionFlow, massDistributionCorrelationFunction, kinematicsDistributionLam2013, &
+ & nonAnalyticSolversNumerical
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (massDistributionClass ), pointer :: massDistributionAccretionFlow_ , massDistributionVirialized_
+ type (kinematicsDistributionLam2013 ), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOAccretionFlowCorrelationFunction), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ double precision , allocatable , dimension(:) :: radius , correlationFunction , &
+ & correlationFunctionVolumeAveraged
+ integer , parameter :: countRadiiPerDecade =10
+ double precision , parameter :: factorRadiusMinimum =10.0d0, factorRadiusMaximum =10.0d0
+ integer :: countRadii , i
+ double precision :: time , mass , &
+ & radiusMinimum , radiusMaximum , &
+ & densityMean , radius200Mean , &
+ & peakHeight , radiusTransition
+ !![
+
+ !!]
+
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Combine the virialized and accretion flow mass distributions.
+ allocate(massDistributionSphericalAccretionFlow :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalAccretionFlow)
+ ! Get the virialized mass distribution.
+ massDistributionVirialized_ => self%darkMatterProfileDMO_%get(node)
+ select type (massDistributionVirialized_)
+ class is (massDistributionSpherical)
+ ! Find the radius enclosing 200 times the mean density.
+ densityMean = self %cosmologyFunctions_ %matterDensityEpochal ( time )
+ radius200Mean = massDistributionVirialized_ %radiusEnclosingDensity(+200.0d0*densityMean)
+ ! Compute the transition radius following Diemer & Kravtsov (2014; equation 6).
+ peakHeight =+self%criticalOverdensity_ %value (time=time,mass=mass) &
+ & /self%cosmologicalMassVariance_%rootVariance(time=time,mass=mass)
+ radiusTransition=+( &
+ & +1.90d0 &
+ & -0.18d0 &
+ & *peakHeight &
+ & ) &
+ & *radius200Mean
+ ! Create the accretion flow mass distribution.
+ allocate(massDistributionCorrelationFunction :: massDistributionAccretionFlow_)
+ select type(massDistributionAccretionFlow_)
+ type is (massDistributionCorrelationFunction)
+ ! Extract basic quantities for the halo.
+ basic => node %basic()
+ time = basic%time ()
+ mass = basic%mass ()
+ ! Build a correlation function.
+ radiusMinimum=radius200Mean/factorRadiusMinimum
+ radiusMaximum=radius200Mean*factorRadiusMaximum
+ countRadii =int(log10(radiusMaximum/radiusMinimum)*countRadiiPerDecade)+1
+ allocate(radius (countRadii))
+ allocate(correlationFunction (countRadii))
+ allocate(correlationFunctionVolumeAveraged(countRadii))
+ radius=Make_Range(radiusMinimum,radiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ correlationFunction (i)=+self%correlationFunctionTwoPoint_%correlation ( radius(i),time) &
+ & *self%darkMatterHaloBias_ %bias (node,radius(i) )
+ correlationFunctionVolumeAveraged(i)=+self%correlationFunctionTwoPoint_%correlationVolumeAveraged( radius(i),time) &
+ & *self%darkMatterHaloBias_ %bias (node,radius(i) )
+ end do
+ !![
+
+
+ massDistributionCorrelationFunction( &
+ & mass =mass , &
+ & time =time , &
+ & radius =radius , &
+ & correlationFunction=correlationFunction , &
+ & cosmologyFunctions_=self%cosmologyFunctions_, &
+ & componentType =componentTypeDarkHalo , &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ !![
+
+
+ massDistributionSphericalAccretionFlow( &
+ & radiusTransition =radiusTransition , &
+ & nonAnalyticSolver =nonAnalyticSolversNumerical , &
+ & massDistribution_ =massDistributionVirialized_ , &
+ & massDistributionAccretionFlow_=massDistributionAccretionFlow_, &
+ & componentType =componentTypeDarkHalo , &
+ & massType =massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ end select
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionLam2013( &
+ & massVirial = mass , &
+ & radiusVirial =self%darkMatterHaloScale_%radiusVirial (node=node ), &
+ & time = time , &
+ & overdensityCritical =self%criticalOverdensity_%value (time=time,mass=mass), &
+ & rateLinearGrowth =self%linearGrowth_ %logarithmicDerivativeExpansionFactor(time=time ), &
+ & scaleFactorVelocity =self%scaleFactorVelocity , &
+ & radius = radius , &
+ & correlationFunctionVolumeAveraged= correlationFunctionVolumeAveraged , &
+ & cosmologyFunctions_ =self%cosmologyFunctions_ &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+
+
+ !!]
+ return
+ end function accretionFlowCorrelationFunctionGet
diff --git a/source/dark_matter_profiles_DMO.finite_resolution.F90 b/source/dark_matter_profiles_DMO.finite_resolution.F90
index 0c609fbff1..6c0132c449 100644
--- a/source/dark_matter_profiles_DMO.finite_resolution.F90
+++ b/source/dark_matter_profiles_DMO.finite_resolution.F90
@@ -22,23 +22,15 @@
simulations for example).
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
+ use :: Cosmology_Functions, only : cosmologyFunctionsClass
+ use :: Mass_Distributions , only : enumerationNonAnalyticSolversType
!![
-
- A dark matter profile DMO class which applies a finite resolution to some other DMO class, typically to mimic the effects
- of finite resolution in an N-body simulation. Specifically, the density profile is given by
- \begin{equation}
- \rho(r) = \rho^\prime(r) \left( 1 + \left[ \frac{\Delta x}{r} \right]^2 \right)^{-1/2},
- \end{equation}
- where $\Delta x$ is the larger of the resolution length, {\normalfont \ttfamily [lengthResolution]}, and the radius in the
- original profile enclosing the mass resolution, {\normalfont \ttfamily [massResolution]}.
-
- Note that this choice was constructed to give a constant density core in an NFW density profile. For a density profile, $\rho^\prime(r)$, which
- rises more steeply than $r^{-1}$ as $r \rightarrow 0$ we will still have a cuspy density profile under this model.
-
+
+ A dark matter profile DMO class which builds \refClass{} objects to mimic the effects of finite resolution in an N-body
+ simulation.
+
!!]
type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOFiniteResolution
@@ -46,14 +38,13 @@
A dark matter halo profile class implementing finiteResolution dark matter halos.
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
- integer (kind=kind_int8 ) :: lastUniqueID
logical :: resolutionIsComoving
- double precision :: lengthResolution , massResolution , &
- & lengthResolutionPrevious , enclosedMassPrevious, &
- & enclosedMassRadiusPrevious
+ double precision :: lengthResolution , massResolution, &
+ & lengthResolutionPrevious
+ integer (kind_int8 ) :: lastUniqueID
contains
!![
@@ -61,30 +52,14 @@
!!]
- final :: finiteResolutionDestructor
- procedure :: autoHook => finiteResolutionAutoHook
- procedure :: calculationReset => finiteResolutionCalculationReset
- procedure :: density => finiteResolutionDensity
- procedure :: densityLogSlope => finiteResolutionDensityLogSlope
- procedure :: radiusEnclosingDensity => finiteResolutionRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => finiteResolutionRadiusEnclosingMass
- procedure :: radialMoment => finiteResolutionRadialMoment
- procedure :: enclosedMass => finiteResolutionEnclosedMass
- procedure :: potential => finiteResolutionPotential
- procedure :: circularVelocity => finiteResolutionCircularVelocity
- procedure :: radiusCircularVelocityMaximum => finiteResolutionRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => finiteResolutionCircularVelocityMaximum
- procedure :: radialVelocityDispersion => finiteResolutionRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => finiteResolutionRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => finiteResolutionRotationNormalization
- procedure :: energy => finiteResolutionEnergy
- procedure :: kSpace => finiteResolutionKSpace
- procedure :: freefallRadius => finiteResolutionFreefallRadius
- procedure :: freefallRadiusIncreaseRate => finiteResolutionFreefallRadiusIncreaseRate
- procedure :: lengthResolutionPhysical => finiteResolutionLengthResolutionPhysical
+ final :: finiteResolutionDestructor
+ procedure :: autoHook => finiteResolutionAutoHook
+ procedure :: calculationReset => finiteResolutionCalculationReset
+ procedure :: get => finiteResolutionGet
+ procedure :: lengthResolutionPhysical => finiteResolutionLengthResolutionPhysical
end type darkMatterProfileDMOFiniteResolution
- interface darkMatterProfileDMOFiniteResolution
+ interface darkMatterProfileDMOFiniteResolution
!!{
Constructors for the {\normalfont \ttfamily finiteResolution} dark matter halo profile class.
!!}
@@ -100,12 +75,12 @@ function finiteResolutionConstructorParameters(parameters) result(self)
!!{
Default constructor for the {\normalfont \ttfamily finiteResolution} dark matter halo profile class.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
implicit none
type (darkMatterProfileDMOFiniteResolution) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
double precision :: lengthResolution , massResolution
type (varying_string ) :: nonAnalyticSolver
@@ -120,7 +95,7 @@ function finiteResolutionConstructorParameters(parameters) result(self)
massResolution
- The resolution mass, $\Delta M$.
+ The resolution mass, $\Delta M$.
resolutionIsComoving
@@ -134,20 +109,18 @@ function finiteResolutionConstructorParameters(parameters) result(self)
Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
-
!!]
- self=darkMatterProfileDMOFiniteResolution(lengthResolution,massResolution,resolutionIsComoving,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),darkMatterProfileDMO_,darkMatterHaloScale_,cosmologyFunctions_)
+ self=darkMatterProfileDMOFiniteResolution(lengthResolution,massResolution,resolutionIsComoving,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),darkMatterProfileDMO_,cosmologyFunctions_)
!![
-
!!]
return
end function finiteResolutionConstructorParameters
- function finiteResolutionConstructorInternal(lengthResolution,massResolution,resolutionIsComoving,nonAnalyticSolver,darkMatterProfileDMO_,darkMatterHaloScale_,cosmologyFunctions_) result(self)
+ function finiteResolutionConstructorInternal(lengthResolution,massResolution,resolutionIsComoving,nonAnalyticSolver,darkMatterProfileDMO_,cosmologyFunctions_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily finiteResolution} dark matter profile class.
!!}
@@ -155,21 +128,15 @@ function finiteResolutionConstructorInternal(lengthResolution,massResolution,res
implicit none
type (darkMatterProfileDMOFiniteResolution) :: self
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
double precision , intent(in ) :: lengthResolution , massResolution
type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
logical , intent(in ) :: resolutionIsComoving
!![
-
+
!!]
- if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%lengthResolutionPrevious =-huge(0.0d0)
- self%enclosedMassPrevious =-huge(0.0d0)
- self%enclosedMassRadiusPrevious=-huge(0.0d0)
+ self%lastUniqueID=-huge(1_kind_int8)
return
end function finiteResolutionConstructorInternal
@@ -180,23 +147,24 @@ subroutine finiteResolutionAutoHook(self)
use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
implicit none
class(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
-
+
call calculationResetEvent%attach(self,finiteResolutionCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOFiniteResolution')
return
end subroutine finiteResolutionAutoHook
-
+
subroutine finiteResolutionDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily finiteResolution} dark matter halo profile class.
!!}
+ use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
!![
-
!!]
+ if (calculationResetEvent%isAttached(self,finiteResolutionCalculationReset)) call calculationResetEvent%detach(self,finiteResolutionCalculationReset)
return
end subroutine finiteResolutionDestructor
@@ -211,344 +179,75 @@ subroutine finiteResolutionCalculationReset(self,node,uniqueID)
integer(kind_int8 ), intent(in ) :: uniqueID
!$GLC attributes unused :: node
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%lengthResolutionPrevious =-huge(0.0d0)
- self%enclosedMassPrevious =-huge(0.0d0)
- self%enclosedMassRadiusPrevious =-huge(0.0d0)
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
+ self%lastUniqueID =uniqueID
+ self%lengthResolutionPrevious=-huge(0.0d0)
return
end subroutine finiteResolutionCalculationReset
- double precision function finiteResolutionDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: lengthResolution
-
- lengthResolution =+self %lengthResolutionPhysical(node )
- finiteResolutionDensity=+self%darkMatterProfileDMO_%density (node,radius) &
- & /sqrt( &
- & +1.0d0 &
- & +( &
- & +lengthResolution &
- & /radius &
- & )**2 &
- & )
- return
- end function finiteResolutionDensity
-
- double precision function finiteResolutionDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: lengthResolution
-
- lengthResolution =+self %lengthResolutionPhysical(node )
- finiteResolutionDensityLogSlope=+self%darkMatterProfileDMO_%densityLogSlope (node,radius) &
- & +( &
- & + lengthResolution &
- & / radius &
- & ) **2 &
- & /( &
- & +1.0d0 &
- & +( &
- & +lengthResolution &
- & /radius &
- & )**2 &
- & )
- return
- end function finiteResolutionDensityLogSlope
-
- double precision function finiteResolutionRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- finiteResolutionRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function finiteResolutionRadiusEnclosingDensity
-
- double precision function finiteResolutionRadiusEnclosingMass(self,node,mass)
+ function finiteResolutionGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- finiteResolutionRadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function finiteResolutionRadiusEnclosingMass
-
- double precision function finiteResolutionRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the radial moment of the density profile.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalFiniteResolution, kinematicsDistributionCollisionless, massDistributionSpherical
implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless ), pointer :: kinematicsDistribution_
class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- finiteResolutionRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function finiteResolutionRadialMoment
-
- double precision function finiteResolutionEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( radius /= self%enclosedMassRadiusPrevious) then
- self%enclosedMassRadiusPrevious=radius
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. radius > radiusLengthResolutionRatioMaximum*self%lengthResolutionPhysical(node)) then
- self%enclosedMassPrevious=self%darkMatterProfileDMO_%enclosedMass (node,radius)
- else
- self%enclosedMassPrevious=self %enclosedMassNumerical(node,radius)
- end if
- end if
- finiteResolutionEnclosedMass=self%enclosedMassPrevious
- return
- end function finiteResolutionEnclosedMass
-
- double precision function finiteResolutionPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. radius > radiusLengthResolutionRatioMaximum*self%lengthResolutionPhysical(node)) then
- finiteResolutionPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- finiteResolutionPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function finiteResolutionPotential
-
- double precision function finiteResolutionCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- finiteResolutionCircularVelocity=sqrt( &
- & +gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & / radius &
- & )
- else
- finiteResolutionCircularVelocity=0.0d0
- end if
- return
- end function finiteResolutionCircularVelocity
-
- double precision function finiteResolutionRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- finiteResolutionRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function finiteResolutionRadiusCircularVelocityMaximum
-
- double precision function finiteResolutionCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- finiteResolutionCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function finiteResolutionCircularVelocityMaximum
-
- double precision function finiteResolutionRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. radius > radiusLengthResolutionRatioMaximum*self%lengthResolutionPhysical(node)) then
- finiteResolutionRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else
- finiteResolutionRadialVelocityDispersion=self %radialVelocityDispersionNumerical(node,radius)
- end if
- return
- end function finiteResolutionRadialVelocityDispersion
-
- double precision function finiteResolutionRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- finiteResolutionRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function finiteResolutionRadiusFromSpecificAngularMomentum
-
- double precision function finiteResolutionRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision :: radiusVirial
-
- radiusVirial =+self%darkMatterHaloScale_%radiusVirial(node )
- finiteResolutionRotationNormalization=+self %radialMoment(node,moment=2.0d0,radiusMinimum=0.0d0,radiusMaximum=radiusVirial) &
- & /self %radialMoment(node,moment=3.0d0,radiusMinimum=0.0d0,radiusMaximum=radiusVirial)
- return
- end function finiteResolutionRotationNormalization
-
- double precision function finiteResolutionEnergy(self,node)
- !!{
- Return the energy of a finiteResolution halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- finiteResolutionEnergy=self %energyNumerical(node)
- end if
- return
- end function finiteResolutionEnergy
-
- double precision function finiteResolutionKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the finiteResolution density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$), using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- finiteResolutionKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function finiteResolutionKSpace
-
- double precision function finiteResolutionFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the finiteResolution density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- finiteResolutionFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function finiteResolutionFreefallRadius
-
- double precision function finiteResolutionFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the finiteResolution density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolution), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ !![
+
+ !!]
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- finiteResolutionFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- finiteResolutionFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalFiniteResolution :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalFiniteResolution)
+ massDistributionDecorated => self%darkMatterProfileDMO_%get(node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalFiniteResolution( &
+ & lengthResolution =self%lengthResolutionPhysical (node), &
+ & nonAnalyticSolver=self%nonAnalyticSolver , &
+ & massDistribution_= massDistributionDecorated , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function finiteResolutionFreefallRadiusIncreaseRate
+ end function finiteResolutionGet
double precision function finiteResolutionLengthResolutionPhysical(self,node)
!!{
@@ -559,6 +258,7 @@ double precision function finiteResolutionLengthResolutionPhysical(self,node)
class(darkMatterProfileDMOFiniteResolution), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class(nodeComponentBasic ), pointer :: basic
+ class(massDistributionClass ), pointer :: massDistribution_
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
if (self%lengthResolutionPrevious < 0.0d0) then
@@ -568,11 +268,16 @@ double precision function finiteResolutionLengthResolutionPhysical(self,node)
self%lengthResolutionPrevious = +self%lengthResolutionPrevious &
& *self%cosmologyFunctions_%expansionFactor(basic%time ())
end if
- if (self%massResolution > 0.0d0) &
- & self%lengthResolutionPrevious=max( &
- & self %lengthResolutionPrevious , &
- & self%darkMatterProfileDMO_%radiusEnclosingMass (node,self%massResolution) &
- & )
+ if (self%massResolution > 0.0d0) then
+ massDistribution_ => self%darkMatterProfileDMO_%get(node)
+ self%lengthResolutionPrevious = max( &
+ & self %lengthResolutionPrevious , &
+ & massDistribution_%radiusEnclosingMass (self%massResolution) &
+ & )
+ !![
+
+ !!]
+ end if
end if
finiteResolutionLengthResolutionPhysical=self%lengthResolutionPrevious
return
diff --git a/source/dark_matter_profiles_DMO.finite_resolution.NFW.F90 b/source/dark_matter_profiles_DMO.finite_resolution.NFW.F90
index edcc145e6c..f85ea136a6 100644
--- a/source/dark_matter_profiles_DMO.finite_resolution.NFW.F90
+++ b/source/dark_matter_profiles_DMO.finite_resolution.NFW.F90
@@ -22,20 +22,13 @@
simulations for example).
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
- use :: Numerical_Interpolation , only : interpolator
-
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
- A dark matter profile DMO class which applies a finite resolution to an NFW density profile, typically to mimic the effects
- of finite resolution in an N-body simulation. Specifically, the density profile is given by
- \begin{equation}
- \rho(r) = \rho_\mathrm{NFW}(r) \left( 1 + \left[ \frac{\Delta x}{r} \right]^2 \right)^{-1/2},
- \end{equation}
- where $\Delta x$ is the larger of the resolution length, {\normalfont \ttfamily [lengthResolution]}, and the radius in the
- original profile enclosing the mass resolution, {\normalfont \ttfamily [massResolution]}.
+ A dark matter profile DMO class which builds \refClass{massDistributionSphericalFiniteResolutionNFW} objects to mimic a finite
+ resolution to an NFW density profile.
@@ -45,91 +38,9 @@
A dark matter halo profile class implementing finiteResolutionNFW dark matter halos.
!!}
private
- double precision :: potentialRadiusPrevious , potentialPrevious , &
- & velocityDispersionRadialRadiusPrevious , velocityDispersionRadialPrevious , &
- & massNormalizationPrevious , lengthResolutionScaleFreePrevious , &
- & lengthResolutionScaleFreePreviousSquared , lengthResolutionScaleFreePreviousCubed , &
- & lengthResolutionScaleFreePreviousSqrtTerm , lengthResolutionScaleFreePreviousSqrt2Term , &
- & lengthResolutionScaleFreePreviousSqrtCubedTerm , lengthResolutionScaleFreePreviousLowerTerm , &
- & lengthResolutionScaleFreePreviousOnePlusTerm , lengthResolutionScaleFreePreviousOnePlus2Term , &
- & densityRadiusPrevious , densityPrevious , &
- & densityNormalizationPrevious , radiusEnclosingDensityDensityPrevious , &
- & radiusEnclosingDensityPrevious , radiusEnclosingMassMassPrevious , &
- & radiusEnclosingMassPrevious , energyPrevious
- ! Velocity dispersion tabulation.
- logical :: velocityDispersionRadialTableInitialized
- integer :: velocityDispersionRadialTableRadiusCoreCount , velocityDispersionRadialTableRadiusCount
- double precision , allocatable, dimension(: ) :: velocityDispersionRadialTableRadiusCore , velocityDispersionRadialTableRadius
- double precision , allocatable, dimension(:,:) :: velocityDispersionRadialTable
- type (interpolator), allocatable :: velocityDispersionRadialTableRadiusCoreInterpolator, velocityDispersionRadialTableRadiusInterpolator
- double precision :: velocityDispersionRadialRadiusMinimum , velocityDispersionRadialRadiusMaximum , &
- & velocityDispersionRadialRadiusCoreMinimum , velocityDispersionRadialRadiusCoreMaximum
- ! Radius-enclosing-density tabulation.
- logical :: radiusEnclosingDensityTableInitialized
- integer :: radiusEnclosingDensityTableRadiusCoreCount , radiusEnclosingDensityTableDensityCount
- double precision , allocatable, dimension(: ) :: radiusEnclosingDensityTableRadiusCore , radiusEnclosingDensityTableDensity
- double precision , allocatable, dimension(:,:) :: radiusEnclosingDensityTable
- type (interpolator), allocatable :: radiusEnclosingDensityTableRadiusCoreInterpolator , radiusEnclosingDensityTableDensityInterpolator
- double precision :: radiusEnclosingDensityDensityMinimum , radiusEnclosingDensityDensityMaximum , &
- & radiusEnclosingDensityRadiusCoreMinimum , radiusEnclosingDensityRadiusCoreMaximum
- ! Radius-enclosing-mass tabulation.
- logical :: radiusEnclosingMassTableInitialized
- integer :: radiusEnclosingMassTableRadiusCoreCount , radiusEnclosingMassTableMassCount
- double precision , allocatable, dimension(: ) :: radiusEnclosingMassTableRadiusCore , radiusEnclosingMassTableMass
- double precision , allocatable, dimension(:,:) :: radiusEnclosingMassTable
- type (interpolator), allocatable :: radiusEnclosingMassTableRadiusCoreInterpolator , radiusEnclosingMassTableMassInterpolator
- double precision :: radiusEnclosingMassMassMinimum , radiusEnclosingMassMassMaximum , &
- & radiusEnclosingMassRadiusCoreMinimum , radiusEnclosingMassRadiusCoreMaximum
- ! Energy tabulation.
- logical :: energyTableInitialized
- integer :: energyTableRadiusCoreCount , energyTableConcentrationCount
- double precision , allocatable, dimension(: ) :: energyTableRadiusCore , energyTableConcentration
- double precision , allocatable, dimension(:,:) :: energyTable
- type (interpolator), allocatable :: energyTableRadiusCoreInterpolator , energyTableConcentrationInterpolator
- double precision :: energyConcentrationMinimum , energyConcentrationMaximum , &
- & energyRadiusCoreMinimum , energyRadiusCoreMaximum
- contains
- !![
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- !!]
- procedure :: autoHook => finiteResolutionNFWAutoHook
- procedure :: calculationReset => finiteResolutionNFWCalculationReset
- procedure :: density => finiteResolutionNFWDensity
- procedure :: enclosedMass => finiteResolutionNFWEnclosedMass
- procedure :: potential => finiteResolutionNFWPotential
- procedure :: radiusEnclosingDensity => finiteResolutionNFWRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => finiteResolutionNFWRadiusEnclosingMass
- procedure :: energy => finiteResolutionNFWEnergy
- procedure :: radialVelocityDispersion => finiteResolutionNFWRadialVelocityDispersion
- procedure :: velocityDispersionRadialTabulate => finiteResolutionNFWVelocityDispersionRadialTabulate
- procedure :: radiusEnclosingDensityTabulate => finiteResolutionNFWRadiusEnclosingDensityTabulate
- procedure :: radiusEnclosingMassTabulate => finiteResolutionNFWRadiusEnclosingMassTabulate
- procedure :: energyTabulate => finiteResolutionNFWEnergyTabulate
- procedure :: densityScaleFree => finiteResolutionNFWDensityScaleFree
- procedure :: massEnclosedScaleFree => finiteResolutionNFWMassEnclosedScaleFree
- procedure :: storeVelocityDispersionTable => finiteResolutionNFWStoreVelocityDispersionTable
- procedure :: restoreVelocityDispersionTable => finiteResolutionNFWRestoreVelocityDispersionTable
- procedure :: storeDensityTable => finiteResolutionNFWStoreDensityTable
- procedure :: restoreDensityTable => finiteResolutionNFWRestoreDensityTable
- procedure :: storeMassTable => finiteResolutionNFWStoreMassTable
- procedure :: restoreMassTable => finiteResolutionNFWRestoreMassTable
- procedure :: storeEnergyTable => finiteResolutionNFWStoreEnergyTable
- procedure :: restoreEnergyTable => finiteResolutionNFWRestoreEnergyTable
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ contains
+ procedure :: get => finiteResolutionNFWGet
end type darkMatterProfileDMOFiniteResolutionNFW
interface darkMatterProfileDMOFiniteResolutionNFW
@@ -140,28 +51,14 @@
module procedure finiteResolutionNFWConstructorInternal
end interface darkMatterProfileDMOFiniteResolutionNFW
- ! Tabulation resolution parameters.
- integer, parameter :: velocityDispersionRadialTableRadiusPointsPerDecade =100
- integer, parameter :: velocityDispersionRadialTableRadiusCorePointsPerDecade=100
- integer, parameter :: radiusEnclosingDensityTableDensityPointsPerDecade =100
- integer, parameter :: radiusEnclosingDensityTableRadiusCorePointsPerDecade =100
- integer, parameter :: radiusEnclosingMassTableMassPointsPerDecade =100
- integer, parameter :: radiusEnclosingMassTableRadiusCorePointsPerDecade =100
- integer, parameter :: energyTableConcentrationPointsPerDecade =100
- integer, parameter :: energyTableRadiusCorePointsPerDecade =100
-
- ! Sub-module-scope variables used in integrations.
- class (darkMatterProfileDMOFiniteResolutionNFW), pointer :: self_
- integer :: iRadiusCore_, iDensity_, iMass_
- !$omp threadprivate(self_,iRadiusCore_,iDensity_,iMass_)
-
contains
function finiteResolutionNFWConstructorParameters(parameters) result(self)
!!{
Default constructor for the {\normalfont \ttfamily finiteResolutionNFW} dark matter halo profile class.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
+ use :: Input_Parameters , only : inputParameter, inputParameters
implicit none
type (darkMatterProfileDMOFiniteResolutionNFW) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -209,7 +106,7 @@ function finiteResolutionNFWConstructorInternal(lengthResolution,massResolution,
!!{
Generic constructor for the {\normalfont \ttfamily finiteResolutionNFW} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
implicit none
type (darkMatterProfileDMOFiniteResolutionNFW) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
@@ -220,1540 +117,81 @@ function finiteResolutionNFWConstructorInternal(lengthResolution,massResolution,
!![
!!]
-
- if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%lengthResolutionPrevious =-huge(0.0d0)
- self%massNormalizationPrevious =-huge(0.0d0)
- self%enclosedMassPrevious =-huge(0.0d0)
- self%enclosedMassRadiusPrevious =-huge(0.0d0)
- self%potentialPrevious =-huge(0.0d0)
- self%potentialRadiusPrevious =-huge(0.0d0)
- self%velocityDispersionRadialPrevious =-huge(0.0d0)
- self%velocityDispersionRadialRadiusPrevious =-huge(0.0d0)
- self%lengthResolutionScaleFreePrevious =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousSquared =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousCubed =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousOnePlusTerm =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousOnePlus2Term =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousSqrtTerm =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousSqrt2Term =-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousSqrtCubedTerm=-huge(0.0d0)
- self%lengthResolutionScaleFreePreviousLowerTerm =-huge(0.0d0)
- self%densityRadiusPrevious =-huge(0.0d0)
- self%densityPrevious =-huge(0.0d0)
- self%densityNormalizationPrevious =-huge(0.0d0)
- self%radiusEnclosingDensityDensityPrevious =-huge(0.0d0)
- self%radiusEnclosingDensityPrevious =-huge(0.0d0)
- self%radiusEnclosingMassMassPrevious =-huge(0.0d0)
- self%radiusEnclosingMassPrevious =-huge(0.0d0)
- self%energyPrevious =+huge(0.0d0)
- ! Velocity dispersion table initialization.
- self%velocityDispersionRadialRadiusMinimum =+huge(0.0d0)
- self%velocityDispersionRadialRadiusMaximum =-huge(0.0d0)
- self%velocityDispersionRadialRadiusCoreMinimum =+huge(0.0d0)
- self%velocityDispersionRadialRadiusCoreMaximum =-huge(0.0d0)
- self%velocityDispersionRadialTableInitialized =.false.
- ! Radius enclosing density table initialization.
- self%radiusEnclosingDensityDensityMinimum =+huge(0.0d0)
- self%radiusEnclosingDensityDensityMaximum =-huge(0.0d0)
- self%radiusEnclosingDensityRadiusCoreMinimum =+huge(0.0d0)
- self%radiusEnclosingDensityRadiusCoreMaximum =-huge(0.0d0)
- self%radiusEnclosingDensityTableInitialized =.false.
- ! Radius enclosing mass table initialization.
- self%radiusEnclosingMassMassMinimum =+huge(0.0d0)
- self%radiusEnclosingMassMassMaximum =-huge(0.0d0)
- self%radiusEnclosingMassRadiusCoreMinimum =+huge(0.0d0)
- self%radiusEnclosingMassRadiusCoreMaximum =-huge(0.0d0)
- self%radiusEnclosingMassTableInitialized =.false.
- ! Energy table initialization.
- self%energyConcentrationMinimum =+huge(0.0d0)
- self%energyConcentrationMaximum =-huge(0.0d0)
- self%energyRadiusCoreMinimum =+huge(0.0d0)
- self%energyRadiusCoreMaximum =-huge(0.0d0)
- self%energyTableInitialized =.false.
+
allocate(darkMatterProfileDMONFW :: self%darkMatterProfileDMO_)
select type (darkMatterProfileDMO_ => self%darkMatterProfileDMO_)
type is (darkMatterProfileDMONFW)
!![
-
- darkMatterProfileDMONFW( &
- & velocityDispersionUseSeriesExpansion=.false. , &
- & darkMatterHaloScale_ =self%darkMatterHaloScale_ &
- & )
-
+
+ darkMatterProfileDMONFW( &
+ & velocityDispersionUseSeriesExpansion=.false. , &
+ & darkMatterHaloScale_ =self%darkMatterHaloScale_ &
+ & )
+
!!]
- end select
+ end select
return
end function finiteResolutionNFWConstructorInternal
- subroutine finiteResolutionNFWAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
-
- call calculationResetEvent%attach(self,finiteResolutionNFWCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOFiniteResolutionNFW')
- return
- end subroutine finiteResolutionNFWAutoHook
-
- subroutine finiteResolutionNFWCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- call self%darkMatterProfileDMOFiniteResolution%calculationReset(node,uniqueID)
- self%potentialPrevious =-huge(0.0d0)
- self%potentialRadiusPrevious =-huge(0.0d0)
- self%velocityDispersionRadialPrevious =-huge(0.0d0)
- self%velocityDispersionRadialRadiusPrevious=-huge(0.0d0)
- self%massNormalizationPrevious =-huge(0.0d0)
- self%densityRadiusPrevious =-huge(0.0d0)
- self%densityPrevious =-huge(0.0d0)
- self%densityNormalizationPrevious =-huge(0.0d0)
- self%radiusEnclosingDensityDensityPrevious =-huge(0.0d0)
- self%radiusEnclosingDensityPrevious =-huge(0.0d0)
- self%radiusEnclosingMassMassPrevious =-huge(0.0d0)
- self%radiusEnclosingMassPrevious =-huge(0.0d0)
- self%energyPrevious =+huge(0.0d0)
- return
- end subroutine finiteResolutionNFWCalculationReset
-
- double precision function finiteResolutionNFWDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot/\mathrm{MPc}^3$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentDarkMatterProfile
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , radiusScaleFree, &
- & lengthResolutionScaleFree
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( radius /= self%densityRadiusPrevious) then
- darkMatterProfile => node%darkMatterProfile ( )
- radiusScaleFree = radius /darkMatterProfile%scale()
- lengthResolutionScaleFree = self%lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%densityRadiusPrevious = radius
- if (self%densityNormalizationPrevious < 0.0d0) then
- basic => node %basic ( )
- concentration = self %darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- self%densityNormalizationPrevious = +basic %mass ( ) &
- & /darkMatterProfile %scale ( )**3 &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & ) &
- & /4.0d0 &
- & /Pi
- end if
- self%densityPrevious=+self%densityNormalizationPrevious &
- & *self%densityScaleFree (radiusScaleFree,lengthResolutionScaleFree)
- end if
- finiteResolutionNFWDensity=self%densityPrevious
- return
- end function finiteResolutionNFWDensity
-
- double precision function finiteResolutionNFWDensityScaleFree(self,radius,radiusCore)
- !!{
- Returns the scale-free density in the dark matter profile at the given {\normalfont \ttfamily radius}.
- !!}
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- double precision , intent(in ) :: radius, radiusCore
-
- finiteResolutionNFWDensityScaleFree=1.0d0/(1.0d0+radius)**2/sqrt(radius**2+radiusCore**2)
- return
- end function finiteResolutionNFWDensityScaleFree
-
- double precision function finiteResolutionNFWEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc). The analytic solution (computed using Mathematica) is
- \begin{equation}
- M(x) = M \frac{-\frac{\sqrt{x^2+X^2}}{(1+x) \left(1+X^2\right)}+\tanh ^{-1}\left(\frac{x}{\sqrt{x^2+X^2}}\right)+\frac{\left(1+2X^2\right) \tanh ^{-1}\left(\frac{X^2-x}{\sqrt{1+X^2} \sqrt{x^2+X^2}}\right)}{\left(1+X^2\right)^{3/2}} -\frac{\left(1 + 2 X^2\right) \tanh ^{-1}\left(\sqrt{\frac{X^2}{1 + X^2}}\right)}{\left(1+ X^2\right)^{3/2}}+\frac{\sqrt{X^2}}{1 + X^2}}{\log (1+c)-\frac{c}{1+c}},
- \end{equation}
- where $x=r/r_\mathrm{s}$, $X = \Delta x/r_\mathrm{s}$, and $r_\mathrm{s}$ is the NFW scale length.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , radiusScaleFree, &
- & lengthResolutionScaleFree
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( radius /= self%enclosedMassRadiusPrevious) then
- darkMatterProfile => node%darkMatterProfile ( )
- radiusScaleFree = radius /darkMatterProfile%scale()
- lengthResolutionScaleFree = self%lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%enclosedMassRadiusPrevious = radius
- if (self%massNormalizationPrevious < 0.0d0) then
- basic => node %basic ( )
- concentration = self %darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- self%massNormalizationPrevious = +basic %mass ( ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- end if
- self%enclosedMassPrevious=+self%massNormalizationPrevious &
- & *self%massEnclosedScaleFree (radiusScaleFree,lengthResolutionScaleFree)
- end if
- finiteResolutionNFWEnclosedMass=self%enclosedMassPrevious
- return
- end function finiteResolutionNFWEnclosedMass
-
- double precision function finiteResolutionNFWRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision , parameter :: epsilonDensity =1.0d-3
- double precision :: concentration , densityScaleFree , &
- & lengthResolutionScaleFree , densityScaleFreeMaximum
- integer (c_size_t ), dimension(0:1) :: jRadiusCore
- double precision , dimension(0:1) :: hRadiusCore
- integer :: iRadiusCore
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( density /= self%radiusEnclosingDensityDensityPrevious) then
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile ( )
- concentration = self%darkMatterHaloScale_%radiusVirial (node)/darkMatterProfile%scale()
- lengthResolutionScaleFree = self %lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%radiusEnclosingDensityDensityPrevious = density
- if (self%massNormalizationPrevious < 0.0d0) then
- basic => node %basic ( )
- concentration = self %darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- self%massNormalizationPrevious = +basic %mass ( ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- end if
- ! Find scale free density, and the maximum such density reached in the profile.
- densityScaleFree =+ density &
- & /self %massNormalizationPrevious &
- & *darkMatterProfile%scale ()**3
- densityScaleFreeMaximum=+1.0d0 &
- & /4.0d0 &
- & /Pi &
- & /lengthResolutionScaleFree
- if (densityScaleFree >= densityScaleFreeMaximum) then
- ! Maximum density is exceeded - return zero radius.
- self%radiusEnclosingDensityPrevious=0.0d0
- else if (densityScaleFree >= densityScaleFreeMaximum*(1.0d0-epsilonDensity)) then
- ! For densities close to the maximum density, use a series solution.
- self%radiusEnclosingDensityPrevious=+0.5d0 &
- & *( &
- & +1.0d0 &
- & -densityScaleFree &
- & /densityScaleFreeMaximum &
- & ) &
- & *darkMatterProfile%scale()
- else
- ! Use a tabulated solution in other regimes.
- ! Ensure table is sufficiently extensive.
- call self%radiusEnclosingDensityTabulate(densityScaleFree,lengthResolutionScaleFree)
- ! Interpolate to get the scale free radius enclosing the scale free density.
- call self%radiusEnclosingDensityTableRadiusCoreInterpolator%linearFactors(lengthResolutionScaleFree,jRadiusCore(0),hRadiusCore)
- jRadiusCore(1)=jRadiusCore(0)+1
- self%radiusEnclosingDensityPrevious=0.0d0
- do iRadiusCore=0,1
- self%radiusEnclosingDensityPrevious=+self%radiusEnclosingDensityPrevious &
- & +self%radiusEnclosingDensityTableDensityInterpolator%interpolate(densityScaleFree,self%radiusEnclosingDensityTable(:,jRadiusCore(iRadiusCore))) &
- & * hRadiusCore(iRadiusCore)
- end do
- self%radiusEnclosingDensityPrevious=+self %radiusEnclosingDensityPrevious &
- & *darkMatterProfile%scale ()
- end if
- end if
- finiteResolutionNFWRadiusEnclosingDensity=self%radiusEnclosingDensityPrevious
- return
- end function finiteResolutionNFWRadiusEnclosingDensity
-
- subroutine finiteResolutionNFWRadiusEnclosingDensityTabulate(self,density,radiusCore)
- !!{
- Tabulates the radius enclosing a given density for finite resolution NFW density profiles.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- double precision , intent(in ) :: density , radiusCore
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-9
- logical :: retabulate
- integer :: iRadiusCore , iDensity , &
- & i
- type (rootFinder ) :: finder
-
- do i=1,2
- retabulate=.false.
- if (.not.self%radiusEnclosingDensityTableInitialized) then
- retabulate=.true.
- else if ( &
- & density < self%radiusEnclosingDensityDensityMinimum &
- & .or. &
- & density > self%radiusEnclosingDensityDensityMaximum &
- & .or. &
- & radiusCore < self%radiusEnclosingDensityRadiusCoreMinimum &
- & .or. &
- & radiusCore > self%radiusEnclosingDensityRadiusCoreMaximum &
- & ) then
- retabulate=.true.
- end if
- if (retabulate .and.i==1) call self%restoreDensityTable()
- if (.not.retabulate ) exit
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%radiusEnclosingDensityDensityMinimum =min(0.5d0*density ,self%radiusEnclosingDensityDensityMinimum )
- self%radiusEnclosingDensityDensityMaximum =max(2.0d0*density ,self%radiusEnclosingDensityDensityMaximum )
- self%radiusEnclosingDensityRadiusCoreMinimum =min(0.5d0*radiusCore,self%radiusEnclosingDensityRadiusCoreMinimum)
- self%radiusEnclosingDensityRadiusCoreMaximum =max(2.0d0*radiusCore,self%radiusEnclosingDensityRadiusCoreMaximum)
- self%radiusEnclosingDensityTableDensityCount =int(log10(self%radiusEnclosingDensityDensityMaximum /self%radiusEnclosingDensityDensityMinimum )*dble(radiusEnclosingDensityTableDensityPointsPerDecade ))+1
- self%radiusEnclosingDensityTableRadiusCoreCount=int(log10(self%radiusEnclosingDensityRadiusCoreMaximum/self%radiusEnclosingDensityRadiusCoreMinimum)*dble(radiusEnclosingDensityTableRadiusCorePointsPerDecade))+1
- if (allocated(self%radiusEnclosingDensityTableDensity)) then
- deallocate(self%radiusEnclosingDensityTableRadiusCore)
- deallocate(self%radiusEnclosingDensityTableDensity )
- deallocate(self%radiusEnclosingDensityTable )
- end if
- allocate(self%radiusEnclosingDensityTableRadiusCore( self%radiusEnclosingDensityTableRadiusCoreCount))
- allocate(self%radiusEnclosingDensityTableDensity (self%radiusEnclosingDensityTableDensityCount ))
- allocate(self%radiusEnclosingDensityTable (self%radiusEnclosingDensityTabledensityCount,self%radiusEnclosingDensityTableRadiusCoreCount))
- ! Create a range of radii and core radii.
- self%radiusEnclosingDensityTableDensity =Make_Range(self%radiusEnclosingDensityDensityMinimum ,self%radiusEnclosingDensityDensityMaximum ,self%radiusEnclosingDensityTableDensityCount ,rangeType=rangeTypeLogarithmic)
- self%radiusEnclosingDensityTableRadiusCore=Make_Range(self%radiusEnclosingDensityRadiusCoreMinimum,self%radiusEnclosingDensityRadiusCoreMaximum,self%radiusEnclosingDensityTableRadiusCoreCount,rangeType=rangeTypeLogarithmic)
- ! Initialize our root finder.
- finder=rootFinder( &
- & rootFunction =rootDensity , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive &
- & )
- ! Loop over density and core radius and populate tables.
- self_ => self
- do iRadiusCore=1,self%radiusEnclosingDensityTableRadiusCoreCount
- iRadiusCore_=iRadiusCore
- do iDensity=1,self%radiusEnclosingDensityTableDensityCount
- iDensity_=iDensity
- if (self%radiusEnclosingDensityTableDensity(iDensity) > 1.0d0/self%radiusEnclosingDensityTableRadiusCore(iRadiusCore)/4.0d0/Pi) then
- ! Density exceeds the maximum density in the profile - so set zero radius.
- self%radiusEnclosingDensityTable(iDensity,iRadiusCore)=0.0d0
- else
- self%radiusEnclosingDensityTable(iDensity,iRadiusCore)=finder%find(rootGuess=1.0d0)
- end if
- end do
- end do
- ! Build interpolators.
- if (allocated(self%radiusEnclosingDensityTableRadiusCoreInterpolator)) deallocate(self%radiusEnclosingDensityTableRadiusCoreInterpolator)
- if (allocated(self%radiusEnclosingDensityTableDensityInterpolator )) deallocate(self%radiusEnclosingDensityTableDensityInterpolator )
- allocate(self%radiusEnclosingDensityTableRadiusCoreInterpolator)
- allocate(self%radiusEnclosingDensityTableDensityInterpolator )
- self%radiusEnclosingDensityTableRadiusCoreInterpolator=interpolator(self%radiusEnclosingDensityTableRadiusCore)
- self%radiusEnclosingDensityTableDensityInterpolator =interpolator(self%radiusEnclosingDensityTableDensity )
- ! Specify that tabulation has been made.
- self%radiusEnclosingDensityTableInitialized=.true.
- call self%storeDensityTable()
- end if
- return
- end subroutine finiteResolutionNFWRadiusEnclosingDensityTabulate
-
- double precision function rootDensity(radius)
- !!{
- Root function used in finding the radius enclosing a given mean density.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
-
- rootDensity=+3.0d0 &
- & *self_%massEnclosedScaleFree (radius,self_%radiusEnclosingDensityTableRadiusCore(iRadiusCore_)) &
- & /4.0d0 &
- & /Pi &
- & / radius **3 &
- & -self_%radiusEnclosingDensityTableDensity( iDensity_ )
- return
- end function rootDensity
-
- double precision function finiteResolutionNFWRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , massScaleFree, &
- & lengthResolutionScaleFree
- integer (c_size_t ), dimension(0:1) :: jRadiusCore
- double precision , dimension(0:1) :: hRadiusCore
- integer :: iRadiusCore
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( mass /= self%radiusEnclosingMassMassPrevious) then
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile ( )
- concentration = self%darkMatterHaloScale_%radiusVirial (node)/darkMatterProfile%scale()
- lengthResolutionScaleFree = self %lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%radiusEnclosingMassMassPrevious = mass
- if (self%massNormalizationPrevious < 0.0d0) then
- basic => node %basic ( )
- concentration = self %darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- self%massNormalizationPrevious = +basic %mass ( ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- end if
- ! Find scale free mass, and the maximum such mass reached in the profile.
- massScaleFree=+ mass &
- & /self%massNormalizationPrevious
- ! Ensure table is sufficiently extensive.
- call self%radiusEnclosingMassTabulate(massScaleFree,lengthResolutionScaleFree)
- ! Interpolate to get the scale free radius enclosing the scale free mass.
- call self%radiusEnclosingMassTableRadiusCoreInterpolator%linearFactors(lengthResolutionScaleFree,jRadiusCore(0),hRadiusCore)
- jRadiusCore(1)=jRadiusCore(0)+1
- self%radiusEnclosingMassPrevious=0.0d0
- do iRadiusCore=0,1
- self%radiusEnclosingMassPrevious=+self%radiusEnclosingMassPrevious &
- & +self%radiusEnclosingMassTableMassInterpolator%interpolate(massScaleFree,self%radiusEnclosingMassTable(:,jRadiusCore(iRadiusCore))) &
- & * hRadiusCore(iRadiusCore)
- end do
- self%radiusEnclosingMassPrevious=+self %radiusEnclosingMassPrevious &
- & *darkMatterProfile%scale ()
- end if
- finiteResolutionNFWRadiusEnclosingMass=self%radiusEnclosingMassPrevious
- return
- end function finiteResolutionNFWRadiusEnclosingMass
-
- subroutine finiteResolutionNFWRadiusEnclosingMassTabulate(self,mass,radiusCore)
- !!{
- Tabulates the radius enclosing a given mass for finite resolution NFW mass profiles.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- double precision , intent(in ) :: mass , radiusCore
- double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-9
- logical :: retabulate
- integer :: iRadiusCore , iMass , &
- & i
- type (rootFinder ) :: finder
-
- do i=1,2
- retabulate=.false.
- if (.not.self%radiusEnclosingMassTableInitialized) then
- retabulate=.true.
- else if ( &
- & mass < self%radiusEnclosingMassMassMinimum &
- & .or. &
- & mass > self%radiusEnclosingMassMassMaximum &
- & .or. &
- & radiusCore < self%radiusEnclosingMassRadiusCoreMinimum &
- & .or. &
- & radiusCore > self%radiusEnclosingMassRadiusCoreMaximum &
- & ) then
- retabulate=.true.
- end if
- if (retabulate .and.i==1) call self%restoreMassTable()
- if (.not.retabulate ) exit
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%radiusEnclosingMassMassMinimum =min(0.5d0*mass ,self%radiusEnclosingMassMassMinimum )
- self%radiusEnclosingMassMassMaximum =max(2.0d0*mass ,self%radiusEnclosingMassMassMaximum )
- self%radiusEnclosingMassRadiusCoreMinimum =min(0.5d0*radiusCore,self%radiusEnclosingMassRadiusCoreMinimum)
- self%radiusEnclosingMassRadiusCoreMaximum =max(2.0d0*radiusCore,self%radiusEnclosingMassRadiusCoreMaximum)
- self%radiusEnclosingMassTableMassCount =int(log10(self%radiusEnclosingMassMassMaximum /self%radiusEnclosingMassMassMinimum )*dble(radiusEnclosingMassTableMassPointsPerDecade ))+1
- self%radiusEnclosingMassTableRadiusCoreCount=int(log10(self%radiusEnclosingMassRadiusCoreMaximum/self%radiusEnclosingMassRadiusCoreMinimum)*dble(radiusEnclosingMassTableRadiusCorePointsPerDecade))+1
- if (allocated(self%radiusEnclosingMassTableMass)) then
- deallocate(self%radiusEnclosingMassTableRadiusCore)
- deallocate(self%radiusEnclosingMassTableMass )
- deallocate(self%radiusEnclosingMassTable )
- end if
- allocate(self%radiusEnclosingMassTableRadiusCore( self%radiusEnclosingMassTableRadiusCoreCount))
- allocate(self%radiusEnclosingMassTableMass (self%radiusEnclosingMassTableMassCount ))
- allocate(self%radiusEnclosingMassTable (self%radiusEnclosingMassTablemassCount,self%radiusEnclosingMassTableRadiusCoreCount))
- ! Create a range of radii and core radii.
- self%radiusEnclosingMassTableMass =Make_Range(self%radiusEnclosingMassMassMinimum ,self%radiusEnclosingMassMassMaximum ,self%radiusEnclosingMassTableMassCount ,rangeType=rangeTypeLogarithmic)
- self%radiusEnclosingMassTableRadiusCore=Make_Range(self%radiusEnclosingMassRadiusCoreMinimum,self%radiusEnclosingMassRadiusCoreMaximum,self%radiusEnclosingMassTableRadiusCoreCount,rangeType=rangeTypeLogarithmic)
- ! Initialize our root finder.
- finder=rootFinder( &
- & rootFunction =rootMass , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
- & )
- ! Loop over mass and core radius and populate tables.
- self_ => self
- do iRadiusCore=1,self%radiusEnclosingMassTableRadiusCoreCount
- iRadiusCore_=iRadiusCore
- do iMass=1,self%radiusEnclosingMassTableMassCount
- iMass_=iMass
- ! Check that the root condition is satisfied at infinitely large radius. If it is not, then no radius encloses the
- ! required mass. Simply set the radius to an infinitely large value in such case.
- if (rootMass(radius=huge(0.0d0)) < 0.0d0) then
- self%radiusEnclosingMassTable(iMass,iRadiusCore)=huge(0.0d0)
- else
- self%radiusEnclosingMassTable(iMass,iRadiusCore)=finder%find(rootGuess=1.0d0)
- end if
- end do
- end do
- ! Build interpolators.
- if (allocated(self%radiusEnclosingMassTableRadiusCoreInterpolator)) deallocate(self%radiusEnclosingMassTableRadiusCoreInterpolator)
- if (allocated(self%radiusEnclosingMassTableMassInterpolator )) deallocate(self%radiusEnclosingMassTableMassInterpolator )
- allocate(self%radiusEnclosingMassTableRadiusCoreInterpolator)
- allocate(self%radiusEnclosingMassTableMassInterpolator )
- self%radiusEnclosingMassTableRadiusCoreInterpolator=interpolator(self%radiusEnclosingMassTableRadiusCore)
- self%radiusEnclosingMassTableMassInterpolator =interpolator(self%radiusEnclosingMassTableMass )
- ! Specify that tabulation has been made.
- self%radiusEnclosingMassTableInitialized=.true.
- call self%storeMassTable()
- end if
- return
- end subroutine finiteResolutionNFWRadiusEnclosingMassTabulate
-
- double precision function rootMass(radius)
- !!{
- Root function used in finding the radius enclosing a given mean mass.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- rootMass=+self_%massEnclosedScaleFree (radius,self_%radiusEnclosingMassTableRadiusCore(iRadiusCore_)) &
- & -self_%radiusEnclosingMassTableMass( iMass_ )
- return
- end function rootMass
-
- double precision function finiteResolutionNFWEnergy(self,node)
- !!{
- Returns the energy (in $M_\odot$ km$^2$/s$^2$) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , lengthResolutionScaleFree
- integer (c_size_t ), dimension(0:1) :: jRadiusCore
- double precision , dimension(0:1) :: hRadiusCore
- integer :: iRadiusCore
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- if (self%energyPrevious > 0.0d0) then
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile ( )
- concentration = self%darkMatterHaloScale_%radiusVirial (node)/darkMatterProfile%scale()
- lengthResolutionScaleFree = self %lengthResolutionPhysical(node)/darkMatterProfile%scale()
- if (self%massNormalizationPrevious < 0.0d0) then
- basic => node %basic ( )
- concentration = self %darkMatterHaloScale_%radiusVirial(node)/darkMatterProfile%scale()
- self%massNormalizationPrevious = +basic %mass ( ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- end if
- ! Ensure table is sufficiently extensive.
- call self%energyTabulate(concentration,lengthResolutionScaleFree)
- ! Interpolate to get the scale free energy.
- call self%energyTableRadiusCoreInterpolator%linearFactors(lengthResolutionScaleFree,jRadiusCore(0),hRadiusCore)
- jRadiusCore(1)=jRadiusCore(0)+1
- self%energyPrevious=0.0d0
- do iRadiusCore=0,1
- self%energyPrevious=+self%energyPrevious &
- & +self%energyTableConcentrationInterpolator%interpolate(concentration,self%energyTable(:,jRadiusCore(iRadiusCore))) &
- & * hRadiusCore(iRadiusCore)
- end do
- self%energyPrevious=+self %energyPrevious &
- & *gravitationalConstantGalacticus &
- & *self %massNormalizationPrevious **2 &
- & /darkMatterProfile%scale ()
- end if
- finiteResolutionNFWEnergy=self%energyPrevious
- return
- end function finiteResolutionNFWEnergy
-
- subroutine finiteResolutionNFWEnergyTabulate(self,concentration,radiusCore)
- !!{
- Tabulates the energy for finite resolution NFW mass profiles.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Integration , only : integrator
- use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- double precision , intent(in ) :: concentration , radiusCore
- double precision , parameter :: multiplierRadius =100.0d0
- type (integrator ) :: integratorPotential , integratorKinetic , &
- & integratorPressure
- double precision :: pseudoPressure , energyKinetic , &
- & energyPotential , concentration_
- logical :: retabulate
- integer :: iRadiusCore , iConcentration , &
- & i
-
- do i=1,2
- retabulate=.false.
- if (.not.self%energyTableInitialized) then
- retabulate=.true.
- else if ( &
- & concentration < self%energyConcentrationMinimum &
- & .or. &
- & concentration > self%energyConcentrationMaximum &
- & .or. &
- & radiusCore < self%energyRadiusCoreMinimum &
- & .or. &
- & radiusCore > self%energyRadiusCoreMaximum &
- & ) then
- retabulate=.true.
- end if
- if ( retabulate.and.i==1) call self%restoreEnergyTable()
- if (.not.retabulate ) exit
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%energyConcentrationMinimum =min(0.5d0*concentration,self%energyConcentrationMinimum)
- self%energyConcentrationMaximum =max(2.0d0*concentration,self%energyConcentrationMaximum)
- self%energyRadiusCoreMinimum =min(0.5d0*radiusCore ,self%energyRadiusCoreMinimum )
- self%energyRadiusCoreMaximum =max(2.0d0*radiusCore ,self%energyRadiusCoreMaximum )
- self%energyTableConcentrationCount=int(log10(self%energyConcentrationMaximum/self%energyConcentrationMinimum)*dble(energyTableConcentrationPointsPerDecade))+1
- self%energyTableRadiusCoreCount =int(log10(self%energyRadiusCoreMaximum /self%energyRadiusCoreMinimum )*dble(energyTableRadiusCorePointsPerDecade ))+1
- if (allocated(self%energyTableConcentration)) then
- deallocate(self%energyTableRadiusCore )
- deallocate(self%energyTableConcentration)
- deallocate(self%energyTable )
- end if
- allocate(self%energyTableRadiusCore ( self%energyTableRadiusCoreCount))
- allocate(self%energyTableConcentration(self%energyTableConcentrationCount ))
- allocate(self%energyTable (self%energyTableconcentrationCount,self%energyTableRadiusCoreCount))
- ! Create a range of radii and core radii.
- self%energyTableConcentration=Make_Range(self%energyConcentrationMinimum,self%energyConcentrationMaximum,self%energyTableConcentrationCount,rangeType=rangeTypeLogarithmic)
- self%energyTableRadiusCore =Make_Range(self%energyRadiusCoreMinimum ,self%energyRadiusCoreMaximum ,self%energyTableRadiusCoreCount ,rangeType=rangeTypeLogarithmic)
- ! Initialize integrators.
- integratorPotential=integrator(integrandEnergyPotential,toleranceRelative=1.0d-3)
- integratorKinetic =integrator(integrandEnergyKinetic ,toleranceRelative=1.0d-3)
- integratorPressure =integrator(integrandPseudoPressure ,toleranceRelative=1.0d-3)
- ! Loop over concentration and core radius and populate tables.
- self_ => self
- do iRadiusCore=1,self%energyTableRadiusCoreCount
- iRadiusCore_=iRadiusCore
- do iConcentration=1,self%energyTableConcentrationCount
- concentration_=self%energyTableConcentration(iConcentration)
- energyPotential =+integratorPotential%integrate( 0.0d0, concentration_)
- energyKinetic =+integratorKinetic %integrate( 0.0d0, concentration_)/4.0d0/Pi
- pseudoPressure =+integratorPressure %integrate(concentration_,multiplierRadius*concentration_)/4.0d0/Pi
- self%energyTable(iConcentration,iRadiusCore)=-0.5d0 &
- & *( &
- & +energyPotential &
- & +self%massEnclosedScaleFree(concentration_,self%energyTableRadiusCore(iRadiusCore))**2 &
- & /concentration_ &
- & ) &
- & +2.0d0 &
- & *Pi &
- & *( &
- & +concentration_ **3 &
- & *pseudoPressure &
- & +energyKinetic &
- & )
- end do
- end do
- ! Build interpolators.
- if (allocated(self%energyTableRadiusCoreInterpolator )) deallocate(self%energyTableRadiusCoreInterpolator )
- if (allocated(self%energyTableConcentrationInterpolator)) deallocate(self%energyTableConcentrationInterpolator)
- allocate(self%energyTableRadiusCoreInterpolator )
- allocate(self%energyTableConcentrationInterpolator)
- self%energyTableRadiusCoreInterpolator =interpolator(self%energyTableRadiusCore )
- self%energyTableConcentrationInterpolator=interpolator(self%energyTableConcentration)
- ! Specify that tabulation has been made.
- self%energyTableInitialized=.true.
- call self%storeEnergyTable()
- end if
- return
- end subroutine finiteResolutionNFWEnergyTabulate
-
- double precision function integrandEnergyPotential(radius)
- !!{
- Integrand for potential energy of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandEnergyPotential=( &
- & +self_%massEnclosedScaleFree(radius,self_%energyTableRadiusCore(iRadiusCore_)) &
- & / radius &
- & )**2
- else
- integrandEnergyPotential=+0.0d0
- end if
- return
- end function integrandEnergyPotential
-
- double precision function integrandEnergyKinetic(radius)
- !!{
- Integrand for kinetic energy of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandEnergyKinetic=+self_%massEnclosedScaleFree(radius,self_%energyTableRadiusCore(iRadiusCore_)) &
- & *self_%densityScaleFree (radius,self_%energyTableRadiusCore(iRadiusCore_)) &
- & * radius
- else
- integrandEnergyKinetic=+0.0d0
- end if
- return
- end function integrandEnergyKinetic
-
- double precision function integrandPseudoPressure(radius)
- !!{
- Integrand for pseudo-pressure ($\rho(r) \sigma^2(r)$) of the halo.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- integrandPseudoPressure=+self_%massEnclosedScaleFree(radius,self_%energyTableRadiusCore(iRadiusCore_)) &
- & *self_%densityScaleFree (radius,self_%energyTableRadiusCore(iRadiusCore_)) &
- & / radius **2
- else
- integrandPseudoPressure=+0.0d0
- end if
- return
- end function integrandPseudoPressure
-
- double precision function finiteResolutionNFWMassEnclosedScaleFree(self,radiusScaleFree,lengthResolutionScaleFree)
- !!{
- Returns the scale-free enclosed mass in the dark matter profile at the given {\normalfont \ttfamily radius}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentDarkMatterProfile
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- double precision , intent(in ) :: radiusScaleFree , lengthResolutionScaleFree
- double precision , parameter :: radiusScaleFreeSmall =1.0d-3, radiusScaleFreeLarge =1.0d4, &
- & radiusScaleFreeLargeATanh=1.0d+6
- double precision :: radiusScaleFreeEffective , arctanhTerm , &
- & arctanhTerm1
-
- if (lengthResolutionScaleFree /= self%lengthResolutionScaleFreePrevious) then
- self%lengthResolutionScaleFreePrevious =lengthResolutionScaleFree
- self%lengthResolutionScaleFreePreviousSquared =lengthResolutionScaleFree**2
- self%lengthResolutionScaleFreePreviousCubed =lengthResolutionScaleFree**3
- self%lengthResolutionScaleFreePreviousOnePlusTerm =+1.0d0+ self%lengthResolutionScaleFreePreviousSquared
- self%lengthResolutionScaleFreePreviousOnePlus2Term =+1.0d0+2.0d0*self%lengthResolutionScaleFreePreviousSquared
- self%lengthResolutionScaleFreePreviousSqrtTerm =sqrt(self%lengthResolutionScaleFreePreviousOnePlusTerm )
- self%lengthResolutionScaleFreePreviousSqrt2Term =sqrt(self%lengthResolutionScaleFreePreviousOnePlus2Term)
- self%lengthResolutionScaleFreePreviousSqrtCubedTerm=self%lengthResolutionScaleFreePreviousSqrtTerm**3
- ! For large values of the argument to arctanh(), use a series solution to avoiding floating point errors.
- if (lengthResolutionScaleFree > radiusScaleFreeLargeATanh) then
- arctanhTerm=-log( &
- & +2.0d0 &
- & *lengthResolutionScaleFree &
- & ) &
- & /2.0d0 &
- & +1.0d0 &
- & /2.0d0 &
- & /lengthResolutionScaleFree &
- & +1.0d0 &
- & /8.0d0 &
- & /lengthResolutionScaleFree**2
- else
- arctanhTerm=+atanh( &
- & +(+1.0d0-lengthResolutionScaleFree) &
- & /self%lengthResolutionScaleFreePreviousSqrtTerm &
- & )
- end if
- self%lengthResolutionScaleFreePreviousLowerTerm =+ lengthResolutionScaleFree &
- & / self%lengthResolutionScaleFreePreviousOnePlusTerm &
- & + 2.0d0 &
- & * self%lengthResolutionScaleFreePreviousOnePlus2Term &
- & * arctanhTerm &
- & / self%lengthResolutionScaleFreePreviousSqrtCubedTerm
- end if
- if (radiusScaleFree < radiusScaleFreeSmall) then
- ! Series expansion for small radii.
- finiteResolutionNFWMassEnclosedScaleFree=+ radiusScaleFree**3 &
- & *( &
- & +1.0d0 /self%lengthResolutionScaleFreePrevious / 3.0d0 &
- & +radiusScaleFree *( +1.0d0 /self%lengthResolutionScaleFreePrevious / 2.0d0 &
- & +radiusScaleFree * ( 1.0d0+(+6.0d0*self%lengthResolutionScaleFreePreviousSquared-1.0d0)/self%lengthResolutionScaleFreePreviousCubed/10.0d0 &
- & +radiusScaleFree * (1.0d0-(+4.0d0*self%lengthResolutionScaleFreePreviousSquared-1.0d0)/self%lengthResolutionScaleFreePreviousCubed/ 6.0d0 &
- & ) &
- & ) &
- & ) &
- & )
- else
- ! Full analytic solution.
- !! Limit the evaluation to some large radius.
- radiusScaleFreeEffective=min(radiusScaleFree,radiusScaleFreeLarge)
- if (radiusScaleFreeEffective > radiusScaleFreeLargeATanh*self%lengthResolutionScaleFreePrevious) then
- arctanhTerm1=+log ( &
- & +4.0d0 &
- & * radiusScaleFreeEffective**2 &
- & /self%lengthResolutionScaleFreePreviousSquared &
- & ) &
- & /2.0d0 &
- & -self%lengthResolutionScaleFreePreviousSquared &
- & /8.0d0 &
- & / radiusScaleFreeEffective**2
- else
- arctanhTerm1=+atanh( &
- & +radiusScaleFreeEffective &
- & /sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreePreviousSquared) &
- & )
- end if
- finiteResolutionNFWMassEnclosedScaleFree=- sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreePrevious**2) &
- & /(+1.0d0+radiusScaleFreeEffective) &
- & /self%lengthResolutionScaleFreePreviousOnePlusTerm &
- & -2.0d0 &
- & *self%lengthResolutionScaleFreePreviousOnePlus2Term &
- & *atanh( &
- & +( &
- & +1.0d0 &
- & +radiusScaleFreeEffective &
- & -sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreePrevious**2) &
- & ) &
- & /self%lengthResolutionScaleFreePreviousSqrtTerm &
- & ) &
- & /self%lengthResolutionScaleFreePreviousSqrtCubedTerm &
- & +arctanhTerm1 &
- & +self%lengthResolutionScaleFreePreviousLowerTerm
- !! Beyond the limiting radius assume logarithmic growth in mass as appropriate for an r⁻³ profile.
- if (radiusScaleFree > radiusScaleFreeEffective) &
- & finiteResolutionNFWMassEnclosedScaleFree=+finiteResolutionNFWMassEnclosedScaleFree &
- & *log( &
- & +radiusScaleFree &
- & /radiusScaleFreeEffective &
- & )
- end if
- return
- end function finiteResolutionNFWMassEnclosedScaleFree
-
- double precision function finiteResolutionNFWPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc). The analytic solution (computed using Mathematica) is
- \begin{eqnarray}
- \Phi(x) &=& -\frac{\mathrm{G} M}{r_\mathrm{s}} \nonumber \\
- & & \left\{ +\frac{\sqrt{x^2+X^2}}{x \left(X^2+1\right)} \right. \nonumber \\
- & & -\frac{X^2 \log \left(\sqrt{X^2+1} \sqrt{x^2+X^2}-x+X^2\right)}{\left(X^2+1\right)^{3/2}} \nonumber \\
- & & -\frac{\tanh ^{-1}\left(\frac{x}{\sqrt{x^2+X^2}}\right)}{x} \nonumber \\
- & & -\frac{\left(2 X^2+1\right) \tanh ^{-1}\left(\frac{X^2-x}{\sqrt{X^2+1} \sqrt{x^2+X^2}}\right)}{x \left(X^2+1\right)^{3/2}} \nonumber \\
- & & -\frac{\sqrt{X^2}}{x \left(X^2+1\right)}+\frac{X^2 \log (x+1)}{\left(X^2+1\right)^{3/2}} \nonumber \\
- & & +\frac{\left(2 X^2+1\right) \tanh ^{-1}\left(\sqrt{\frac{X^2}{X^2+1}}\right)}{x \left(X^2+1\right)^{3/2}} \nonumber \\
- & & \left. +\frac{ \left(\sqrt{X^2+1}-X^2 \log \left(\sqrt{X^2+1}-1\right)\right)}{\left(X^2+1\right)^{3/2}} \right\} \nonumber \\
- & & /\left[\log (1+c)-\frac{c}{1+c}\right]
- \end{eqnarray}
- !!}
- use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType, structureErrorCodeSuccess
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , radiusScaleFree, &
- & lengthResolutionScaleFree
- double precision , parameter :: radiusScaleFreeSmall =1.0d-3
-
- if (present(status)) status=structureErrorCodeSuccess
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( radius /= self%potentialRadiusPrevious) then
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile ( )
- radiusScaleFree = radius /darkMatterProfile%scale()
- concentration = self%darkMatterHaloScale_%radiusVirial (node)/darkMatterProfile%scale()
- lengthResolutionScaleFree = self %lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%potentialRadiusPrevious = radius
- if (radiusScaleFree < radiusScaleFreeSmall) then
- ! Series expansion for small radii.
- self%potentialPrevious = -gravitationalConstantGalacticus &
- & *basic %mass () &
- & /darkMatterProfile%scale() &
- & *( &
- & +(+1.0d0-lengthResolutionScaleFree ) &
- & /(+1.0d0+lengthResolutionScaleFree**2) &
- & + lengthResolutionScaleFree**2 &
- & *( &
- & +asinh(lengthResolutionScaleFree ) &
- & +log ( &
- & +(1.0d0+sqrt(+1.0d0+lengthResolutionScaleFree**2)) &
- & / lengthResolutionScaleFree &
- & ) &
- & ) &
- & /(+1.0d0+lengthResolutionScaleFree**2)**1.5d0 &
- & - radiusScaleFree**2 &
- & *(+1.0d0-radiusScaleFree ) &
- & / lengthResolutionScaleFree &
- & /6.0d0 &
- & ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- else
- self%potentialPrevious = -gravitationalConstantGalacticus &
- & *basic %mass () &
- & /darkMatterProfile%scale() &
- & *( &
- & + sqrt( lengthResolutionScaleFree**2 ) &
- & / radiusScaleFree /(+1.0d0+ lengthResolutionScaleFree**2) &
- & - sqrt( +radiusScaleFree**2+lengthResolutionScaleFree**2 ) &
- & / radiusScaleFree /(+1.0d0+ lengthResolutionScaleFree**2) &
- & - (+1.0d0+2.0d0*lengthResolutionScaleFree**2) &
- & *atanh( &
- & +sqrt( +lengthResolutionScaleFree**2/(+1.0d0+ lengthResolutionScaleFree**2) ) &
- & ) &
- & / radiusScaleFree /(+1.0d0+ lengthResolutionScaleFree**2)**1.5d0 &
- & +atanh( &
- & + radiusScaleFree &
- & /sqrt( +radiusScaleFree**2+lengthResolutionScaleFree**2 ) &
- & ) &
- & / radiusScaleFree &
- & + (+1.0d0+2.0d0*lengthResolutionScaleFree**2) &
- & *atanh( &
- & ( -radiusScaleFree +lengthResolutionScaleFree**2 ) &
- & /sqrt (+1.0d0+ lengthResolutionScaleFree**2) &
- & /sqrt( +radiusScaleFree**2+lengthResolutionScaleFree**2 ) &
- & ) &
- & / radiusScaleFree /(+1.0d0+ lengthResolutionScaleFree**2)**1.5d0 &
- & - lengthResolutionScaleFree**2 &
- & *log ( &
- & +1.0d0+radiusScaleFree &
- & ) &
- & / (+1.0d0+ lengthResolutionScaleFree**2)**1.5d0 &
- & + lengthResolutionScaleFree**2/(+1.0d0+ lengthResolutionScaleFree**2)**1.5d0 &
- & *log ( &
- & -radiusScaleFree +lengthResolutionScaleFree**2 &
- & +sqrt(+1.0d0 +lengthResolutionScaleFree**2 ) &
- & *sqrt( +radiusScaleFree**2+lengthResolutionScaleFree**2 ) &
- & ) &
- & +( &
- & + sqrt(+1.0d0 +lengthResolutionScaleFree**2 ) &
- & - lengthResolutionScaleFree**2 &
- & *log( &
- & -1.0d0 &
- & +sqrt(+1.0d0 +lengthResolutionScaleFree**2 ) &
- & ) &
- & ) &
- & / (+1.0d0+ lengthResolutionScaleFree**2)**1.5d0 &
- & ) &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & )
- end if
- end if
- finiteResolutionNFWPotential=self%potentialPrevious
- return
- end function finiteResolutionNFWPotential
-
- double precision function finiteResolutionNFWRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: concentration , radiusScaleFree , &
- & lengthResolutionScaleFree , radiusScaleFreeEffective
- double precision , parameter :: lengthResolutionScaleFreeSmall=1.0d-3
- integer (c_size_t ), dimension(0:1) :: jRadiusCore
- double precision , dimension(0:1) :: hRadiusCore
- integer :: iRadiusCore
-
- if (node%uniqueID() /= self%lastUniqueID ) call self%calculationReset(node,node%uniqueID())
- if ( radius /= self%velocityDispersionRadialRadiusPrevious) then
- basic => node%basic ( )
- darkMatterProfile => node%darkMatterProfile ( )
- radiusScaleFree = radius /darkMatterProfile%scale()
- concentration = self%darkMatterHaloScale_%radiusVirial (node)/darkMatterProfile%scale()
- lengthResolutionScaleFree = self %lengthResolutionPhysical(node)/darkMatterProfile%scale()
- self%velocityDispersionRadialRadiusPrevious = radius
- ! Compute the effective radius. In the core of the profile the velocity dispersion must become constant. Therefore, we
- ! limit the smallest radius we consider to a small fraction of the core radius. Below this radius a constant velocity
- ! dispersion is assumed.
- radiusScaleFreeEffective=max(radiusScaleFree,lengthResolutionScaleFreeSmall*lengthResolutionScaleFree)
- ! Ensure table is sufficiently extensive.
- call self%velocityDispersionRadialTabulate(radiusScaleFreeEffective,lengthResolutionScaleFree)
- ! Interpolate to get the velocity dispersion.
- call self%velocityDispersionRadialTableRadiusCoreInterpolator%linearFactors(lengthResolutionScaleFree,jRadiusCore(0),hRadiusCore)
- jRadiusCore(1)=jRadiusCore(0)+1
- self%velocityDispersionRadialPrevious=0.0d0
- do iRadiusCore=0,1
- self%velocityDispersionRadialPrevious=+self%velocityDispersionRadialPrevious &
- & +self%velocityDispersionRadialTableRadiusInterpolator%interpolate(radiusScaleFreeEffective,self%velocityDispersionRadialTable(:,jRadiusCore(iRadiusCore))) &
- & * hRadiusCore(iRadiusCore)
- end do
- self%velocityDispersionRadialPrevious=+self%velocityDispersionRadialPrevious &
- & *sqrt( &
- & +gravitationalConstantGalacticus &
- & *basic %mass () &
- & /darkMatterProfile%scale() &
- & /( &
- & - concentration &
- & / (1.0d0+concentration) &
- & +log(1.0d0+concentration) &
- & ) &
- & )
- end if
- finiteResolutionNFWRadialVelocityDispersion=self%velocityDispersionRadialPrevious
- return
- end function finiteResolutionNFWRadialVelocityDispersion
-
- subroutine finiteResolutionNFWVelocityDispersionRadialTabulate(self,radius,radiusCore)
+ function finiteResolutionNFWGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Tabulates the mass enclosed within a given radius for finite resolution NFW density profiles.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
- use :: Numerical_Integration, only : integrator
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalFiniteResolutionNFW, kinematicsDistributionFiniteResolutionNFW, massDistributionSpherical
implicit none
- class (darkMatterProfileDMOFiniteResolutionNFW), intent(inout), target :: self
- double precision , intent(in ) :: radius , radiusCore
- double precision , parameter :: radiusTiny =1.0d-1
- type (integrator ), save :: integrator_
- logical , save :: initialized =.false.
- !$omp threadprivate(integrator_,initialized)
- logical :: retabulate
- integer :: iRadiusCore , iRadius , &
- & i
- double precision :: jeansIntegral , jeansIntegralPrevious, &
- & radiusLower , radiusUpper , &
- & radiusOuter , density
-
- do i=1,2
- retabulate=.false.
- if (.not.self%velocityDispersionRadialTableInitialized) then
- retabulate=.true.
- else if ( &
- & radius < self%velocityDispersionRadialRadiusMinimum &
- & .or. &
- & radius > self%velocityDispersionRadialRadiusMaximum &
- & .or. &
- & radiusCore < self%velocityDispersionRadialRadiusCoreMinimum &
- & .or. &
- & radiusCore > self%velocityDispersionRadialRadiusCoreMaximum &
- & ) then
- retabulate=.true.
- end if
- if (retabulate .and.i==1) call self%restoreVelocityDispersionTable()
- if (.not.retabulate ) exit
- end do
- if (retabulate) then
- ! Decide how many points to tabulate and allocate table arrays.
- self%velocityDispersionRadialRadiusMinimum =min(0.5d0*radius ,self%velocityDispersionRadialRadiusMinimum )
- self%velocityDispersionRadialRadiusMaximum =max(2.0d0*radius ,self%velocityDispersionRadialRadiusMaximum )
- self%velocityDispersionRadialRadiusCoreMinimum =min(0.5d0*radiusCore,self%velocityDispersionRadialRadiusCoreMinimum)
- self%velocityDispersionRadialRadiusCoreMaximum =max(2.0d0*radiusCore,self%velocityDispersionRadialRadiusCoreMaximum)
- self%velocityDispersionRadialTableRadiusCount =int(log10(self%velocityDispersionRadialRadiusMaximum /self%velocityDispersionRadialRadiusMinimum )*dble(velocityDispersionRadialTableRadiusPointsPerDecade ))+1
- self%velocityDispersionRadialTableRadiusCoreCount=int(log10(self%velocityDispersionRadialRadiusCoreMaximum/self%velocityDispersionRadialRadiusCoreMinimum)*dble(velocityDispersionRadialTableRadiusCorePointsPerDecade))+1
- if (allocated(self%velocityDispersionRadialTableRadius)) then
- deallocate(self%velocityDispersionRadialTableRadiusCore)
- deallocate(self%velocityDispersionRadialTableRadius )
- deallocate(self%velocityDispersionRadialTable )
- end if
- allocate(self%velocityDispersionRadialTableRadiusCore( self%velocityDispersionRadialTableRadiusCoreCount))
- allocate(self%velocityDispersionRadialTableRadius (self%velocityDispersionRadialTableRadiusCount ))
- allocate(self%velocityDispersionRadialTable (self%velocityDispersionRadialTableRadiusCount,self%velocityDispersionRadialTableRadiusCoreCount))
- ! Create a range of radii and core radii.
- self%velocityDispersionRadialTableRadius =Make_Range(self%velocityDispersionRadialRadiusMinimum ,self%velocityDispersionRadialRadiusMaximum ,self%velocityDispersionRadialTableRadiusCount ,rangeType=rangeTypeLogarithmic)
- self%velocityDispersionRadialTableRadiusCore=Make_Range(self%velocityDispersionRadialRadiusCoreMinimum,self%velocityDispersionRadialRadiusCoreMaximum,self%velocityDispersionRadialTableRadiusCoreCount,rangeType=rangeTypeLogarithmic)
- ! Initialize integrator if necessary.
- if (.not.initialized) then
- integrator_=integrator(jeansEquationIntegrand,toleranceRelative=1.0d-2)
- initialized=.true.
- end if
- ! Loop over radii and α and populate tables.
- self_ => self
- radiusOuter = max(10.0d0*self%velocityDispersionRadialRadiusMaximum,1000.0d0)
- do iRadiusCore=1,self%velocityDispersionRadialTableRadiusCoreCount
- iRadiusCore_ =iRadiusCore
- jeansIntegralPrevious=0.0d0
- do iRadius=self%velocityDispersionRadialTableRadiusCount,1,-1
- ! For radii that are tiny compared to the core radius the velocity dispersion become almost constant. Simply assume this to avoid floating point errors.
- if ( &
- & self%velocityDispersionRadialTableRadius(iRadius) < radiusTiny &
- & .and. &
- & self%velocityDispersionRadialTableRadius(iRadius) < radiusTiny*self%velocityDispersionRadialTableRadiusCore(iRadiusCore) &
- & .and. &
- & iRadius < self%velocityDispersionRadialTableRadiusCount &
- & ) then
- self%velocityDispersionRadialTable(iRadius,iRadiusCore)=self%velocityDispersionRadialTable(iRadius+1,iRadiusCore)
- else
- ! Find the limits for the integral.
- if (iRadius == self%velocityDispersionRadialTableRadiusCount) then
- radiusUpper=radiusOuter
- else
- radiusUpper=self%velocityDispersionRadialTableRadius(iRadius+1)
- end if
- radiusLower =self %velocityDispersionRadialTableRadius( iRadius )
- density =self %densityScaleFree (radiusLower,self%velocityDispersionRadialTableRadiusCore(iRadiusCore))
- jeansIntegral =integrator_%integrate (radiusLower, radiusUpper )
- self%velocityDispersionRadialTable(iRadius,iRadiusCore)=+sqrt( &
- & +( &
- & +jeansIntegral &
- & +jeansIntegralPrevious &
- & ) &
- & /density &
- & )
- jeansIntegralPrevious =+jeansIntegralPrevious &
- & +jeansIntegral
- end if
- end do
- end do
- ! Build interpolators.
- if (allocated(self%velocityDispersionRadialTableRadiusCoreInterpolator)) deallocate(self%velocityDispersionRadialTableRadiusCoreInterpolator)
- if (allocated(self%velocityDispersionRadialTableRadiusInterpolator )) deallocate(self%velocityDispersionRadialTableRadiusInterpolator )
- allocate(self%velocityDispersionRadialTableRadiusCoreInterpolator)
- allocate(self%velocityDispersionRadialTableRadiusInterpolator )
- self%velocityDispersionRadialTableRadiusCoreInterpolator=interpolator(self%velocityDispersionRadialTableRadiusCore)
- self%velocityDispersionRadialTableRadiusInterpolator =interpolator(self%velocityDispersionRadialTableRadius )
- ! Specify that tabulation has been made.
- self%velocityDispersionRadialTableInitialized=.true.
- call self%storeVelocityDispersionTable()
- end if
- return
- end subroutine finiteResolutionNFWVelocityDispersionRadialTabulate
-
- double precision function jeansEquationIntegrand(radius)
- !!{
- Integrand for dark matter profile Jeans equation.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius > 0.0d0) then
- jeansEquationIntegrand=+self_%massEnclosedScaleFree(radius,self_%velocityDispersionRadialTableRadiusCore(iRadiusCore_)) &
- & *self_%densityScaleFree (radius,self_%velocityDispersionRadialTableRadiusCore(iRadiusCore_)) &
- & / radius **2
- else
- jeansEquationIntegrand=0.0d0
- end if
- return
- end function jeansEquationIntegrand
-
- subroutine finiteResolutionNFWStoreVelocityDispersionTable(self)
- !!{
- Store the tabulated velocity dispersion data to file.
- !!}
- use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
- & File_Path
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//) , char
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'VelocityDispersion_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.,includeFileModificationTimes=.true.)// &
- & '.hdf5'
- call Directory_Make(char(File_Path(char(fileName))))
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
- call file%writeDataset(self%velocityDispersionRadialTableRadiusCore,'radiusCore' )
- call file%writeDataset(self%velocityDispersionRadialTableRadius ,'radius' )
- call file%writeDataset(self%velocityDispersionRadialTable ,'velocityDispersion')
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- return
- end subroutine finiteResolutionNFWStoreVelocityDispersionTable
-
- subroutine finiteResolutionNFWRestoreVelocityDispersionTable(self)
- !!{
- Restore the tabulated velocity dispersion data from file, returning true if successful.
- !!}
- use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//)
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'VelocityDispersion_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- if (File_Exists(fileName)) then
- if (allocated(self%velocityDispersionRadialTableRadius)) then
- deallocate(self%velocityDispersionRadialTableRadiusCore)
- deallocate(self%velocityDispersionRadialTableRadius )
- deallocate(self%velocityDispersionRadialTable )
- end if
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName))
- call file%readDataset('radiusCore' ,self%velocityDispersionRadialTableRadiusCore)
- call file%readDataset('radius' ,self%velocityDispersionRadialTableRadius )
- call file%readDataset('velocityDispersion',self%velocityDispersionRadialTable )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- self%velocityDispersionRadialTableRadiusCount =size(self%velocityDispersionRadialTableRadius )
- self%velocityDispersionRadialTableRadiusCoreCount=size(self%velocityDispersionRadialTableRadiusCore)
- self%velocityDispersionRadialRadiusMinimum =self%velocityDispersionRadialTableRadius ( 1)
- self%velocityDispersionRadialRadiusMaximum =self%velocityDispersionRadialTableRadius (self%velocityDispersionRadialTableRadiusCount )
- self%velocityDispersionRadialRadiusCoreMinimum =self%velocityDispersionRadialTableRadiusCore( 1)
- self%velocityDispersionRadialRadiusCoreMaximum =self%velocityDispersionRadialTableRadiusCore(self%velocityDispersionRadialTableRadiusCoreCount)
- if (allocated(self%velocityDispersionRadialTableRadiusCoreInterpolator)) deallocate(self%velocityDispersionRadialTableRadiusCoreInterpolator)
- if (allocated(self%velocityDispersionRadialTableRadiusInterpolator )) deallocate(self%velocityDispersionRadialTableRadiusInterpolator )
- allocate(self%velocityDispersionRadialTableRadiusCoreInterpolator)
- allocate(self%velocityDispersionRadialTableRadiusInterpolator )
- self%velocityDispersionRadialTableRadiusCoreInterpolator=interpolator(self%velocityDispersionRadialTableRadiusCore)
- self%velocityDispersionRadialTableRadiusInterpolator =interpolator(self%velocityDispersionRadialTableRadius )
- self%velocityDispersionRadialTableInitialized =.true.
- end if
- return
- end subroutine finiteResolutionNFWRestoreVelocityDispersionTable
-
- subroutine finiteResolutionNFWStoreDensityTable(self)
- !!{
- Store the tabulated radius-enclosing-density data to file.
- !!}
- use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
- & File_Path
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//) , char
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Density_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- call Directory_Make(char(File_Path(char(fileName))))
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
- call file%writeDataset(self%radiusEnclosingDensityTableRadiusCore,'radiusCore')
- call file%writeDataset(self%radiusEnclosingDensityTableDensity ,'density' )
- call file%writeDataset(self%radiusEnclosingDensityTable ,'radius' )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- return
- end subroutine finiteResolutionNFWStoreDensityTable
-
- subroutine finiteResolutionNFWRestoreDensityTable(self)
- !!{
- Restore the tabulated radius-enclosing-density data from file, returning true if successful.
- !!}
- use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//)
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Density_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- if (File_Exists(fileName)) then
- if (allocated(self%radiusEnclosingDensityTableDensity)) then
- deallocate(self%radiusEnclosingDensityTableRadiusCore)
- deallocate(self%radiusEnclosingDensityTableDensity )
- deallocate(self%radiusEnclosingDensityTable )
- end if
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName))
- call file%readDataset('radiusCore',self%radiusEnclosingDensityTableRadiusCore)
- call file%readDataset('density' ,self%radiusEnclosingDensityTableDensity )
- call file%readDataset('radius' ,self%radiusEnclosingDensityTable )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- self%radiusEnclosingDensityTableDensityCount =size(self%radiusEnclosingDensityTableDensity )
- self%radiusEnclosingDensityTableRadiusCoreCount=size(self%radiusEnclosingDensityTableRadiusCore)
- self%radiusEnclosingDensityDensityMinimum =self%radiusEnclosingDensityTableDensity ( 1)
- self%radiusEnclosingDensityDensityMaximum =self%radiusEnclosingDensityTableDensity (self%radiusEnclosingDensityTableDensityCount )
- self%radiusEnclosingDensityRadiusCoreMinimum =self%radiusEnclosingDensityTableRadiusCore( 1)
- self%radiusEnclosingDensityRadiusCoreMaximum =self%radiusEnclosingDensityTableRadiusCore(self%radiusEnclosingDensityTableRadiusCoreCount)
- if (allocated(self%radiusEnclosingDensityTableRadiusCoreInterpolator)) deallocate(self%radiusEnclosingDensityTableRadiusCoreInterpolator)
- if (allocated(self%radiusEnclosingDensityTableDensityInterpolator )) deallocate(self%radiusEnclosingDensityTableDensityInterpolator )
- allocate(self%radiusEnclosingDensityTableRadiusCoreInterpolator)
- allocate(self%radiusEnclosingDensityTableDensityInterpolator )
- self%radiusEnclosingDensityTableRadiusCoreInterpolator=interpolator(self%radiusEnclosingDensityTableRadiusCore)
- self%radiusEnclosingDensityTableDensityInterpolator =interpolator(self%radiusEnclosingDensityTableDensity )
- self%radiusEnclosingDensityTableInitialized =.true.
- end if
- return
- end subroutine finiteResolutionNFWRestoreDensityTable
-
- subroutine finiteResolutionNFWStoreMassTable(self)
- !!{
- Store the tabulated radius-enclosing-mass data to file.
- !!}
- use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
- & File_Path
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//) , char
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Mass_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- call Directory_Make(char(File_Path(char(fileName))))
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
- call file%writeDataset(self%radiusEnclosingMassTableRadiusCore,'radiusCore')
- call file%writeDataset(self%radiusEnclosingMassTableMass ,'mass' )
- call file%writeDataset(self%radiusEnclosingMassTable ,'radius' )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- return
- end subroutine finiteResolutionNFWStoreMassTable
-
- subroutine finiteResolutionNFWRestoreMassTable(self)
- !!{
- Restore the tabulated radius-enclosing-mass data from file, returning true if successful.
- !!}
- use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//)
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Mass_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- if (File_Exists(fileName)) then
- if (allocated(self%radiusEnclosingMassTableMass)) then
- deallocate(self%radiusEnclosingMassTableRadiusCore)
- deallocate(self%radiusEnclosingMassTableMass )
- deallocate(self%radiusEnclosingMassTable )
- end if
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName))
- call file%readDataset('radiusCore',self%radiusEnclosingMassTableRadiusCore)
- call file%readDataset('mass' ,self%radiusEnclosingMassTableMass )
- call file%readDataset('radius' ,self%radiusEnclosingMassTable )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- self%radiusEnclosingMassTableMassCount =size(self%radiusEnclosingMassTableMass )
- self%radiusEnclosingMassTableRadiusCoreCount=size(self%radiusEnclosingMassTableRadiusCore)
- self%radiusEnclosingMassMassMinimum =self%radiusEnclosingMassTableMass ( 1)
- self%radiusEnclosingMassMassMaximum =self%radiusEnclosingMassTableMass (self%radiusEnclosingMassTableMassCount )
- self%radiusEnclosingMassRadiusCoreMinimum =self%radiusEnclosingMassTableRadiusCore( 1)
- self%radiusEnclosingMassRadiusCoreMaximum =self%radiusEnclosingMassTableRadiusCore(self%radiusEnclosingMassTableRadiusCoreCount)
- if (allocated(self%radiusEnclosingMassTableRadiusCoreInterpolator)) deallocate(self%radiusEnclosingMassTableRadiusCoreInterpolator)
- if (allocated(self%radiusEnclosingMassTableMassInterpolator )) deallocate(self%radiusEnclosingMassTableMassInterpolator )
- allocate(self%radiusEnclosingMassTableRadiusCoreInterpolator)
- allocate(self%radiusEnclosingMassTableMassInterpolator )
- self%radiusEnclosingMassTableRadiusCoreInterpolator=interpolator(self%radiusEnclosingMassTableRadiusCore)
- self%radiusEnclosingMassTableMassInterpolator =interpolator(self%radiusEnclosingMassTableMass )
- self%radiusEnclosingMassTableInitialized =.true.
- end if
- return
- end subroutine finiteResolutionNFWRestoreMassTable
-
- subroutine finiteResolutionNFWStoreEnergyTable(self)
- !!{
- Store the tabulated energy data to file.
- !!}
- use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
- & File_Path
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//) , char
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
-
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Energy_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- call Directory_Make(char(File_Path(char(fileName))))
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
- call file%writeDataset(self%energyTableRadiusCore ,'radiusCore' )
- call file%writeDataset(self%energyTableConcentration,'concentration')
- call file%writeDataset(self%energyTable ,'energy' )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- return
- end subroutine finiteResolutionNFWStoreEnergyTable
-
- subroutine finiteResolutionNFWRestoreEnergyTable(self)
- !!{
- Restore the tabulated radius-enclosing-mass data from file, returning true if successful.
- !!}
- use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
- use :: HDF5_Access , only : hdf5Access
- use :: IO_HDF5 , only : hdf5Object
- use :: Input_Paths , only : inputPath , pathTypeDataDynamic
- use :: ISO_Varying_String, only : varying_string, operator(//)
- implicit none
- class(darkMatterProfileDMOFiniteResolutionNFW), intent(inout) :: self
- type (lockDescriptor ) :: fileLock
- type (hdf5Object ) :: file
- type (varying_string ) :: fileName
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionFiniteResolutionNFW), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOFiniteResolutionNFW ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ !![
+
+ !!]
- fileName=inputPath(pathTypeDataDynamic) // &
- & 'darkMatter/' // &
- & self%objectType ( )// &
- & 'Energy_' // &
- & self%hashedDescriptor(includeSourceDigest=.true.)// &
- & '.hdf5'
- if (File_Exists(fileName)) then
- if (allocated(self%energyTableConcentration)) then
- deallocate(self%energyTableRadiusCore )
- deallocate(self%energyTableConcentration)
- deallocate(self%energyTable )
- end if
- ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
- call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
- !$ call hdf5Access%set()
- call file%openFile(char(fileName))
- call file%readDataset('radiusCore' ,self%energyTableRadiusCore )
- call file%readDataset('concentration',self%energyTableConcentration)
- call file%readDataset('energy' ,self%energyTable )
- call file%close()
- !$ call hdf5Access%unset()
- call File_Unlock(fileLock)
- self%energyTableConcentrationCount=size(self%energyTableConcentration )
- self%energyTableRadiusCoreCount =size(self%energyTableRadiusCore)
- self%energyConcentrationMinimum =self%energyTableConcentration( 1)
- self%energyConcentrationMaximum =self%energyTableConcentration(self%energyTableConcentrationCount)
- self%energyRadiusCoreMinimum =self%energyTableRadiusCore ( 1)
- self%energyRadiusCoreMaximum =self%energyTableRadiusCore (self%energyTableRadiusCoreCount )
- if (allocated(self%energyTableRadiusCoreInterpolator )) deallocate(self%energyTableRadiusCoreInterpolator )
- if (allocated(self%energyTableConcentrationInterpolator)) deallocate(self%energyTableConcentrationInterpolator)
- allocate(self%energyTableRadiusCoreInterpolator )
- allocate(self%energyTableConcentrationInterpolator)
- self%energyTableRadiusCoreInterpolator =interpolator(self%energyTableRadiusCore )
- self%energyTableConcentrationInterpolator=interpolator(self%energyTableConcentration)
- self%energyTableInitialized =.true.
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalFiniteResolutionNFW :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalFiniteResolutionNFW)
+ basic => node%basic()
+ darkMatterProfile => node%darkMatterProfile()
+ !![
+
+
+ massDistributionSphericalFiniteResolutionNFW( &
+ & lengthResolution =self %lengthResolutionPhysical(node), &
+ & radiusScale =darkMatterProfile %scale ( ), &
+ & radiusVirial =self %darkMatterHaloScale_%radiusVirial (node), &
+ & mass =basic %mass ( ), &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionFiniteResolutionNFW( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end subroutine finiteResolutionNFWRestoreEnergyTable
+ end function finiteResolutionNFWGet
diff --git a/source/dark_matter_profiles_DMO.heated.F90 b/source/dark_matter_profiles_DMO.heated.F90
index 1c23bfc075..a50bb77b80 100644
--- a/source/dark_matter_profiles_DMO.heated.F90
+++ b/source/dark_matter_profiles_DMO.heated.F90
@@ -21,76 +21,29 @@
An implementation of heated dark matter halo profiles.
!!}
- use :: Root_Finder, only : rootFinder
-
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversType
+
!![
- A dark matter profile DMO class in which dark matter halos start out with a density profile defined by another {\normalfont
- \ttfamily darkMatterProfileDMO}. This profile is then modified by heating, under the assumption that the
- energy of a shell of mass before and after heating are related by
- \begin{equation}
- -{ \mathrm{G} M^\prime(r^\prime) \over r^\prime } = -{ \mathrm{G} M(r) \over r } + 2 \epsilon(r),
- \end{equation}
- where $M(r)$ is the mass enclosed within a radius $r$, and $\epsilon(r)$ represents the specific heating in the shell
- initially at radius $r$. Primes indicate values after heating, while unprimed variables indicate quantities prior to
- heating. With the assumption of no shell crossing, $M^\prime(r^\prime)=M(r)$ and this equation can be solved for $r$ given
- $r^\prime$ and $\epsilon(r)$.
-
- Not all methods have analytic solutions for this profile. If {\normalfont \ttfamily [nonAnalyticSolver]}$=${\normalfont
- \ttfamily fallThrough} then attempts to call these methods in heated profiles will simply return the result from the
- unheated profile, otherwise a numerical calculation is performed.
+ A dark matter profile DMO class which builds \refClass{massDistributionSphericalHeated} objects to account for heating of
+ some other dark matter profile.
!!]
-
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
- use :: Kind_Numbers , only : kind_int8
-
type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOHeated
!!{
A dark matter halo profile class implementing heated dark matter halos.
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_ => null()
- integer (kind=kind_int8 ) :: lastUniqueID
- double precision :: radiusFinalPrevious , radiusInitialPrevious
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_ => null()
+ double precision :: toleranceRelativeVelocityDispersion , toleranceRelativeVelocityDispersionMaximum
type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
- logical :: velocityDispersionApproximate
- type (rootFinder ) :: finder
+ logical :: velocityDispersionApproximate , tolerateVelocityMaximumFailure
contains
- !![
-
-
-
-
-
- !!]
- final :: heatedDestructor
- procedure :: autoHook => heatedAutoHook
- procedure :: calculationReset => heatedCalculationReset
- procedure :: radiusInitial => heatedRadiusInitial
- procedure :: noShellCrossingIsValid => heatedNoShellCrossingIsValid
- procedure :: density => heatedDensity
- procedure :: densityLogSlope => heatedDensityLogSlope
- procedure :: radiusEnclosingDensity => heatedRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => heatedRadiusEnclosingMass
- procedure :: radialMoment => heatedRadialMoment
- procedure :: enclosedMass => heatedEnclosedMass
- procedure :: potential => heatedPotential
- procedure :: circularVelocity => heatedCircularVelocity
- procedure :: radiusCircularVelocityMaximum => heatedRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => heatedCircularVelocityMaximum
- procedure :: radialVelocityDispersion => heatedRadialVelocityDispersion
- procedure :: jeansEquationIntegrand => heatedJeansEquationIntegrand
- procedure :: jeansEquationRadius => heatedJeansEquationRadius
- procedure :: radiusFromSpecificAngularMomentum => heatedRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => heatedRotationNormalization
- procedure :: energy => heatedEnergy
- procedure :: kSpace => heatedKSpace
- procedure :: freefallRadius => heatedFreefallRadius
- procedure :: freefallRadiusIncreaseRate => heatedFreefallRadiusIncreaseRate
+ final :: heatedDestructor
+ procedure :: get => heatedGet
end type darkMatterProfileDMOHeated
interface darkMatterProfileDMOHeated
@@ -101,27 +54,21 @@
module procedure heatedConstructorInternal
end interface darkMatterProfileDMOHeated
- ! Global variables used in root solving.
- double precision :: radiusFinal_
- type (treeNode ), pointer :: node_
- type (darkMatterProfileDMOHeated), pointer :: self_
- !$omp threadprivate(radiusFinal_,node_,self_)
-
contains
function heatedConstructorParameters(parameters) result(self)
!!{
Default constructor for the {\normalfont \ttfamily heated} dark matter halo profile class.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
+ use :: Input_Parameters , only : inputParameter, inputParameters
implicit none
type (darkMatterProfileDMOHeated ) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_
type (varying_string ) :: nonAnalyticSolver
- logical :: velocityDispersionApproximate
+ logical :: velocityDispersionApproximate , tolerateVelocityMaximumFailure
double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
!![
@@ -149,652 +96,127 @@ function heatedConstructorParameters(parameters) result(self)
The maximum relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
+
+ tolerateVelocityMaximumFailure
+ .true.
+ If true, tolerate failures to find the radius of the peak in the rotation curve.
+
+
-
!!]
- self=darkMatterProfileDMOHeated(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),velocityDispersionApproximate,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_)
+ self=darkMatterProfileDMOHeated(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),velocityDispersionApproximate,tolerateVelocityMaximumFailure,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterProfileHeating_)
!![
-
!!]
return
end function heatedConstructorParameters
- function heatedConstructorInternal(nonAnalyticSolver,velocityDispersionApproximate,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_) result(self)
+ function heatedConstructorInternal(nonAnalyticSolver,velocityDispersionApproximate,tolerateVelocityMaximumFailure,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterProfileHeating_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily heated} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversIsValid
+ use :: Error , only : Error_Report
implicit none
type (darkMatterProfileDMOHeated ) :: self
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileHeatingClass ), intent(in ), target :: darkMatterProfileHeating_
type (enumerationNonAnalyticSolversType), intent(in ) :: nonAnalyticSolver
- logical , intent(in ) :: velocityDispersionApproximate
- double precision , intent(in ) :: toleranceRelativeVelocityDispersion , toleranceRelativeVelocityDispersionMaximum
- double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative =1.0d-6
+ logical , intent(in ) :: velocityDispersionApproximate , tolerateVelocityMaximumFailure
+ double precision , intent(in ) :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
!![
-
+
!!]
! Validate.
if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- ! Construct the object.
- self%genericLastUniqueID=-1_kind_int8
- self%lastUniqueID =-1_kind_int8
- self%radiusFinalPrevious=-huge(0.0d0)
- self%finder =rootFinder( &
- & rootFunction =heatedRadiusInitialRoot, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
return
end function heatedConstructorInternal
- subroutine heatedAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOHeated), intent(inout) :: self
-
- call calculationResetEvent%attach(self,heatedCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOHeated')
- return
- end subroutine heatedAutoHook
-
subroutine heatedDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily heated} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOHeated), intent(inout) :: self
!![
-
!!]
- if (calculationResetEvent%isAttached(self,heatedCalculationReset)) call calculationResetEvent%detach(self,heatedCalculationReset)
return
end subroutine heatedDestructor
- subroutine heatedCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- ! Reset calculations for this profile.
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%radiusFinalPrevious =-huge(0.0d0)
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine heatedCalculationReset
-
- double precision function heatedDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusInitial , massEnclosed, &
- & densityInitial, jacobian
-
- radiusInitial =self %radiusInitial(node,radius )
- densityInitial=self%darkMatterProfileDMO_%density (node,radiusInitial)
- if (radius == 0.0d0 .and. radiusInitial == 0.0d0) then
- ! At zero radius, the density is unchanged.
- heatedDensity =+densityInitial
- else if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_)) then
- ! No heating, the density is unchanged.
- heatedDensity =+densityInitial
- else if (.not.self%noShellCrossingIsValid(node_,radiusInitial,radius)) then
- ! Shell crossing assumption is broken - simply return the density unchanged.
- heatedDensity =+self%darkMatterProfileDMO_%density (node,radius )
- else
- massEnclosed=+self%darkMatterProfileDMO_%enclosedMass (node,radiusInitial)
- if (massEnclosed > 0.0d0) then
- jacobian =+1.0d0 &
- & /( &
- & +( &
- & +radius &
- & /radiusInitial &
- & ) **2 &
- & +2.0d0 &
- & *radius **2 &
- & /gravitationalConstantGalacticus &
- & /massEnclosed &
- & *( &
- & +self%darkMatterProfileHeating_%specificEnergyGradient(node,radiusInitial,self%darkMatterProfileDMO_) &
- & -4.0d0 &
- & *Pi &
- & *radiusInitial **2 &
- & *densityInitial &
- & *self%darkMatterProfileHeating_%specificEnergy (node,radiusInitial,self%darkMatterProfileDMO_) &
- & /massEnclosed &
- & ) &
- & )
- heatedDensity =+densityInitial &
- & *( &
- & +radiusInitial &
- & /radius &
- & ) **2 &
- & *jacobian
- else
- heatedDensity =+densityInitial
- end if
- end if
- return
- end function heatedDensity
-
- double precision function heatedDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope (node,radius)
- else
- heatedDensityLogSlope=self %densityLogSlopeNumerical(node,radius)
- end if
- return
- end function heatedDensityLogSlope
-
- double precision function heatedRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- heatedRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function heatedRadiusEnclosingDensity
-
- double precision function heatedRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- use :: Galactic_Structure_Options , only : radiusLarge
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- double precision :: radiusInitial
- double precision :: energySpecific
-
- radiusInitial =self%darkMatterProfileDMO_ %radiusEnclosingMass(node,mass )
- energySpecific=self%darkMatterProfileHeating_%specificEnergy (node,radiusInitial,self%darkMatterProfileDMO_)
- if (radiusInitial <= 0.0d0) then
- heatedRadiusEnclosingMass=radiusLarge
- else
- heatedRadiusEnclosingMass=+1.0d0 &
- & / &
- & ( &
- & +1.0d0/radiusInitial &
- & -2.0d0/gravitationalConstantGalacticus/mass*energySpecific &
- & )
- ! If the radius found is negative, which means the initial shell has expanded to infinity, return the largest radius.
- if (heatedRadiusEnclosingMass < 0.0d0) heatedRadiusEnclosingMass=radiusLarge
- end if
- return
- end function heatedRadiusEnclosingMass
-
- double precision function heatedRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- heatedRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function heatedRadialMoment
-
- double precision function heatedEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- heatedEnclosedMass=self%darkMatterProfileDMO_%enclosedMass(node,self%radiusInitial(node,radius))
- return
- end function heatedEnclosedMass
-
- double precision function heatedRadiusInitial(self,node,radiusFinal)
- !!{
- Find the initial radius corresponding to the given {\normalfont \ttfamily radiusFinal} in
- the heated dark matter profile.
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radiusFinal
- double precision , parameter :: epsilonExpand=1.0d-2
- double precision :: factorExpand
-
- ! If profile is unheated, the initial radius equals the final radius.
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_)) then
- heatedRadiusInitial=radiusFinal
- return
- end if
- ! Reset calculations if necessary.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Find the initial radius in the unheated profile.
- if (radiusFinal /= self%radiusFinalPrevious) then
- self_ => self
- node_ => node
- radiusFinal_ = radiusFinal
- if (self%radiusFinalPrevious <= -huge(0.0d0) .or. radiusFinal < self%radiusInitialPrevious .or. radiusFinal > 10.0d0*self%radiusInitialPrevious) then
- ! No previous solution is available, or the requested final radius is smaller than the previous initial radius, or the
- ! final radius is much larger than the previous initial radius. In this case, our guess for the initial radius is the
- ! final radius, and we expand the range downward to find a solution.
- call self%finder%rangeExpand( &
- & rangeExpandUpward =1.01d0 , &
- & rangeExpandDownward =0.50d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- self%radiusInitialPrevious=self%finder%find(rootGuess=radiusFinal)
- else
- ! Previous solution exists, and the requested final radius is larger (but not too much larger) than the previous initial
- ! radius. Use the previous initial radius as a guess for the solution, with range expansion in steps determined by the
- ! relative values of the current and previous final radii. If the current final radius is close to the previous final
- ! radius this should give a guess for the initial radius close to the actual solution.
- if (radiusFinal > self%radiusFinalPrevious) then
- factorExpand= radiusFinal /self%radiusFinalPrevious
- else
- factorExpand=self%radiusFinalPrevious/ radiusFinal
- end if
- factorExpand=max(factorExpand,1.0d0+epsilonExpand)
- call self%finder%rangeExpand( &
- & rangeExpandUpward =1.0d0*factorExpand , &
- & rangeExpandDownward =1.0d0/factorExpand , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- self%radiusInitialPrevious=self%finder%find(rootGuess=self%radiusInitialPrevious)
- end if
- self%radiusFinalPrevious=radiusFinal
- end if
- heatedRadiusInitial=self%radiusInitialPrevious
- return
- end function heatedRadiusInitial
-
- logical function heatedNoShellCrossingIsValid(self,node,radiusInitial,radiusFinal)
- !!{
- Determines if the no shell crossing assumption is valid.
- !!}
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radiusInitial, radiusFinal
- double precision :: massEnclosed
-
- massEnclosed = + self%darkMatterProfileDMO_ %enclosedMass (node,radiusInitial )
- heatedNoShellCrossingIsValid= + self%darkMatterProfileHeating_%specificEnergyGradient(node,radiusInitial,self%darkMatterProfileDMO_) &
- & > &
- & +0.5d0 &
- & *gravitationalConstantGalacticus &
- & *( &
- & +4.0d0 &
- & *Pi &
- & *radiusInitial**2 &
- & *self%darkMatterProfileDMO_ %density (node,radiusInitial ) &
- & *( &
- & -1.0d0/radiusFinal &
- & +1.0d0/radiusInitial &
- & ) &
- & -massEnclosed &
- & /radiusInitial**2 &
- & )
- return
- end function heatedNoShellCrossingIsValid
-
- double precision function heatedRadiusInitialRoot(radiusInitial)
- !!{
- Root function used in finding initial radii in heated dark matter halo profiles.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- double precision, intent(in ) :: radiusInitial
- double precision, parameter :: fractionRadiusSmall=1.0d-3
- double precision :: massEnclosed
-
- if (radiusInitial < fractionRadiusSmall*radiusFinal_) then
- ! The initial radius is a small fraction of the final radius. Check if the assumption of no shell crossing is locally
- ! broken. If the gradient of the heating term is less than that of the gravitational potential term then it is likely that
- ! no root exists. In this case shell crossing is likely to be occurring. Simply return a value of zero, which places the
- ! root at the current radius.
- if (.not.self_%noShellCrossingIsValid(node_,radiusInitial,radiusFinal_)) then
- heatedRadiusInitialRoot=0.0d0
- return
- end if
- end if
- massEnclosed =+self_%darkMatterProfileDMO_ %enclosedMass (node_,radiusInitial )
- heatedRadiusInitialRoot=+self_%darkMatterProfileHeating_%specificEnergy(node_,radiusInitial,self_%darkMatterProfileDMO_) &
- & +0.5d0 &
- & *gravitationalConstantGalacticus &
- & *massEnclosed &
- & *( &
- & +1.0d0/radiusFinal_ &
- & -1.0d0/radiusInitial &
- & )
- return
- end function heatedRadiusInitialRoot
-
- double precision function heatedPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- heatedPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function heatedPotential
-
- double precision function heatedCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- heatedCircularVelocity=sqrt( &
- & +gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & / radius &
- & )
- else
- heatedCircularVelocity=0.0d0
- end if
- return
- end function heatedCircularVelocity
-
- double precision function heatedRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- heatedRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function heatedRadiusCircularVelocityMaximum
-
- double precision function heatedCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- heatedCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function heatedCircularVelocityMaximum
-
- double precision function heatedRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusInitial , energySpecific, &
- & velocityDispersionSquare
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- ! Use the original, unheated profile velocity dispersion.
- heatedRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else if (self%velocityDispersionApproximate) then
- ! Use the approximate solution for velocity dispersion.
- radiusInitial = self%radiusInitial (node,radius )
- energySpecific = self%darkMatterProfileHeating_%specificEnergy (node,radiusInitial,self%darkMatterProfileDMO_)
- velocityDispersionSquare =+self%darkMatterProfileDMO_ %radialVelocityDispersion (node,radiusInitial )**2 &
- & -2.0d0/3.0d0*energySpecific
- heatedRadialVelocityDispersion=sqrt(max(0.0d0,velocityDispersionSquare))
- else
- ! Use a numerical solution.
- heatedRadialVelocityDispersion=+self %radialVelocityDispersionNumerical(node,radius )
- end if
- return
- end function heatedRadialVelocityDispersion
-
- double precision function heatedJeansEquationIntegrand(self,node,radius)
- !!{
- Integrand for generic dark matter profile Jeans equation. Here we do the integration with respect to the
- initial radius $r_i$.
- \begin{eqnarray}
- \sigma_r(r) &=& \frac{1}{\rho(r)}\int_r^{r^{\mathrm{max}}} \rho(r) \frac{\mathrm{G} M(r)}{r^2} \mathrm{d} r \nonumber \\
- &=& \frac{1}{\rho(r)}\int_{r_i}^{r_{i}^{\mathrm{max}}} \rho_i(r_i) \frac{\mathrm{G} M(r_i)}{r_i^2}\left(\frac{r_i}{r}\right)^4 \mathrm{d} r_i.
- \end{eqnarray}
- Here $r$ can be written as a function of $r_i$
- \begin{equation}
- r=\frac{1}{1/r_i-2\epsilon(r_i)/(\mathrm{G}M(r_i))}.
- \end{equation}
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusFinal , energySpecific, &
- & enclosedMass
-
- enclosedMass =self%darkMatterProfileDMO_ %enclosedMass (node,radius )
- energySpecific=self%darkMatterProfileHeating_%specificEnergy(node,radius,self%darkMatterProfileDMO_)
- radiusFinal =+1.0d0 &
- & /( &
- & +1.0d0/radius &
- & -2.0d0*energySpecific/gravitationalConstantGalacticus/enclosedMass &
- & )
- if (radiusFinal > 0.0d0) then
- heatedJeansEquationIntegrand=+gravitationalConstantGalacticus &
- & *enclosedMass &
- & *self%darkMatterProfileDMO_%density(node,radius) &
- & / radius **2 &
- & *(radius/radiusFinal)**4
- else
- heatedJeansEquationIntegrand=0.0d0
- end if
- return
- end function heatedJeansEquationIntegrand
-
- double precision function heatedJeansEquationRadius(self,node,radius)
- !!{
- Return the radius variable used in solving the Jeans equation that corresponds to a given physical radius.
- Here we do the integration with respect to the initial radius, so return the initial radius.
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- heatedJeansEquationRadius=self%radiusInitial(node,radius)
- return
- end function heatedJeansEquationRadius
-
- double precision function heatedRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- heatedRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function heatedRadiusFromSpecificAngularMomentum
-
- double precision function heatedRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization (node)
- else
- heatedRotationNormalization=self %rotationNormalizationNumerical(node)
- end if
- return
- end function heatedRotationNormalization
-
- double precision function heatedEnergy(self,node)
- !!{
- Return the energy of a heated halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- heatedEnergy=self %energyNumerical(node)
- end if
- return
- end function heatedEnergy
-
- double precision function heatedKSpace(self,node,waveNumber)
+ function heatedGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the Fourier transform of the heated density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$), using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalHeated, kinematicsDistributionHeated, massDistributionSpherical, massDistributionHeatingClass
implicit none
- class (darkMatterProfileDMOHeated), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- heatedKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function heatedKSpace
-
- double precision function heatedFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the heated density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- heatedFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function heatedFreefallRadius
-
- double precision function heatedFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the heated density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOHeated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionHeated), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOHeated ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ class (massDistributionHeatingClass), pointer :: massDistributionHeating_
+ !![
+
+ !!]
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- heatedFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalHeated :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalHeated)
+ massDistributionDecorated => self%darkMatterProfileDMO_ %get(node,weightBy,weightIndex)
+ massDistributionHeating_ => self%darkMatterProfileHeating_%get(node )
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalHeated( &
+ & nonAnalyticSolver =self%nonAnalyticSolver , &
+ & tolerateVelocityMaximumFailure=self%tolerateVelocityMaximumFailure, &
+ & massDistribution_ = massDistributionDecorated , &
+ & massDistributionHeating_ = massDistributionHeating_ , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionHeated( &
+ & nonAnalyticSolver =self%nonAnalyticSolver , &
+ & velocityDispersionApproximate =self%velocityDispersionApproximate , &
+ & toleranceRelativeVelocityDispersion =self%toleranceRelativeVelocityDispersion , &
+ & toleranceRelativeVelocityDispersionMaximum=self%toleranceRelativeVelocityDispersionMaximum &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function heatedFreefallRadiusIncreaseRate
+ end function heatedGet
diff --git a/source/dark_matter_profiles_DMO.heated.monotonic.F90 b/source/dark_matter_profiles_DMO.heated.monotonic.F90
index 5e8c76b2d5..29c4605fcc 100644
--- a/source/dark_matter_profiles_DMO.heated.monotonic.F90
+++ b/source/dark_matter_profiles_DMO.heated.monotonic.F90
@@ -21,79 +21,29 @@
An implementation of heated dark matter halo profiles based on the energy ordering of shells.
!!}
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversType
+
!![
- A dark matter profile DMO class in which dark matter halos start out with a density profile defined by another {\normalfont
- \ttfamily darkMatterProfileDMO}. This profile is then modified by heating, under the assumption that the
- energy of a shell of mass before and after heating are related by
- \begin{equation}
- -{ \mathrm{G} M^\prime(r^\prime) \over r^\prime } = -{ \mathrm{G} M(r) \over r } + 2 \epsilon(r),
- \end{equation}
- where $M(r)$ is the mass enclosed within a radius $r$, and $\epsilon(r)$ represents the specific heating in the shell
- initially at radius $r$. Primes indicate values after heating, while unprimed variables indicate quantities prior to
- heating.
-
- The above equation can be re-written as
- \begin{equation}
- -r^{\prime -1} = -r^{-1} + \xi(r),
- \end{equation}
- where $\xi(r) = 2 \epsilon(r)/[\mathrm{G} M(r)/r]$ measures the perturbation to the shell. To avoid shell crossing a
- monotonicity relation $r_1 < r_2 \implies \xi(r_1) \le \xi(r_2)$ is enforced by starting at large radius and stepping inward,
- enforcing the condition in the next innermost shell as necessary.
-
- Not all methods have analytic solutions for this profile. If {\normalfont \ttfamily [nonAnalyticSolver]}$=${\normalfont
- \ttfamily fallThrough} then attempts to call these methods in heated profiles will simply return the result from the
- unheated profile, otherwise a numerical calculation is performed.
+ A dark matter profile DMO class in which builds \refClass{massDistributionSphericalHeatedMonotonic} objects to account for
+ heating of some other dark matter profile.
!!]
-
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
- use :: Kind_Numbers , only : kind_int8
- use :: Numerical_Interpolation , only : interpolator
-
type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOHeatedMonotonic
!!{
A dark matter halo profile class implementing heated dark matter halos.
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_ => null()
- integer (kind=kind_int8 ) :: lastUniqueID
- type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
- double precision :: radiusInitialMinimum , radiusInitialMaximum, &
- & radiusFinalMinimum , radiusFinalMaximum
- type (interpolator ), allocatable :: massProfile
- logical :: isBound
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_ => null()
+ type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
+ double precision :: toleranceRelativeVelocityDispersion , toleranceRelativeVelocityDispersionMaximum
contains
- !![
-
-
-
-
- !!]
- final :: heatedMonotonicDestructor
- procedure :: autoHook => heatedMonotonicAutoHook
- procedure :: calculationReset => heatedMonotonicCalculationReset
- procedure :: computeSolution => heatedMonotonicComputeSolution
- procedure :: density => heatedMonotonicDensity
- procedure :: densityLogSlope => heatedMonotonicDensityLogSlope
- procedure :: radiusEnclosingDensity => heatedMonotonicRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => heatedMonotonicRadiusEnclosingMass
- procedure :: radialMoment => heatedMonotonicRadialMoment
- procedure :: enclosedMass => heatedMonotonicEnclosedMass
- procedure :: potential => heatedMonotonicPotential
- procedure :: circularVelocity => heatedMonotonicCircularVelocity
- procedure :: radiusCircularVelocityMaximum => heatedMonotonicRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => heatedMonotonicCircularVelocityMaximum
- procedure :: radialVelocityDispersion => heatedMonotonicRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => heatedMonotonicRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => heatedMonotonicRotationNormalization
- procedure :: energy => heatedMonotonicEnergy
- procedure :: kSpace => heatedMonotonicKSpace
- procedure :: freefallRadius => heatedMonotonicFreefallRadius
- procedure :: freefallRadiusIncreaseRate => heatedMonotonicFreefallRadiusIncreaseRate
+ final :: heatedMonotonicDestructor
+ procedure :: get => heatedMonotonicGet
end type darkMatterProfileDMOHeatedMonotonic
interface darkMatterProfileDMOHeatedMonotonic
@@ -110,7 +60,8 @@ function heatedMonotonicConstructorParameters(parameters) result(self)
!!{
Default constructor for the {\normalfont \ttfamily heatedMonotonic} dark matter halo profile class.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
+ use :: Input_Parameters , only : inputParameter, inputParameters
implicit none
type (darkMatterProfileDMOHeatedMonotonic) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -118,8 +69,7 @@ function heatedMonotonicConstructorParameters(parameters) result(self)
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileHeatingClass ), pointer :: darkMatterProfileHeating_
type (varying_string ) :: nonAnalyticSolver
- double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum, &
- & toleranceRelativePotential
+ double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
!![
@@ -138,19 +88,13 @@ function heatedMonotonicConstructorParameters(parameters) result(self)
toleranceRelativeVelocityDispersionMaximum
1.0d-3
- The maximum allowed relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles before aborting.
-
-
- toleranceRelativePotential
- 1.0d-3
-
- The maximum allowed relative tolerance to use in numerical solutions for the gravitational potential in dark-matter-only density profiles before aborting.
+ The maximum relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
!!]
- self=darkMatterProfileDMOHeatedMonotonic(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,toleranceRelativePotential,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_)
+ self=darkMatterProfileDMOHeatedMonotonic(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_)
!![
@@ -160,53 +104,32 @@ function heatedMonotonicConstructorParameters(parameters) result(self)
return
end function heatedMonotonicConstructorParameters
- function heatedMonotonicConstructorInternal(nonAnalyticSolver,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,toleranceRelativePotential,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_) result(self)
+ function heatedMonotonicConstructorInternal(nonAnalyticSolver,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileHeating_) result(self)
!!{
Generic constructor for the {\normalfont \ttfamily heatedMonotonic} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversIsValid
+ use :: Error , only : Error_Report
implicit none
type (darkMatterProfileDMOHeatedMonotonic) :: self
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileHeatingClass ), intent(in ), target :: darkMatterProfileHeating_
type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
- double precision , intent(in ) :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum, &
- & toleranceRelativePotential
+ double precision , intent(in ) :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
!![
-
+
!!]
! Validate.
- if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- ! Construct the object.
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%radiusInitialMinimum=+huge(0.0d0)
- self%radiusInitialMaximum=-huge(0.0d0)
- self%radiusFinalMinimum =+huge(0.0d0)
- self%radiusFinalMaximum =-huge(0.0d0)
- self%isBound =.true.
+ if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
return
end function heatedMonotonicConstructorInternal
- subroutine heatedMonotonicAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
-
- call calculationResetEvent%attach(self,heatedMonotonicCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOHeatedMonotonic')
- return
- end subroutine heatedMonotonicAutoHook
-
subroutine heatedMonotonicDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily heatedMonotonic} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
@@ -215,487 +138,76 @@ subroutine heatedMonotonicDestructor(self)
!!]
- if (calculationResetEvent%isAttached(self,heatedMonotonicCalculationReset)) call calculationResetEvent%detach(self,heatedMonotonicCalculationReset)
return
end subroutine heatedMonotonicDestructor
- subroutine heatedMonotonicCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers , only : kind_int8
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- ! Reset calculations for this profile.
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%isBound =.true.
- self%radiusInitialMinimum =+huge(0.0d0)
- self%radiusInitialMaximum =-huge(0.0d0)
- self%radiusFinalMinimum =+huge(0.0d0)
- self%radiusFinalMaximum =-huge(0.0d0)
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum=+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum=-huge(0.0d0)
- if (allocated(self%massProfile )) deallocate(self%massProfile )
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine heatedMonotonicCalculationReset
-
- subroutine heatedMonotonicComputeSolution(self,node,radius)
- !!{
- Compute the solution for the heated density profile.
- !!}
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Table_Labels , only : extrapolationTypeFix , extrapolationTypeZero
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusFractionMinimum=1.0d-6, radiusFractionMaximum=10.0d0
- integer , parameter :: countPerDecadeRadius =100
- double precision , allocatable, dimension(:) :: massEnclosed , massShell , &
- & radiusInitial , radiusFinal , &
- & energyFinal , perturbation
- logical , allocatable, dimension(:) :: isBound
- integer :: i , countRadii
-
- ! Determine if we need to retabulate.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Nothing to do if profile is already tabulated.
- if (allocated(self%massProfile)) return
- ! Choose extent of radii at which to tabulate the initial profile.
- self%radiusInitialMinimum=radiusFractionMinimum*self%darkMatterHaloScale_%radiusVirial(node)
- self%radiusInitialMaximum=radiusFractionMaximum*self%darkMatterHaloScale_%radiusVirial(node)
- ! Build grid of radii.
- countRadii=int(log10(self%radiusInitialMaximum/self%radiusInitialMinimum)*dble(countPerDecadeRadius)+1.0d0)
- if (allocated(radiusInitial)) then
- deallocate(radiusInitial)
- deallocate(radiusFinal )
- deallocate(massEnclosed )
- deallocate(massShell )
- deallocate(energyFinal )
- deallocate(perturbation )
- end if
- allocate(radiusInitial(countRadii))
- allocate(radiusFinal (countRadii))
- allocate(massEnclosed (countRadii))
- allocate(massShell (countRadii))
- allocate(energyFinal (countRadii))
- allocate(perturbation (countRadii))
- radiusInitial=Make_Range(self%radiusInitialMinimum,self%radiusInitialMaximum,countRadii,rangeTypeLogarithmic)
- ! Evaluate masses and energies of shells.
- do i=countRadii,1,-1
- massEnclosed(i)=+self%darkMatterProfileDMO_ %enclosedMass (node,radiusInitial(i) )
- perturbation(i)=+2.0d0 &
- & *self%darkMatterProfileHeating_ %specificEnergy(node,radiusInitial(i),self%darkMatterProfileDMO_) &
- & /gravitationalConstantGalacticus &
- & / massEnclosed (i) &
- & * radiusInitial(i)
- ! Limit the perturbation to avoid shell-crossing.
- if (i < countRadii) &
- & perturbation(i)=min( &
- & +perturbation (i ), &
- & +1.0d0 &
- & -radiusInitial (i ) &
- & /radiusInitial (i+1) &
- & *( &
- & +massEnclosed(i ) &
- & /massEnclosed(i+1) &
- & )**(-1.0d0/3.0d0) &
- & *( &
- & +1.0d0 &
- & -perturbation(i+1) &
- & ) &
- & )
- end do
- ! Compute the final energy of the heated profile.
- energyFinal=+gravitationalConstantGalacticus &
- & *massEnclosed &
- & /radiusInitial &
- & *( &
- & -1.0d0 &
- & +perturbation &
- & )
- ! Find shell masses.
- massShell(1 )=+massEnclosed(1 )
- massShell(2:countRadii)=+massEnclosed(2:countRadii ) &
- & -massEnclosed(1:countRadii-1)
- ! Evaluation boundedness.
- isBound= energyFinal < 0.0d0 &
- & .and. &
- & massShell > 0.0d0
- ! Find final radii.
- where (isBound)
- radiusFinal=-gravitationalConstantGalacticus &
- & *massEnclosed &
- & /energyFinal
- elsewhere
- radiusFinal=+huge(0.0d0)
- end where
- ! Build the final profile interpolator.
- self%isBound=count(isBound) > 2
- if (self%isBound) then
- self%radiusFinalMinimum =minval(radiusFinal ,mask=isBound)
- self%radiusFinalMaximum =maxval(radiusFinal ,mask=isBound)
- ! Construct the interpolator.
- if (allocated(self%massProfile)) deallocate(self%massProfile)
- allocate(self%massProfile)
- self%massProfile=interpolator( &
- & x =log(pack(radiusFinal ,mask=isBound)), &
- & y =log(pack(massEnclosed,mask=isBound)), &
- & extrapolationType=extrapolationTypeFix &
- & )
- end if
- return
- end subroutine heatedMonotonicComputeSolution
-
- double precision function heatedMonotonicEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_)) then
- ! No heating - use the unheated solution.
- heatedMonotonicEnclosedMass=self%darkMatterProfileDMO_%enclosedMass(node,radius)
- else if (radius <= 0.0d0) then
- ! Non-positive radius, mass must be zero.
- heatedMonotonicEnclosedMass=0.0d0
- else
- ! Compute the solution (as needed).
- call self%computeSolution(node,radius)
- ! For bound halos, interpolate to find the enclosed mass. For unbound halos the enclosed mass is zero.
- if (self%isBound) then
- if (radius < self%radiusFinalMinimum) then
- ! Assume constant density below the minimum radius.
- heatedMonotonicEnclosedMass=+exp(self%massProfile%interpolate(log(self%radiusFinalMinimum))) &
- & *( &
- & + radius &
- & /self%radiusFinalMinimum &
- & )**3
- else
- heatedMonotonicEnclosedMass=+exp(self%massProfile%interpolate(log(radius)))
- end if
- else
- heatedMonotonicEnclosedMass=0.0d0
- end if
- end if
- return
- end function heatedMonotonicEnclosedMass
-
- double precision function heatedMonotonicDensity(self,node,radius)
+ function heatedMonotonicGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radius_
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_)) then
- ! No heating - use the unheated solution.
- heatedMonotonicDensity=self%darkMatterProfileDMO_%density(node,radius)
- else
- call self%computeSolution(node,radius)
- ! For bound halos, interpolate to find the density. For unbound halos the density is zero.
- if (self%isBound) then
- radius_ =max(radius,self%radiusFinalMinimum)
- heatedMonotonicDensity=+ self%massProfile%derivative (log(radius_)) &
- & *exp(self%massProfile%interpolate(log(radius_))) &
- & /4.0d0 &
- & /Pi &
- & /radius**3
- else
- heatedMonotonicDensity=+0.0d0
- end if
- end if
- return
- end function heatedMonotonicDensity
-
- double precision function heatedMonotonicDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicDensityLogSlope=self%darkMatterProfileDMO_%densityLogSlope (node,radius)
- else
- heatedMonotonicDensityLogSlope=self %densityLogSlopeNumerical(node,radius)
- end if
- return
- end function heatedMonotonicDensityLogSlope
-
- double precision function heatedMonotonicRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- heatedMonotonicRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function heatedMonotonicRadiusEnclosingDensity
-
- double precision function heatedMonotonicRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- heatedMonotonicRadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function heatedMonotonicRadiusEnclosingMass
-
- double precision function heatedMonotonicRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalHeatedMonotonic, kinematicsDistributionCollisionless, massDistributionSpherical, massDistributionHeatingClass
implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionCollisionless), pointer :: kinematicsDistribution_
class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- heatedMonotonicRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function heatedMonotonicRadialMoment
-
- double precision function heatedMonotonicPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- heatedMonotonicPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function heatedMonotonicPotential
-
- double precision function heatedMonotonicCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (radius > 0.0d0) then
- heatedMonotonicCircularVelocity=sqrt( &
- & +gravitationalConstantGalacticus &
- & *self%enclosedMass(node,radius) &
- & / radius &
- & )
- else
- heatedMonotonicCircularVelocity=0.0d0
- end if
- return
- end function heatedMonotonicCircularVelocity
-
- double precision function heatedMonotonicRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- heatedMonotonicRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function heatedMonotonicRadiusCircularVelocityMaximum
-
- double precision function heatedMonotonicCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- heatedMonotonicCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function heatedMonotonicCircularVelocityMaximum
-
- double precision function heatedMonotonicRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion (node,radius)
- else
- heatedMonotonicRadialVelocityDispersion=self %radialVelocityDispersionNumerical(node,radius)
- end if
- return
- end function heatedMonotonicRadialVelocityDispersion
-
- double precision function heatedMonotonicRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- heatedMonotonicRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function heatedMonotonicRadiusFromSpecificAngularMomentum
-
- double precision function heatedMonotonicRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization (node)
- else
- heatedMonotonicRotationNormalization=self %rotationNormalizationNumerical(node)
- end if
- return
- end function heatedMonotonicRotationNormalization
-
- double precision function heatedMonotonicEnergy(self,node)
- !!{
- Return the energy of a heated halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- heatedMonotonicEnergy=self %energyNumerical(node)
- end if
- return
- end function heatedMonotonicEnergy
-
- double precision function heatedMonotonicKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the heated density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$), using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- heatedMonotonicKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function heatedMonotonicKSpace
-
- double precision function heatedMonotonicFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the heated density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- heatedMonotonicFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function heatedMonotonicFreefallRadius
-
- double precision function heatedMonotonicFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the heated density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOHeatedMonotonic), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ class (massDistributionHeatingClass ), pointer :: massDistributionHeating_
+ !![
+
+ !!]
- if (self%darkMatterProfileHeating_%specificEnergyIsEverywhereZero(node,self%darkMatterProfileDMO_) .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- heatedMonotonicFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- heatedMonotonicFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalHeatedMonotonic :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalHeatedMonotonic)
+ massDistributionDecorated => self%darkMatterProfileDMO_ %get(node,weightBy,weightIndex)
+ massDistributionHeating_ => self%darkMatterProfileHeating_%get(node )
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalHeatedMonotonic( &
+ & radiusVirial =self%darkMatterHaloScale_%radiusVirial (node), &
+ & nonAnalyticSolver =self %nonAnalyticSolver , &
+ & massDistribution_ = massDistributionDecorated , &
+ & massDistributionHeating_= massDistributionHeating_ , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionCollisionless( &
+ & toleranceRelativeVelocityDispersion =self%toleranceRelativeVelocityDispersion , &
+ & toleranceRelativeVelocityDispersionMaximum=self%toleranceRelativeVelocityDispersionMaximum &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function heatedMonotonicFreefallRadiusIncreaseRate
+ end function heatedMonotonicGet
diff --git a/source/dark_matter_profiles_DMO.isothermal.F90 b/source/dark_matter_profiles_DMO.isothermal.F90
index d13aa5d97e..5c50c49ebb 100644
--- a/source/dark_matter_profiles_DMO.isothermal.F90
+++ b/source/dark_matter_profiles_DMO.isothermal.F90
@@ -21,14 +21,13 @@
An implementation of isothermal dark matter halo profiles.
!!}
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
- A dark matter profile DMO class in which the density profile is given by:
- \begin{equation}
- \rho_\mathrm{dark matter}(r) \propto r^{-2},
- \end{equation}
- normalized such that the total mass of the \gls{node} is enclosed with the virial radius.
+ A dark matter profile DMO class in which builds \refClass{} objects to implement isothermal density profiles, normalized such
+ that the total mass of the \gls{node} is enclosed with the virial radius.
!!]
@@ -37,24 +36,10 @@
A dark matter halo profile class implementing isothermal dark matter halos.
!!}
private
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
- final :: isothermalDestructor
- procedure :: density => isothermalDensity
- procedure :: densityLogSlope => isothermalDensityLogSlope
- procedure :: radialMoment => isothermalRadialMoment
- procedure :: enclosedMass => isothermalEnclosedMass
- procedure :: radiusEnclosingDensity => isothermalRadiusEnclosingDensity
- procedure :: potential => isothermalPotential
- procedure :: circularVelocity => isothermalCircularVelocity
- procedure :: radiusCircularVelocityMaximum => isothermalRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => isothermalCircularVelocityMaximum
- procedure :: radialVelocityDispersion => isothermalRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => isothermalRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => isothermalRotationNormalization
- procedure :: energy => isothermalEnergy
- procedure :: kSpace => isothermalKSpace
- procedure :: freefallRadius => isothermalFreefallRadius
- procedure :: freefallRadiusIncreaseRate => isothermalFreefallRadiusIncreaseRate
+ final :: isothermalDestructor
+ procedure :: get => isothermalGet
end type darkMatterProfileDMOIsothermal
interface darkMatterProfileDMOIsothermal
@@ -115,308 +100,60 @@ subroutine isothermalDestructor(self)
return
end subroutine isothermalDestructor
- double precision function isothermalDensity(self,node,radius)
+ function isothermalGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
- use :: Numerical_Constants_Math, only : Pi
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionIsothermal, kinematicsDistributionIsothermal
implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
-
- basic => node %basic()
- isothermalDensity = basic%mass ()/4.0d0/Pi/self%darkMatterHaloScale_%radiusVirial(node)/radius**2
- return
- end function isothermalDensity
-
- double precision function isothermalDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- isothermalDensityLogSlope=-2.0d0
- return
- end function isothermalDensityLogSlope
-
- double precision function isothermalRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given
- in units of Mpc).
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
- use :: Numerical_Comparison , only : Values_Agree
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- class (nodeComponentBasic ) , pointer :: basic
- double precision :: radiusMinimumActual, radiusMaximumActual
-
- radiusMinimumActual=0.0d0
- radiusMaximumActual=self%darkMatterHaloScale_%radiusVirial(node)
- if (present(radiusMinimum)) radiusMinimumActual=radiusMinimum
- if (present(radiusMaximum)) radiusMaximumActual=radiusMaximum
- basic => node%basic ()
- if (Values_Agree(moment,1.0d0,absTol=1.0d-6)) then
- isothermalRadialMoment=+basic%mass() &
- & /4.0d0 &
- & /Pi &
- & /self%darkMatterHaloScale_%radiusVirial(node) &
- & *log( &
- & +radiusMaximumActual &
- & /radiusMinimumActual &
- & )
- else
- isothermalRadialMoment=+basic%mass() &
- & /4.0d0 &
- & /Pi &
- & /self%darkMatterHaloScale_%radiusVirial(node) &
- & / (moment-1.0d0) &
- & *( &
- & +radiusMaximumActual**(moment-1.0d0) &
- & -radiusMinimumActual**(moment-1.0d0) &
- & )
- end if
- return
- end function isothermalRadialMoment
-
- double precision function isothermalEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, treeNode
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
-
- basic => node%basic ()
- isothermalEnclosedMass=basic%mass()*(radius/self%darkMatterHaloScale_%radiusVirial(node))
- return
- end function isothermalEnclosedMass
-
- double precision function isothermalPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Galactic_Structure_Options, only : enumerationStructureErrorCodeType, structureErrorCodeInfinite, structureErrorCodeSuccess
- implicit none
- class (darkMatterProfileDMOIsothermal ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- double precision , parameter :: radiusFractionalMinimum=1.0d-30
- double precision :: radiusFractional
-
- radiusFractional = radius/self%darkMatterHaloScale_%radiusVirial(node)
- if (radiusFractional <= 0.0d0) then
- isothermalPotential=0.0d0
- if (present(status)) status=structureErrorCodeInfinite
- else
- isothermalPotential=log(radiusFractional)*self%darkMatterHaloScale_%velocityVirial(node)**2
- if (present(status)) status=structureErrorCodeSuccess
- end if
- return
- end function isothermalPotential
-
- double precision function isothermalCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc). For an isothermal halo this is independent of radius and therefore equal to the virial velocity.
- !!}
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: radius
-
- isothermalCircularVelocity=self%darkMatterHaloScale_%velocityVirial(node)
- return
- end function isothermalCircularVelocity
-
- double precision function isothermalRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}. For an isothermal halo circular
- velocity is independent of radius, so a value of the virial radius is returned.
- !!}
- implicit none
- class(darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- isothermalRadiusCircularVelocityMaximum=self%darkMatterHaloScale_%radiusVirial(node)
- return
- end function isothermalRadiusCircularVelocityMaximum
-
- double precision function isothermalCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}. For an isothermal halo circular
- velocity is independent of radius.
- !!}
- implicit none
- class(darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- isothermalCircularVelocityMaximum=self%circularVelocity(node,0.0d0)
- return
- end function isothermalCircularVelocityMaximum
-
- double precision function isothermalRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius}
- (given in units of Mpc). For an isothermal halo this is independent of radius and equal to the virial velocity divided by $\sqrt(2)$.
- !!}
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: radius
-
- isothermalRadialVelocityDispersion=self%darkMatterHaloScale_%velocityVirial(node)/sqrt(2.0d0)
- return
- end function isothermalRadialVelocityDispersion
-
- double precision function isothermalRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc). For an isothermal halo, the circular velocity is constant (and therefore equal to the virial
- velocity). Therefore, $r = j/V_\mathrm{virial}$ where $j$(={\normalfont \ttfamily specificAngularMomentum}) is the specific angular momentum and
- $r$ the required radius.
- !!}
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- isothermalRadiusFromSpecificAngularMomentum=specificAngularMomentum/self%darkMatterHaloScale_%velocityVirial(node)
- return
- end function isothermalRadiusFromSpecificAngularMomentum
-
- double precision function isothermalRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- isothermalRotationNormalization=2.0d0/self%darkMatterHaloScale_%radiusVirial(node)
- return
- end function isothermalRotationNormalization
-
- double precision function isothermalEnergy(self,node)
- !!{
- Return the energy of an isothermal halo density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, treeNode
- implicit none
- class(darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(nodeComponentBasic ), pointer :: basic
-
- basic => node%basic ()
- isothermalEnergy = -0.5d0*basic%mass()*self%darkMatterHaloScale_%velocityVirial(node)**2
- return
- end function isothermalEnergy
-
- double precision function isothermalKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the isothermal density profile at the specified {\normalfont \ttfamily waveNumber} (given in Mpc$^{-1}$), using the
- expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; table~1).
- !!}
- use :: Exponential_Integrals, only : Sine_Integral
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
- double precision :: radiusScale, waveNumberScaleFree
-
- ! Get the scale radius (for which we use the virial radius).
- radiusScale = self%darkMatterHaloScale_%radiusVirial(node)
-
- ! Get the dimensionless wavenumber.
- waveNumberScaleFree=waveNumber*radiusScale
-
- ! Compute the Fourier transformed profile.
- isothermalKSpace=Sine_Integral(waveNumberScaleFree)/waveNumberScaleFree
-
- return
- end function isothermalKSpace
-
- double precision function isothermalFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the isothermal density profile at the specified {\normalfont \ttfamily time} (given in Gyr). For an isothermal
- potential, the freefall radius, $r_\mathrm{ff}(t)$, is:
- \begin{equation}
- r_\mathrm{ff}(t) = \sqrt{{2 \over \pi}} V_\mathrm{virial} t.
- \end{equation}
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- isothermalFreefallRadius=sqrt(2.0d0/Pi)*self%darkMatterHaloScale_%velocityVirial(node)*time&
- &/Mpc_per_km_per_s_To_Gyr
- return
- end function isothermalFreefallRadius
-
- double precision function isothermalFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the isothermal density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr). For an isothermal potential, the rate of increase of the freefall radius, $\dot{r}_\mathrm{ff}(t)$, is:
- \begin{equation}
- \dot{r}_\mathrm{ff}(t) = \sqrt{{2 \over \pi}} V_\mathrm{virial}.
- \end{equation}
- !!}
- use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
- !$GLC attributes unused :: time
-
- isothermalFreefallRadiusIncreaseRate=sqrt(2.0d0/Pi)*self%darkMatterHaloScale_%velocityVirial(node) &
- & /Mpc_per_km_per_s_To_Gyr
- return
- end function isothermalFreefallRadiusIncreaseRate
-
- double precision function isothermalRadiusEnclosingDensity(self,node,density)
- !!{
- Null implementation of function to compute the radius enclosing a given density for isothermal dark matter halo profiles.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOIsothermal), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
- class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionIsothermal), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOIsothermal ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentBasic ), pointer :: basic
+ !![
+
+ !!]
- basic => node %basic ( )
- isothermalRadiusEnclosingDensity = +sqrt( &
- & + basic %mass ( ) &
- & /4.0d0 &
- & *3.0d0 &
- & /Pi &
- & /self%darkMatterHaloScale_%radiusVirial(node) &
- & /density &
- & )
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionIsothermal :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionIsothermal)
+ basic => node%basic()
+ !![
+
+
+ massDistributionIsothermal( &
+ & mass =basic %mass ( ), &
+ & lengthReference=self %darkMatterHaloScale_%radiusVirial (node), &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionIsothermal( &
+ & velocityDispersion_=self %darkMatterHaloScale_%velocityVirial(node)/sqrt(2.0d0) &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function isothermalRadiusEnclosingDensity
-
+ end function isothermalGet
diff --git a/source/dark_matter_profiles_DMO.multiple.F90 b/source/dark_matter_profiles_DMO.multiple.F90
index 0f30ad3ca9..10adb6a5dc 100644
--- a/source/dark_matter_profiles_DMO.multiple.F90
+++ b/source/dark_matter_profiles_DMO.multiple.F90
@@ -38,24 +38,8 @@
private
class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMOHost_ => null(), darkMatterProfileDMOSatellite_ => null()
contains
- final :: multipleDestructor
- procedure :: density => multipleDensity
- procedure :: densityLogSlope => multipleDensityLogSlope
- procedure :: radiusEnclosingDensity => multipleRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => multipleRadiusEnclosingMass
- procedure :: radialMoment => multipleRadialMoment
- procedure :: enclosedMass => multipleEnclosedMass
- procedure :: potential => multiplePotential
- procedure :: circularVelocity => multipleCircularVelocity
- procedure :: radiusCircularVelocityMaximum => multipleRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => multipleCircularVelocityMaximum
- procedure :: radialVelocityDispersion => multipleRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => multipleRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => multipleRotationNormalization
- procedure :: energy => multipleEnergy
- procedure :: kSpace => multipleKSpace
- procedure :: freefallRadius => multipleFreefallRadius
- procedure :: freefallRadiusIncreaseRate => multipleFreefallRadiusIncreaseRate
+ final :: multipleDestructor
+ procedure :: get => multipleGet
end type darkMatterProfileDMOMultiple
interface darkMatterProfileDMOMultiple
@@ -118,302 +102,21 @@ subroutine multipleDestructor(self)
return
end subroutine multipleDestructor
- double precision function multipleDensity(self,node,radius)
+ function multipleGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (darkMatterProfileDMOMultiple), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
if (node%isSatellite()) then
- multipleDensity=self%darkMatterProfileDMOSatellite_%density(node,radius)
+ massDistribution_ => self%darkMatterProfileDMOSatellite_%get(node,weightBy,weightIndex)
else
- multipleDensity=self%darkMatterProfileDMOHost_ %density(node,radius)
+ massDistribution_ => self%darkMatterProfileDMOHost_ %get(node,weightBy,weightIndex)
end if
return
- end function multipleDensity
-
- double precision function multipleDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- multipleDensityLogSlope=self%darkMatterProfileDMOSatellite_%densityLogSlope(node,radius)
- else
- multipleDensityLogSlope=self%darkMatterProfileDMOHost_ %densityLogSlope(node,radius)
- end if
- return
- end function multipleDensityLogSlope
-
- double precision function multipleRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (node%isSatellite()) then
- multipleRadiusEnclosingDensity=self%darkMatterProfileDMOSatellite_%radiusEnclosingDensity(node,density)
- else
- multipleRadiusEnclosingDensity=self%darkMatterProfileDMOHost_ %radiusEnclosingDensity(node,density)
- end if
- return
- end function multipleRadiusEnclosingDensity
-
- double precision function multipleRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
-
- if (node%isSatellite()) then
- multipleRadiusEnclosingMass=self%darkMatterProfileDMOSatellite_%radiusEnclosingMass(node,mass)
- else
- multipleRadiusEnclosingMass=self%darkMatterProfileDMOHost_ %radiusEnclosingMass(node,mass)
- end if
- return
- end function multipleRadiusEnclosingMass
-
- double precision function multipleRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (node%isSatellite()) then
- multipleRadialMoment=self%darkMatterProfileDMOSatellite_%radialMoment(node,moment,radiusMinimum,radiusMaximum)
- else
- multipleRadialMoment=self%darkMatterProfileDMOHost_ %radialMoment(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function multipleRadialMoment
-
- double precision function multipleEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- multipleEnclosedMass=self%darkMatterProfileDMOSatellite_%enclosedMass(node,radius)
- else
- multipleEnclosedMass=self%darkMatterProfileDMOHost_ %enclosedMass(node,radius)
- end if
- return
- end function multipleEnclosedMass
-
- double precision function multiplePotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- if (node%isSatellite()) then
- multiplePotential=self%darkMatterProfileDMOSatellite_%potential(node,radius,status)
- else
- multiplePotential=self%darkMatterProfileDMOHost_ %potential(node,radius,status)
- end if
- return
- end function multiplePotential
-
- double precision function multipleCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- multipleCircularVelocity=self%darkMatterProfileDMOSatellite_%circularVelocity(node,radius)
- else
- multipleCircularVelocity=self%darkMatterProfileDMOHost_ %circularVelocity(node,radius)
- end if
- return
- end function multipleCircularVelocity
-
- double precision function multipleRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- multipleRadiusCircularVelocityMaximum=self%darkMatterProfileDMOSatellite_%radiusCircularVelocityMaximum(node)
- else
- multipleRadiusCircularVelocityMaximum=self%darkMatterProfileDMOHost_ %radiusCircularVelocityMaximum(node)
- end if
- return
- end function multipleRadiusCircularVelocityMaximum
-
- double precision function multipleCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- multipleCircularVelocityMaximum=self%darkMatterProfileDMOSatellite_%circularVelocityMaximum(node)
- else
- multipleCircularVelocityMaximum=self%darkMatterProfileDMOHost_ %circularVelocityMaximum(node)
- end if
- return
- end function multipleCircularVelocityMaximum
-
- double precision function multipleRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (node%isSatellite()) then
- multipleRadialVelocityDispersion=self%darkMatterProfileDMOSatellite_%radialVelocityDispersion(node,radius)
- else
- multipleRadialVelocityDispersion=self%darkMatterProfileDMOHost_ %radialVelocityDispersion(node,radius)
- end if
- return
- end function multipleRadialVelocityDispersion
-
- double precision function multipleRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (node%isSatellite()) then
- multipleRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMOSatellite_%radiusFromSpecificAngularMomentum(node,specificAngularMomentum)
- else
- multipleRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMOHost_ %radiusFromSpecificAngularMomentum(node,specificAngularMomentum)
- end if
- return
- end function multipleRadiusFromSpecificAngularMomentum
-
- double precision function multipleRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- multipleRotationNormalization=self%darkMatterProfileDMOSatellite_%rotationNormalization(node)
- else
- multipleRotationNormalization=self%darkMatterProfileDMOHost_ %rotationNormalization(node)
- end if
- return
- end function multipleRotationNormalization
-
- double precision function multipleEnergy(self,node)
- !!{
- Return the energy of a multiple halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (node%isSatellite()) then
- multipleEnergy=self%darkMatterProfileDMOSatellite_%energy(node)
- else
- multipleEnergy=self%darkMatterProfileDMOHost_ %energy(node)
- end if
- return
- end function multipleEnergy
-
- double precision function multipleKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the multiple density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (node%isSatellite()) then
- multipleKSpace=self%darkMatterProfileDMOSatellite_%kSpace(node,waveNumber)
- else
- multipleKSpace=self%darkMatterProfileDMOHost_ %kSpace(node,waveNumber)
- end if
- return
- end function multipleKSpace
-
- double precision function multipleFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the multiple density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (node%isSatellite()) then
- multipleFreefallRadius=self%darkMatterProfileDMOSatellite_%freefallRadius(node,time)
- else
- multipleFreefallRadius=self%darkMatterProfileDMOHost_ %freefallRadius(node,time)
- end if
- return
- end function multipleFreefallRadius
-
- double precision function multipleFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the multiple density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOMultiple), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (node%isSatellite()) then
- multipleFreefallRadiusIncreaseRate=self%darkMatterProfileDMOSatellite_%freefallRadiusIncreaseRate(node,time)
- else
- multipleFreefallRadiusIncreaseRate=self%darkMatterProfileDMOHost_ %freefallRadiusIncreaseRate(node,time)
- end if
- return
- end function multipleFreefallRadiusIncreaseRate
+ end function multipleGet
diff --git a/source/dark_matter_profiles_DMO.truncated.F90 b/source/dark_matter_profiles_DMO.truncated.F90
index 3269efcdfa..db4bf40542 100644
--- a/source/dark_matter_profiles_DMO.truncated.F90
+++ b/source/dark_matter_profiles_DMO.truncated.F90
@@ -21,11 +21,14 @@
An implementation of truncated dark matter halo profiles.
!!}
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
-
+ use :: Mass_Distributions , only : enumerationNonAnalyticSolversType
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
- truncated dark matter halo profiles.
+
+ Truncated dark matter halo profiles are built via the \refClass{massDistributionSphericalTruncated} class.
+
!!]
type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOTruncated
@@ -33,43 +36,13 @@
A dark matter halo profile class implementing truncated dark matter halos.
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- double precision :: radiusFractionalTruncateMinimum , radiusFractionalTruncateMaximum
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ double precision :: radiusFractionalTruncateMinimum , radiusFractionalTruncateMaximum
type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
- ! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID
- ! Stored values of computed quantities.
- double precision :: enclosedMassTruncateMinimumPrevious , enclosedMassTruncateMaximumPrevious , &
- & enclosingMassRadiusPrevious , radialVelocityDispersionTruncateMinimumPrevious, &
- & radialVelocityDispersionTruncateMinimumUntruncatedPrevious
contains
- !![
-
-
-
-
- !!]
- final :: truncatedDestructor
- procedure :: autoHook => truncatedAutoHook
- procedure :: calculationReset => truncatedCalculationReset
- procedure :: density => truncatedDensity
- procedure :: densityLogSlope => truncatedDensityLogSlope
- procedure :: radiusEnclosingDensity => truncatedRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => truncatedRadiusEnclosingMass
- procedure :: radialMoment => truncatedRadialMoment
- procedure :: enclosedMass => truncatedEnclosedMass
- procedure :: potential => truncatedPotential
- procedure :: circularVelocity => truncatedCircularVelocity
- procedure :: circularVelocityMaximum => truncatedCircularVelocityMaximum
- procedure :: radiusCircularVelocityMaximum => truncatedRadiusCircularVelocityMaximum
- procedure :: radialVelocityDispersion => truncatedRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => truncatedRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => truncatedRotationNormalization
- procedure :: energy => truncatedEnergy
- procedure :: kSpace => truncatedKSpace
- procedure :: freefallRadius => truncatedFreefallRadius
- procedure :: freefallRadiusIncreaseRate => truncatedFreefallRadiusIncreaseRate
- procedure :: truncationFunction => truncatedTruncationFunction
+ final :: truncatedDestructor
+ procedure :: get => truncatedGet
end type darkMatterProfileDMOTruncated
interface darkMatterProfileDMOTruncated
@@ -86,7 +59,8 @@ function truncatedConstructorParameters(parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily truncated} dark matter halo profile class which takes a parameter set as input.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Input_Parameters , only : inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
implicit none
type (darkMatterProfileDMOTruncated) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -130,7 +104,8 @@ function truncatedConstructorInternal(radiusFractionalTruncateMinimum,radiusFrac
!!{
Internal constructor for the {\normalfont \ttfamily truncated} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Error , only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversIsValid
implicit none
type (darkMatterProfileDMOTruncated ) :: self
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
@@ -143,28 +118,13 @@ function truncatedConstructorInternal(radiusFractionalTruncateMinimum,radiusFrac
! Validate.
if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID=-1_kind_int8
return
end function truncatedConstructorInternal
- subroutine truncatedAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOTruncated), intent(inout) :: self
-
- call calculationResetEvent%attach(self,truncatedCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOTruncated)')
- return
- end subroutine truncatedAutoHook
-
subroutine truncatedDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily truncated} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOTruncated), intent(inout) :: self
@@ -172,410 +132,73 @@ subroutine truncatedDestructor(self)
!!]
- if (calculationResetEvent%isAttached(self,truncatedCalculationReset)) call calculationResetEvent%detach(self,truncatedCalculationReset)
return
end subroutine truncatedDestructor
- subroutine truncatedCalculationReset(self,node,uniqueID)
- !!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%enclosingMassRadiusPrevious =-1.0d0
- self%enclosedMassTruncateMinimumPrevious =-1.0d0
- self%enclosedMassTruncateMaximumPrevious =-1.0d0
- self%radialVelocityDispersionTruncateMinimumPrevious =-1.0d0
- self%radialVelocityDispersionTruncateMinimumUntruncatedPrevious=-1.0d0
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum =+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum =-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine truncatedCalculationReset
-
- subroutine truncatedTruncationFunction(self,node,radius,x,multiplier,multiplierGradient)
- !!{
- Return the scaled truncation radial coordinate, and the truncation multiplier.
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , intent( out), optional :: x , multiplier, &
- & multiplierGradient
- double precision :: radiusVirial , x_
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
- if (radius <= radiusVirial*self%radiusFractionalTruncateMinimum) then
- if (present(x )) x =+0.0d0
- if (present(multiplier )) multiplier =+1.0d0
- if (present(multiplierGradient)) multiplierGradient=+0.0d0
- else if (radius >= radiusVirial*self%radiusFractionalTruncateMaximum) then
- if (present(x )) x =+1.0d0
- if (present(multiplier )) multiplier =+0.0d0
- if (present(multiplierGradient)) multiplierGradient=+0.0d0
- else
- x_ =+( radius /radiusVirial-self%radiusFractionalTruncateMinimum) &
- & /(self%radiusFractionalTruncateMaximum -self%radiusFractionalTruncateMinimum)
- if (present(x )) x =x_
- if (present(multiplier )) multiplier = +1.0d0 &
- & -3.0d0*x_**2 &
- & +2.0d0*x_**3
- if (present(multiplierGradient)) multiplierGradient=+( &
- & -6.0d0*x_ &
- & +6.0d0*x_**2 &
- & ) &
- & / radiusvirial &
- & /(self%radiusFractionalTruncateMaximum -self%radiusFractionalTruncateMinimum)
- end if
- return
- end subroutine truncatedTruncationFunction
-
- double precision function truncatedDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: multiplier
-
- call self%truncationFunction(node,radius,multiplier=multiplier)
- truncatedDensity=+self%darkMatterProfileDMO_%density(node,radius) &
- & *multiplier
- return
- end function truncatedDensity
-
- double precision function truncatedDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: multiplier, multiplierGradient
-
- call self%truncationFunction(node,radius,multiplier=multiplier,multiplierGradient=multiplierGradient)
- if (multiplier > 0.0d0) then
- truncatedDensityLogSlope=+self%darkMatterProfileDMO_%densityLogSlope(node,radius) &
- & +radius &
- & *multiplierGradient &
- & /multiplier
- else
- truncatedDensityLogSlope=+0.0d0
- end if
- return
- end function truncatedDensityLogSlope
-
- double precision function truncatedRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- truncatedRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function truncatedRadiusEnclosingDensity
-
- double precision function truncatedRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- double precision :: radiusVirial, radiusTruncateMinimum
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial =self%darkMatterHaloScale_%radiusVirial(node)
- radiusTruncateMinimum=radiusVirial*self%radiusFractionalTruncateMinimum
- if (self%enclosedMassTruncateMinimumPrevious < 0.0d0) then
- self%enclosedMassTruncateMinimumPrevious=self%enclosedMass(node,radiusTruncateMinimum)
- end if
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. mass <= self%enclosedMassTruncateMinimumPrevious) then
- truncatedRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- truncatedRadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function truncatedRadiusEnclosingMass
-
- double precision function truncatedRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- truncatedRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function truncatedRadialMoment
-
- double precision function truncatedEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusVirial, radiusTruncateMinimum
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial =self%darkMatterHaloScale_%radiusVirial(node)
- radiusTruncateMinimum=radiusVirial*self%radiusFractionalTruncateMinimum
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. radius <= radiusTruncateMinimum) then
- truncatedEnclosedMass=+self%darkMatterProfileDMO_%enclosedMass (node,radius )
- else
- truncatedEnclosedMass=+self%darkMatterProfileDMO_%enclosedMass (node,radiusTruncateMinimum ) &
- & +self %enclosedMassDifferenceNumerical(node,radiusTruncateMinimum,radius)
- end if
- return
- end function truncatedEnclosedMass
-
- double precision function truncatedPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- truncatedPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function truncatedPotential
-
- double precision function truncatedCircularVelocity(self,node,radius)
+ function truncatedGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalTruncated, kinematicsDistributionTruncated, massDistributionSpherical
implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedCircularVelocity=self%darkMatterProfileDMO_%circularVelocity (node,radius)
- else
- truncatedCircularVelocity=self %circularVelocityNumerical(node,radius)
- end if
- return
- end function truncatedCircularVelocity
-
- double precision function truncatedCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- truncatedCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function truncatedCircularVelocityMaximum
-
- double precision function truncatedRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- truncatedRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function truncatedRadiusCircularVelocityMaximum
-
- double precision function truncatedRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusVirial, radiusTruncateMinimum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial =self%darkMatterHaloScale_%radiusVirial(node)
- radiusTruncateMinimum=radiusVirial*self%radiusFractionalTruncateMinimum
- if (radius >= radiusTruncateMinimum) then
- truncatedRadialVelocityDispersion=self%radialVelocityDispersionNumerical(node,radius)
- else
- if (self%radialVelocityDispersionTruncateMinimumPrevious < 0.0d0 .or. self%radialVelocityDispersionTruncateMinimumUntruncatedPrevious < 0.0d0) then
- self%radialVelocityDispersionTruncateMinimumPrevious =self%radialVelocityDispersionNumerical (node,radiusTruncateMinimum)
- self%radialVelocityDispersionTruncateMinimumUntruncatedPrevious=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radiusTruncateMinimum)
- end if
- truncatedRadialVelocityDispersion=sqrt( &
- & +self%darkMatterProfileDMO_%radialVelocityDispersion (node,radius )**2 &
- & +self%density (node,radiusTruncateMinimum) &
- & /self%density (node,radius ) &
- & *( &
- & +self%radialVelocityDispersionTruncateMinimumPrevious **2 &
- & -self%radialVelocityDispersionTruncateMinimumUntruncatedPrevious **2 &
- & ) &
- & )
- end if
- end if
- return
- end function truncatedRadialVelocityDispersion
-
- double precision function truncatedRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- truncatedRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function truncatedRadiusFromSpecificAngularMomentum
-
- double precision function truncatedRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization (node)
- else
- truncatedRotationNormalization=self %rotationNormalizationNumerical(node)
- end if
- return
- end function truncatedRotationNormalization
-
- double precision function truncatedEnergy(self,node)
- !!{
- Return the energy of a truncated halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- truncatedEnergy=self %energyNumerical(node)
- end if
- return
- end function truncatedEnergy
-
- double precision function truncatedKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the truncated density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- truncatedKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function truncatedKSpace
-
- double precision function truncatedFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the truncated density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- truncatedFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function truncatedFreefallRadius
-
- double precision function truncatedFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the truncated density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncated), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionTruncated), pointer :: kinematicsDistribution_
+ class (darkMatterProfileDMOTruncated ), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ double precision :: radiusVirial
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ !![
+
+ !!]
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- truncatedFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalTruncated :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalTruncated)
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial(node )
+ massDistributionDecorated => self%darkMatterProfileDMO_%get (node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalTruncated( &
+ & radiusTruncateMinimum=self%radiusFractionalTruncateMinimum*radiusVirial, &
+ & radiusTruncateMaximum=self%radiusFractionalTruncateMaximum*radiusVirial, &
+ & nonAnalyticSolver =self%nonAnalyticSolver , &
+ & massDistribution_ = massDistributionDecorated , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionTruncated( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function truncatedFreefallRadiusIncreaseRate
+ end function truncatedGet
diff --git a/source/dark_matter_profiles_DMO.truncated.exponential.F90 b/source/dark_matter_profiles_DMO.truncated.exponential.F90
index 968613a669..30987b07aa 100644
--- a/source/dark_matter_profiles_DMO.truncated.exponential.F90
+++ b/source/dark_matter_profiles_DMO.truncated.exponential.F90
@@ -23,11 +23,15 @@
An implementation of exponentially truncated dark matter halo profiles \cite{kazantzidis_2006}.
!!}
- use :: Dark_Matter_Profiles_Generic, only : enumerationNonAnalyticSolversType, enumerationNonAnalyticSolversEncode, enumerationNonAnalyticSolversIsValid, nonAnalyticSolversFallThrough
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Mass_Distributions , only : enumerationNonAnalyticSolversType
!![
- exponentially truncated dark matter halo profiles \cite{kazantzidis_2006}.
+
+ Exponentially truncated dark matter halo profiles \cite{kazantzidis_2006} are constructed via the
+ \refClass{massDistributionSphericalTruncatedExponential} class.
+
!!]
type, extends(darkMatterProfileDMOClass) :: darkMatterProfileDMOTruncatedExponential
@@ -36,44 +40,12 @@
!!}
private
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- double precision :: radiusFractionalDecay , alpha , &
- & beta , gamma
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ double precision :: radiusFractionalDecay
type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
- ! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID
- ! Stored values of computed quantities.
- double precision :: radialVelocityDispersionVirialRadiusPrevious, radialVelocityDispersionVirialRadiusUntruncatedPrevious, &
- & enclosingMassRadiusPrevious , kappaPrevious , &
- & gammaFunctionIncompletePrevious , densityNormalizationPrevious , &
- & massVirialPrevious
contains
- !![
-
-
-
-
- !!]
- final :: truncatedExponentialDestructor
- procedure :: autoHook => truncatedExponentialAutoHook
- procedure :: calculationReset => truncatedExponentialCalculationReset
- procedure :: density => truncatedExponentialDensity
- procedure :: densityLogSlope => truncatedExponentialDensityLogSlope
- procedure :: radiusEnclosingDensity => truncatedExponentialRadiusEnclosingDensity
- procedure :: radiusEnclosingMass => truncatedExponentialRadiusEnclosingMass
- procedure :: radialMoment => truncatedExponentialRadialMoment
- procedure :: enclosedMass => truncatedExponentialEnclosedMass
- procedure :: potential => truncatedExponentialPotential
- procedure :: circularVelocity => truncatedExponentialCircularVelocity
- procedure :: radiusCircularVelocityMaximum => truncatedExponentialRadiusCircularVelocityMaximum
- procedure :: circularVelocityMaximum => truncatedExponentialCircularVelocityMaximum
- procedure :: radialVelocityDispersion => truncatedExponentialRadialVelocityDispersion
- procedure :: radiusFromSpecificAngularMomentum => truncatedExponentialRadiusFromSpecificAngularMomentum
- procedure :: rotationNormalization => truncatedExponentialRotationNormalization
- procedure :: energy => truncatedExponentialEnergy
- procedure :: kSpace => truncatedExponentialKSpace
- procedure :: freefallRadius => truncatedExponentialFreefallRadius
- procedure :: freefallRadiusIncreaseRate => truncatedExponentialFreefallRadiusIncreaseRate
- procedure :: truncationFunction => truncatedExponentialTruncationFunction
+ final :: truncatedExponentialDestructor
+ procedure :: get => truncatedExponentialGet
end type darkMatterProfileDMOTruncatedExponential
interface darkMatterProfileDMOTruncatedExponential
@@ -90,15 +62,15 @@ function truncatedExponentialConstructorParameters(parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily exponentially truncated} dark matter halo profile class which takes a parameter set as input.
!!}
- use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversEncode
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (darkMatterProfileDMOTruncatedExponential) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
type (varying_string ) :: nonAnalyticSolver
- double precision :: radiusFractionalDecay, alpha, &
- & beta , gamma
+ double precision :: radiusFractionalDecay
!![
@@ -112,29 +84,11 @@ function truncatedExponentialConstructorParameters(parameters) result(self)
1.0d0
The truncation scale (in units of the virial radius).
-
-
- alpha
- 1.0d0
-
- Parameter $\alpha$ in the \cite{kazantzidis_2006} truncated profile.
-
-
- beta
- 3.0d0
-
- Parameter $\beta$ in the \cite{kazantzidis_2006} truncated profile.
-
-
- gamma
- 1.0d0
-
- Parameter $\gamma$ in the \cite{kazantzidis_2006} truncated profile.
-
+
!!]
- self=darkMatterProfileDMOTruncatedExponential(radiusFractionalDecay,alpha,beta,gamma,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),darkMatterProfileDMO_,darkMatterHaloScale_)
+ self=darkMatterProfileDMOTruncatedExponential(radiusFractionalDecay,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),darkMatterProfileDMO_,darkMatterHaloScale_)
!![
@@ -143,50 +97,31 @@ function truncatedExponentialConstructorParameters(parameters) result(self)
return
end function truncatedExponentialConstructorParameters
- function truncatedExponentialConstructorInternal(radiusFractionalDecay,alpha,beta,gamma,nonAnalyticSolver,darkMatterProfileDMO_,darkMatterHaloScale_) result(self)
+ function truncatedExponentialConstructorInternal(radiusFractionalDecay,nonAnalyticSolver,darkMatterProfileDMO_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily exponentially truncated} dark matter profile class.
!!}
- use :: Error, only : Error_Report
+ use :: Error , only : Error_Report
+ use :: Mass_Distributions, only : enumerationNonAnalyticSolversIsValid
implicit none
type (darkMatterProfileDMOTruncatedExponential) :: self
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- double precision , intent(in ) :: radiusFractionalDecay, alpha, &
- & beta , gamma
+ double precision , intent(in ) :: radiusFractionalDecay
type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
!![
-
+
!!]
! Validate.
- if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
- self%lastUniqueID =-1_kind_int8
- self%genericLastUniqueID =-1_kind_int8
- self%kappaPrevious =-huge(0.0d0)
- self%enclosingMassRadiusPrevious =-1.0d0
- self%radialVelocityDispersionVirialRadiusPrevious =-1.0d0
- self%radialVelocityDispersionVirialRadiusUntruncatedPrevious=-1.0d0
+ if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
return
end function truncatedExponentialConstructorInternal
- subroutine truncatedExponentialAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
-
- call calculationResetEvent%attach(self,truncatedExponentialCalculationReset,openMPThreadBindingAllLevels,label='darkMatterProfileDMOTruncatedExponential')
- return
- end subroutine truncatedExponentialAutoHook
-
subroutine truncatedExponentialDestructor(self)
!!{
Destructor for the {\normalfont \ttfamily exponentially truncated} dark matter halo profile class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
@@ -194,459 +129,73 @@ subroutine truncatedExponentialDestructor(self)
!!]
- if (calculationResetEvent%isAttached(self,truncatedExponentialCalculationReset)) call calculationResetEvent%detach(self,truncatedExponentialCalculationReset)
return
end subroutine truncatedExponentialDestructor
- subroutine truncatedExponentialCalculationReset(self,node,uniqueID)
+ function truncatedExponentialGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Reset the dark matter profile calculation.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%lastUniqueID =uniqueID
- self%genericLastUniqueID =uniqueID
- self%kappaPrevious =-huge(0.0d0)
- self%enclosingMassRadiusPrevious =-1.0d0
- self%radialVelocityDispersionVirialRadiusPrevious =-1.0d0
- self%radialVelocityDispersionVirialRadiusUntruncatedPrevious=-1.0d0
- self%genericEnclosedMassRadiusMinimum =+huge(0.0d0)
- self%genericEnclosedMassRadiusMaximum =-huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMinimum =+huge(0.0d0)
- self%genericVelocityDispersionRadialRadiusMaximum =-huge(0.0d0)
- if (allocated(self%genericVelocityDispersionRadialVelocity)) deallocate(self%genericVelocityDispersionRadialVelocity)
- if (allocated(self%genericVelocityDispersionRadialRadius )) deallocate(self%genericVelocityDispersionRadialRadius )
- if (allocated(self%genericEnclosedMassMass )) deallocate(self%genericEnclosedMassMass )
- if (allocated(self%genericEnclosedMassRadius )) deallocate(self%genericEnclosedMassRadius )
- return
- end subroutine truncatedExponentialCalculationReset
-
- subroutine truncatedExponentialTruncationFunction(self,node,radius,multiplier,multiplierGradient)
- !!{
- Return the scaled truncation radial coordinate, and the truncation multiplier.
+ Return the dark matter mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galactic_Structure_Options, only : componentTypeDarkHalo , massTypeDark , weightByMass
+ use :: Mass_Distributions , only : massDistributionSphericalTruncatedExponential, kinematicsDistributionTruncated, massDistributionSpherical
implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionTruncated ), pointer :: kinematicsDistribution_
class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision , intent( out), optional :: multiplier , multiplierGradient
- double precision :: radiusVirial, radiusDecay , &
- & multiplier_
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
- if (radius <= radiusVirial) then
- if (present(multiplier )) multiplier =+1.0d0
- if (present(multiplierGradient)) multiplierGradient=+0.0d0
- else
- if (self%kappaPrevious == -huge(0.0d0)) call recomputeKappa(self,node)
- radiusDecay =+self%radiusFractionalDecay*radiusVirial
- multiplier_ =+self%darkMatterProfileDMO_%density(node,radiusVirial) &
- & /self%darkMatterProfileDMO_%density(node,radius ) &
- & *( &
- & +radius &
- & /radiusVirial &
- & )**self%kappaPrevious &
- & *exp( &
- & -( &
- & +radius &
- & -radiusVirial &
- & ) &
- & / radiusDecay &
- & )
- if (present(multiplier )) multiplier =+multiplier_
- if (present(multiplierGradient)) multiplierGradient=+multiplier_ &
- & *( &
- & +self%kappaPrevious /radius &
- & -1.0d0 /radiusDecay &
- & -self%darkMatterProfileDMO_%densityLogSlope(node,radius)/radius &
- & )
- end if
- return
- end subroutine truncatedExponentialTruncationFunction
-
- subroutine recomputeKappa (self,node)
- !!{
- Recompute parameter kappa in the truncation function.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile , treeNode
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Unnormalized
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision :: radiusVirial , scaleRadius, &
- & concentration
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial = self %darkMatterHaloScale_%radiusVirial(node )
- darkMatterProfile => node %darkMatterProfile (autoCreate=.true.)
- scaleRadius = darkMatterProfile%scale ( )
- concentration = +radiusVirial &
- & /scaleRadius
- self%kappaPrevious = -( &
- & +self%gamma &
- & +self%beta &
- & *concentration**self%alpha &
- & ) &
- & /( &
- & +1.0d0 &
- & +concentration**self%alpha &
- & ) &
- & +1.0d0 &
- & /self%radiusFractionalDecay
- self%massVirialPrevious =+self%darkMatterProfileDMO_%enclosedMass(node ,radiusVirial )
- self%densityNormalizationPrevious =+4.0d0 &
- & *Pi &
- & *self%darkMatterProfileDMO_%density (node ,radiusVirial ) &
- & *radiusVirial**3 &
- & *self%radiusFractionalDecay** (3.0d0+self%kappaPrevious ) &
- & *exp ( 1.0d0/self%radiusFractionalDecay)
- self%gammaFunctionIncompletePrevious=+Gamma_Function_Incomplete_Unnormalized (3.0d0+self%kappaPrevious,1.0d0/self%radiusFractionalDecay)
- return
- end subroutine recomputeKappa
-
- double precision function truncatedExponentialDensity(self,node,radius)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: multiplier
-
- call self%truncationFunction(node,radius,multiplier=multiplier)
- truncatedExponentialDensity=+self%darkMatterProfileDMO_%density(node,radius) &
- & *multiplier
- return
- end function truncatedExponentialDensity
-
- double precision function truncatedExponentialDensityLogSlope(self,node,radius)
- !!{
- Returns the logarithmic slope of the density in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: multiplier, multiplierGradient
-
- call self%truncationFunction(node,radius,multiplier=multiplier,multiplierGradient=multiplierGradient)
- if (multiplier > 0.0d0) then
- truncatedExponentialDensityLogSlope=+self%darkMatterProfileDMO_%densityLogSlope(node,radius) &
- & +radius &
- & *multiplierGradient &
- & /multiplier
- else
- truncatedExponentialDensityLogSlope=+0.0d0
- end if
- return
- end function truncatedExponentialDensityLogSlope
-
- double precision function truncatedExponentialRadiusEnclosingDensity(self,node,density)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily density} (given in units of $M_\odot/$Mpc$^{-3}$).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: density
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRadiusEnclosingDensity=self%darkMatterProfileDMO_%radiusEnclosingDensity (node,density)
- else
- truncatedExponentialRadiusEnclosingDensity=self %radiusEnclosingDensityNumerical(node,density)
- end if
- return
- end function truncatedExponentialRadiusEnclosingDensity
-
- double precision function truncatedExponentialRadiusEnclosingMass(self,node,mass)
- !!{
- Returns the radius (in Mpc) in the dark matter profile of {\normalfont \ttfamily node} which encloses the given
- {\normalfont \ttfamily mass} (given in units of $M_\odot$).
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, treeNode
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: mass
- class (nodeComponentBasic ), pointer :: basic
- double precision :: massVirial
-
- basic => node %basic()
- massVirial = basic%mass ()
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. mass <= massVirial) then
- truncatedExponentialRadiusEnclosingMass=self%darkMatterProfileDMO_%radiusEnclosingMass (node,mass)
- else
- truncatedExponentialRadiusEnclosingMass=self %radiusEnclosingMassNumerical(node,mass)
- end if
- return
- end function truncatedExponentialRadiusEnclosingMass
-
- double precision function truncatedExponentialRadialMoment(self,node,moment,radiusMinimum,radiusMaximum)
- !!{
- Returns the density (in $M_\odot$ Mpc$^{-3}$) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRadialMoment=self%darkMatterProfileDMO_%radialMoment (node,moment,radiusMinimum,radiusMaximum)
- else
- truncatedExponentialRadialMoment=self %radialMomentNumerical(node,moment,radiusMinimum,radiusMaximum)
- end if
- return
- end function truncatedExponentialRadialMoment
-
- double precision function truncatedExponentialEnclosedMass(self,node,radius)
- !!{
- Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
- units of Mpc).
- !!}
- use :: Gamma_Functions , only : Gamma_Function_Incomplete_Unnormalized
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusVirial, radiusDecay
-
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
- if (radius <= radiusVirial) then
- truncatedExponentialEnclosedMass=+self%darkMatterProfileDMO_%enclosedMass(node,radius )
- else
- if (self%kappaPrevious == -huge(0.0d0)) call recomputeKappa(self,node)
- radiusDecay =+self%radiusFractionalDecay*radiusVirial
- truncatedExponentialEnclosedMass=+self%massVirialPrevious &
- & +self%densityNormalizationPrevious &
- & *( &
- & +self%gammaFunctionIncompletePrevious &
- & -Gamma_Function_Incomplete_Unnormalized(3.0d0+self%kappaPrevious,radius/radiusDecay) &
- & )
- end if
- return
- end function truncatedExponentialEnclosedMass
-
- double precision function truncatedExponentialPotential(self,node,radius,status)
- !!{
- Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
- \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType ), intent( out), optional :: status
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialPotential=self%darkMatterProfileDMO_%potential (node,radius,status)
- else
- truncatedExponentialPotential=self %potentialNumerical(node,radius,status)
- end if
- return
- end function truncatedExponentialPotential
-
- double precision function truncatedExponentialCircularVelocity(self,node,radius)
- !!{
- Returns the circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialCircularVelocity=self%darkMatterProfileDMO_%circularVelocity (node,radius)
- else
- truncatedExponentialCircularVelocity=self %circularVelocityNumerical(node,radius)
- end if
- return
- end function truncatedExponentialCircularVelocity
-
- double precision function truncatedExponentialRadiusCircularVelocityMaximum(self,node)
- !!{
- Returns the radius (in Mpc) at which the maximum circular velocity is achieved in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRadiusCircularVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum (node)
- else
- truncatedExponentialRadiusCircularVelocityMaximum=self %radiusCircularVelocityMaximumNumerical(node)
- end if
- return
- end function truncatedExponentialRadiusCircularVelocityMaximum
-
- double precision function truncatedExponentialCircularVelocityMaximum(self,node)
- !!{
- Returns the maximum circular velocity (in km/s) in the dark matter profile of {\normalfont \ttfamily node}.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialCircularVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum (node)
- else
- truncatedExponentialCircularVelocityMaximum=self %circularVelocityMaximumNumerical(node)
- end if
- return
- end function truncatedExponentialCircularVelocityMaximum
-
- double precision function truncatedExponentialRadialVelocityDispersion(self,node,radius)
- !!{
- Returns the radial velocity dispersion (in km/s) in the dark matter profile of {\normalfont \ttfamily node} at the given
- {\normalfont \ttfamily radius} (given in units of Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusVirial
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRadialVelocityDispersion=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radius)
- else
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- radiusVirial=self%darkMatterHaloScale_%radiusVirial(node)
- if (radius >= radiusVirial) then
- truncatedExponentialRadialVelocityDispersion=self%radialVelocityDispersionNumerical(node,radius)
- else
- if (self%radialVelocityDispersionVirialRadiusPrevious < 0.0d0 .or. self%radialVelocityDispersionVirialRadiusUntruncatedPrevious < 0.0d0) then
- self%radialVelocityDispersionVirialRadiusPrevious =self%radialVelocityDispersionNumerical (node,radiusVirial)
- self%radialVelocityDispersionVirialRadiusUntruncatedPrevious=self%darkMatterProfileDMO_%radialVelocityDispersion(node,radiusVirial)
- end if
- truncatedExponentialRadialVelocityDispersion=sqrt( &
- & +self%darkMatterProfileDMO_%radialVelocityDispersion (node,radius )**2 &
- & +self%density (node,radiusVirial) &
- & /self%density (node,radius ) &
- & *( &
- & +self%radialVelocityDispersionVirialRadiusPrevious **2 &
- & -self%radialVelocityDispersionVirialRadiusUntruncatedPrevious **2 &
- & ) &
- & )
- end if
- end if
- return
- end function truncatedExponentialRadialVelocityDispersion
-
- double precision function truncatedExponentialRadiusFromSpecificAngularMomentum(self,node,specificAngularMomentum)
- !!{
- Returns the radius (in Mpc) in {\normalfont \ttfamily node} at which a circular orbit has the given {\normalfont \ttfamily specificAngularMomentum} (given
- in units of km s$^{-1}$ Mpc).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRadiusFromSpecificAngularMomentum=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum (node,specificAngularMomentum)
- else
- truncatedExponentialRadiusFromSpecificAngularMomentum=self %radiusFromSpecificAngularMomentumNumerical(node,specificAngularMomentum)
- end if
- return
- end function truncatedExponentialRadiusFromSpecificAngularMomentum
-
- double precision function truncatedExponentialRotationNormalization(self,node)
- !!{
- Return the normalization of the rotation velocity vs. specific angular momentum relation.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialRotationNormalization=self%darkMatterProfileDMO_%rotationNormalization (node)
- else
- truncatedExponentialRotationNormalization=self %rotationNormalizationNumerical(node)
- end if
- return
- end function truncatedExponentialRotationNormalization
-
- double precision function truncatedExponentialEnergy(self,node)
- !!{
- Return the energy of a truncatedExponential halo density profile.
- !!}
- implicit none
- class(darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialEnergy=self%darkMatterProfileDMO_%energy (node)
- else
- truncatedExponentialEnergy=self %energyNumerical(node)
- end if
- return
- end function truncatedExponentialEnergy
-
- double precision function truncatedExponentialKSpace(self,node,waveNumber)
- !!{
- Returns the Fourier transform of the truncatedExponential density profile at the specified {\normalfont \ttfamily waveNumber}
- (given in Mpc$^{-1}$).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: waveNumber
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialKSpace=self%darkMatterProfileDMO_%kSpace (node,waveNumber)
- else
- truncatedExponentialKSpace=self %kSpaceNumerical(node,waveNumber)
- end if
- return
- end function truncatedExponentialKSpace
-
- double precision function truncatedExponentialFreefallRadius(self,node,time)
- !!{
- Returns the freefall radius in the truncatedExponential density profile at the specified {\normalfont \ttfamily time} (given in
- Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
-
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialFreefallRadius=self%darkMatterProfileDMO_%freefallRadius (node,time)
- else
- truncatedExponentialFreefallRadius=self %freefallRadiusNumerical(node,time)
- end if
- return
- end function truncatedExponentialFreefallRadius
-
- double precision function truncatedExponentialFreefallRadiusIncreaseRate(self,node,time)
- !!{
- Returns the rate of increase of the freefall radius in the truncatedExponential density profile at the specified {\normalfont
- \ttfamily time} (given in Gyr).
- !!}
- implicit none
- class (darkMatterProfileDMOTruncatedExponential), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: time
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ double precision :: radiusVirial
+ class (massDistributionClass ), pointer :: massDistributionDecorated
+ !![
+
+ !!]
- if (self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
- truncatedExponentialFreefallRadiusIncreaseRate=self%darkMatterProfileDMO_%freefallRadiusIncreaseRate (node,time)
- else
- truncatedExponentialFreefallRadiusIncreaseRate=self %freefallRadiusIncreaseRateNumerical(node,time)
- end if
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Create the mass distribution.
+ allocate(massDistributionSphericalTruncatedExponential :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionSphericalTruncatedExponential)
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial(node )
+ massDistributionDecorated => self%darkMatterProfileDMO_%get (node,weightBy,weightIndex)
+ select type (massDistributionDecorated)
+ class is (massDistributionSpherical)
+ !![
+
+
+ massDistributionSphericalTruncatedExponential( &
+ & radiusTruncateMinimum= radiusVirial, &
+ & radiusTruncateDecay =self%radiusFractionalDecay*radiusVirial, &
+ & nonAnalyticSolver =self%nonAnalyticSolver , &
+ & massDistribution_ = massDistributionDecorated , &
+ & componentType = componentTypeDarkHalo , &
+ & massType = massTypeDark &
+ & )
+
+
+ !!]
+ class default
+ call Error_Report('expected a spherical mass distribution'//{introspection:location})
+ end select
+ !![
+
+ !!]
+ end select
+ allocate(kinematicsDistribution_)
+ !![
+
+
+ kinematicsDistributionTruncated( &
+ & )
+
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
return
- end function truncatedExponentialFreefallRadiusIncreaseRate
+ end function truncatedExponentialGet
diff --git a/source/display.F90 b/source/display.F90
index 4d15b7905a..2c548e7ccc 100644
--- a/source/display.F90
+++ b/source/display.F90
@@ -57,10 +57,10 @@ module Display
character(len=10 ), allocatable, dimension(:) :: indentationFormat
character(len=10 ), allocatable, dimension(:) :: indentationFormatNoNewLine
- character(len=20 ) :: threadFormat , masterFormat
+ character(len=20 ) :: threadFormat , masterFormat
- logical :: displayInitialized =.false. , verbositySet=.false.
- type (enumerationVerbosityLevelType) :: verbosityLevel =verbosityLevelSilent
+ logical :: displayInitialized =.false. , verbositySet=.false.
+ type (enumerationVerbosityLevelType) :: verbosityLevel =verbosityLevelStandard
! Progress bar state.
logical :: barVisible =.false.
diff --git a/source/galactic.filters.halo_mass.F90 b/source/galactic.filters.halo_mass.F90
index 88ad410a31..5fc4629d56 100644
--- a/source/galactic.filters.halo_mass.F90
+++ b/source/galactic.filters.halo_mass.F90
@@ -21,10 +21,9 @@
Contains a module which implements a galactic high-pass filter for halo mass under a given definition.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -40,7 +39,6 @@
!!}
private
double precision :: massThreshold
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -69,7 +67,6 @@ function haloMassConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
double precision :: massThreshold
@@ -82,23 +79,21 @@ function haloMassConstructorParameters(parameters) result(self)
-
!!]
- self=galacticFilterHaloMass(massThreshold,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=galacticFilterHaloMass(massThreshold,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_)
!![
-
!!]
return
end function haloMassConstructorParameters
- function haloMassConstructorInternal(massThreshold,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
+ function haloMassConstructorInternal(massThreshold,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
!!{
Internal constructor for the ``haloMass'' galactic filter class.
!!}
@@ -108,9 +103,8 @@ function haloMassConstructorInternal(massThreshold,cosmologyFunctions_,cosmology
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass), intent(in ), target :: virialDensityContrast_, virialDensityContrastDefinition_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
!![
-
+
!!]
return
@@ -127,7 +121,6 @@ subroutine haloMassDestructor(self)
-
!!]
return
@@ -153,7 +146,6 @@ logical function haloMassPasses(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& ) &
& >= &
diff --git a/source/galactic.filters.halo_mass_range.F90 b/source/galactic.filters.halo_mass_range.F90
index fd1035f465..ff4c70adb6 100644
--- a/source/galactic.filters.halo_mass_range.F90
+++ b/source/galactic.filters.halo_mass_range.F90
@@ -21,10 +21,9 @@
Contains a module which implements a galactic filter for halo mass under a given definition.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -39,7 +38,6 @@
!!}
private
double precision :: massLow , massHigh
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -68,7 +66,6 @@ function haloMassRangeConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
double precision :: massLow , massHigh
@@ -86,23 +83,21 @@ function haloMassRangeConstructorParameters(parameters) result(self)
-
!!]
- self=galacticFilterHaloMassRange(massLow,massHigh,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=galacticFilterHaloMassRange(massLow,massHigh,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_)
!![
-
!!]
return
end function haloMassRangeConstructorParameters
- function haloMassRangeConstructorInternal(massLow,massHigh,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
+ function haloMassRangeConstructorInternal(massLow,massHigh,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
!!{
Internal constructor for the ``haloMassRange'' galactic filter class.
!!}
@@ -112,9 +107,8 @@ function haloMassRangeConstructorInternal(massLow,massHigh,cosmologyFunctions_,c
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_, virialDensityContrastDefinition_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
!![
-
+
!!]
return
@@ -131,7 +125,6 @@ subroutine haloMassRangeDestructor(self)
-
!!]
return
@@ -158,7 +151,6 @@ logical function haloMassRangePasses(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
haloMassRangePasses = massHalo >= self%massLow &
diff --git a/source/galactic.filters.stellar_absolute_magnitudes.F90 b/source/galactic.filters.stellar_absolute_magnitudes.F90
index 4efe9780f0..20ba1cebd9 100644
--- a/source/galactic.filters.stellar_absolute_magnitudes.F90
+++ b/source/galactic.filters.stellar_absolute_magnitudes.F90
@@ -20,8 +20,6 @@
!!{
Contains a module which implements a galactic low-pass (i.e. bright-pass) filter for stellar absolute magnitudes.
!!}
-
- use :: Galactic_Structure, only : galacticStructureClass
!![
@@ -36,10 +34,8 @@
A galactic low-pass (i.e. bright pass) filter class for stellar absolute magnitudes.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
- double precision , allocatable, dimension(:) :: absoluteMagnitudeThreshold
+ double precision, allocatable, dimension(:) :: absoluteMagnitudeThreshold
contains
- final :: stellarAbsoluteMagnitudesDestructor
procedure :: passes => stellarAbsoluteMagnitudesPasses
end type galacticFilterStellarAbsoluteMagnitudes
@@ -64,7 +60,6 @@ function stellarAbsoluteMagnitudesConstructorParameters(parameters) result(self)
type (galacticFilterStellarAbsoluteMagnitudes) :: self
type (inputParameters ), intent(inout) :: parameters
double precision , allocatable , dimension(:) :: absoluteMagnitudeThreshold
- class (galacticStructureClass ), pointer :: galacticStructure_
! Check and read parameters.
if (parameters%count('absoluteMagnitudeThreshold') /= unitStellarLuminosities%luminosityCount(unmapped=.true.)) &
@@ -79,17 +74,15 @@ function stellarAbsoluteMagnitudesConstructorParameters(parameters) result(self)
The parameter $M_0$ appearing in the stellar absolute magnitude threshold for the stellar absolute magnitude galactic filter class.
-
!!]
- self=galacticFilterStellarAbsoluteMagnitudes(absoluteMagnitudeThreshold,galacticStructure_)
+ self=galacticFilterStellarAbsoluteMagnitudes(absoluteMagnitudeThreshold)
!![
-
!!]
return
end function stellarAbsoluteMagnitudesConstructorParameters
- function stellarAbsoluteMagnitudesConstructorInternal(absoluteMagnitudeThreshold,galacticStructure_) result(self)
+ function stellarAbsoluteMagnitudesConstructorInternal(absoluteMagnitudeThreshold) result(self)
!!{
Internal constructor for the ``stellarAbsoluteMagnitudes'' galactic filter class.
!!}
@@ -97,10 +90,9 @@ function stellarAbsoluteMagnitudesConstructorInternal(absoluteMagnitudeThreshold
use :: Stellar_Luminosities_Structure, only : Stellar_Luminosities_Parameter_Map, unitStellarLuminosities
implicit none
type (galacticFilterStellarAbsoluteMagnitudes) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ), dimension(:) :: absoluteMagnitudeThreshold
!![
-
+
!!]
if (size(absoluteMagnitudeThreshold) /= unitStellarLuminosities%luminosityCount(unmapped=.true.)) &
@@ -113,31 +105,20 @@ function stellarAbsoluteMagnitudesConstructorInternal(absoluteMagnitudeThreshold
return
end function stellarAbsoluteMagnitudesConstructorInternal
- subroutine stellarAbsoluteMagnitudesDestructor(self)
- !!{
- Destructor for the ``stellarAbsoluteMagnitudes'' galactic filter class.
- !!}
- implicit none
- type(galacticFilterStellarAbsoluteMagnitudes), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine stellarAbsoluteMagnitudesDestructor
-
logical function stellarAbsoluteMagnitudesPasses(self,node)
!!{
Implement a stellar absolute magnitude low-pass galactic filter.
!!}
use :: Galactic_Structure_Options , only : massTypeStellar , weightByLuminosity
- use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass
use :: Stellar_Luminosities_Structure, only : unitStellarLuminosities
implicit none
class (galacticFilterStellarAbsoluteMagnitudes), intent(inout) :: self
type (treeNode ), intent(inout), target :: node
class (nodeComponentBasic ), pointer :: basic
- double precision :: time , luminosity, &
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision :: time , luminosity, &
& abMagnitude
integer :: iLuminosity
@@ -150,7 +131,11 @@ logical function stellarAbsoluteMagnitudesPasses(self,node)
! Only check those luminosities which are being output at this output time.
if (unitStellarLuminosities%isOutput(iLuminosity,time)) then
! Get the total stellar luminosity of the galaxy.
- luminosity=self%galacticStructure_%massEnclosed(node,massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=iLuminosity)
+ massDistribution_ => node %massDistribution(massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=iLuminosity)
+ luminosity = massDistribution_%massTotal ( )
+ !![
+
+ !!]
! Test only if the luminosity is greater than zero.
if (luminosity > 0.0d0) then
! Convert to absolute magnitude.
diff --git a/source/galactic.filters.stellar_apparent_magnitudes.F90 b/source/galactic.filters.stellar_apparent_magnitudes.F90
index 6806d28bd0..413539d8e6 100644
--- a/source/galactic.filters.stellar_apparent_magnitudes.F90
+++ b/source/galactic.filters.stellar_apparent_magnitudes.F90
@@ -22,7 +22,6 @@
!!}
use :: Cosmology_Functions, only : cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -38,7 +37,6 @@
!!}
private
class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable, dimension(:) :: apparentMagnitudeThreshold
contains
final :: stellarApparentMagnitudesDestructor
@@ -67,7 +65,6 @@ function stellarApparentMagnitudesConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
double precision , allocatable , dimension(:) :: apparentMagnitudeThreshold
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (galacticStructureClass ), pointer :: galacticStructure_
! Check and read parameters.
if (parameters%count('apparentMagnitudeThreshold') /= unitStellarLuminosities%luminosityCount(unmapped=.true.)) &
@@ -83,18 +80,16 @@ function stellarApparentMagnitudesConstructorParameters(parameters) result(self)
The parameter $m_0$ appearing in the stellar apparent magnitude threshold for the stellar apparent magnitude galactic filter class.
-
!!]
- self=galacticFilterStellarApparentMagnitudes(apparentMagnitudeThreshold,cosmologyFunctions_,galacticStructure_)
+ self=galacticFilterStellarApparentMagnitudes(apparentMagnitudeThreshold,cosmologyFunctions_)
!![
-
!!]
return
end function stellarApparentMagnitudesConstructorParameters
- function stellarApparentMagnitudesConstructorInternal(apparentMagnitudeThreshold,cosmologyFunctions_,galacticStructure_) result(self)
+ function stellarApparentMagnitudesConstructorInternal(apparentMagnitudeThreshold,cosmologyFunctions_) result(self)
!!{
Internal constructor for the ``stellarApparentMagnitudes'' galactic filter class.
!!}
@@ -104,9 +99,8 @@ function stellarApparentMagnitudesConstructorInternal(apparentMagnitudeThreshold
type (galacticFilterStellarApparentMagnitudes) :: self
double precision , intent(in ), dimension(:) :: apparentMagnitudeThreshold
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
if (size(apparentMagnitudeThreshold) /= unitStellarLuminosities%luminosityCount(unmapped=.true.)) &
@@ -128,7 +122,6 @@ subroutine stellarApparentMagnitudesDestructor(self)
!![
-
!!]
return
end subroutine stellarApparentMagnitudesDestructor
@@ -138,12 +131,14 @@ logical function stellarApparentMagnitudesPasses(self,node)
Implement a stellar apparent magnitude low-pass galactic filter.
!!}
use :: Galactic_Structure_Options , only : massTypeStellar , weightByLuminosity
- use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass
use :: Stellar_Luminosities_Structure, only : unitStellarLuminosities
implicit none
class (galacticFilterStellarApparentMagnitudes), intent(inout) :: self
type (treeNode ), intent(inout), target :: node
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: expansionFactorTolerance=1.0d-6
double precision :: time , luminosity , &
& abMagnitude , expansionFactor, &
@@ -167,7 +162,11 @@ logical function stellarApparentMagnitudesPasses(self,node)
! Only check those luminosities which are being output at this output time.
if (unitStellarLuminosities%isOutput(iLuminosity,time)) then
! Get the total stellar luminosity of the galaxy.
- luminosity=self%galacticStructure_%massEnclosed(node,massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=iLuminosity)
+ massDistribution_ => node %massDistribution(massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=iLuminosity)
+ luminosity = massDistribution_%massTotal ( )
+ !![
+
+ !!]
! Test only if the luminosity is greater than zero.
if (luminosity > 0.0d0) then
! Convert to apparent magnitude.
diff --git a/source/galactic.structure.F90 b/source/galactic.structure.F90
deleted file mode 100644
index d209648c6b..0000000000
--- a/source/galactic.structure.F90
+++ /dev/null
@@ -1,177 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 provides a class that implements galactic structure functions.
-!!}
-
-module Galactic_Structure
- !!{
- Provides an object that implements galactic structure functions.
- !!}
- use :: Galacticus_Nodes , only : treeNode
- use :: Galactic_Structure_Options, only : enumerationCoordinateSystemType , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType, &
- & enumerationStructureErrorCodeType
- private
-
- !![
-
- galacticStructure
- Galactic Structure
- Object providing galactic structure functions.
- standard
-
- Return the density at the given {\normalfont \ttfamily position} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: position
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Return the spherically-averaged density at the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Return the mass enclosed within the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), optional :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Return the radius enclosing a given mass (or fractional mass) in {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ), optional :: mass , massFractional
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Return the rotation velocity for a circular orbit at the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- Return the gradient of the rotation velocity for a circular orbit at the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- Return the gravitational potential at the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
-
-
- Return the surface density of given {\normalfont \ttfamily massType}) at the specified {\normalfont \ttfamily position}.
- double precision
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: position
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Return the radius enclosing a given surface density in {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: surfaceDensity
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
-
- Compute the gravitational acceleration at a given position in {\normalfont \ttfamily node}.
- double precision, dimension(3)
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- Compute the gravitational tidal tensor at a given position in {\normalfont \ttfamily node}.
- type(tensorRank2Dimension3Symmetric)
- yes
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- Compute the integral appearing in the \cite{chandrasekhar_dynamical_1943} dynamical friction model in {\normalfont \ttfamily node}.
- double precision, dimension(3)
- yes
- type (treeNode ), intent(inout), target :: node , nodeSatellite
- double precision , intent(in ), dimension(3) :: positionCartesian, velocityCartesian
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- Return the velocity dispersion at the given {\normalfont \ttfamily radius} in {\normalfont \ttfamily node}.
- double precision
- yes
- yes
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius , radiusOuter
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
-
- !!]
-
-end module Galactic_Structure
diff --git a/source/galactic.structure.standard.F90 b/source/galactic.structure.standard.F90
deleted file mode 100644
index d1b799bf1a..0000000000
--- a/source/galactic.structure.standard.F90
+++ /dev/null
@@ -1,1098 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 the standard galactic structure functions.
-!!}
-
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Dark_Matter_Profiles , only : darkMatterProfileClass
- use :: Root_Finder , only : rootFinder
- use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType, enumerationStructureErrorCodeType
- !![
-
-
- The standard galactic structure functions.
-
-
- !!]
- type, extends(galacticStructureClass) :: galacticStructureStandard
- !!{
- The standard galactic structure functions.
- !!}
- private
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileClass ), pointer :: darkMatterProfile_ => null()
- type (rootFinder ) :: finderMass , finderSurfaceDensity
- double precision :: radiusEnclosingMassPrevious , potentialOffset , &
- & radiusEnclosingSurfaceDensityPrevious
- integer (kind_int8 ) :: uniqueIDPrevious
- logical :: potentialOffsetComputed
- contains
- !![
-
-
-
-
-
- !!]
- final :: standardDestructor
- procedure :: autoHook => standardAutoHook
- procedure :: density => standardDensity
- procedure :: densitySphericalAverage => standardDensitySphericalAverage
- procedure :: massEnclosed => standardMassEnclosed
- procedure :: radiusEnclosingMass => standardRadiusEnclosingMass
- procedure :: velocityRotation => standardVelocityRotation
- procedure :: velocityRotationGradient => standardVelocityRotationGradient
- procedure :: potential => standardPotential
- procedure :: surfaceDensity => standardSurfaceDensity
- procedure :: radiusEnclosingSurfaceDensity => standardRadiusEnclosingSurfaceDensity
- procedure :: acceleration => standardAcceleration
- procedure :: tidalTensor => standardTidalTensor
- procedure :: chandrasekharIntegral => standardChandrasekharIntegral
- procedure :: velocityDispersion => standardVelocityDispersion
- procedure :: defaults => standardDefaults
- procedure :: restore => standardRestore
- procedure :: calculationReset => standardCalculationReset
- end type galacticStructureStandard
-
- interface galacticStructureStandard
- !!{
- Constructors for the ``standard'' galactic structure class.
- !!}
- module procedure standardConstructorParameters
- module procedure standardConstructorInternal
- end interface galacticStructureStandard
-
- ! Types used to store state to allow recursive calling of these functions.
- type :: galacticStructureState
- type (enumerationComponentTypeType) :: componentType_
- type (enumerationMassTypeType ) :: massType_
- type (enumerationWeightByType ) :: weightBy_
- integer :: weightIndex_
- double precision :: radius_
- end type galacticStructureState
-
- type :: galacticStructureStateList
- type(galacticStructureState ) :: state
- type(galacticStructureStateList), pointer :: next => null(), previous => null()
- end type galacticStructureStateList
-
- ! State stack used to allow recursive calling of these functions.
- type (galacticStructureStateList ), pointer :: galacticStructureState_, galacticStructureStateHead_
- !$omp threadprivate(galacticStructureState_,galacticStructureStateHead_)
-
- ! Submodule-scope variables used in callback functions and root-finding.
- type (enumerationStructureErrorCodeType), :: status_
- double precision , dimension(3) :: positionSpherical_ , positionCartesian_ , &
- & positionCylindrical_ , velocityCartesian_
- !$omp threadprivate(status_,positionSpherical_,positionCartesian_,positionCylindrical_,velocityCartesian_)
-
- ! Submodule-scope variables used in root finding.
- double precision :: massTarget , surfaceDensityTarget
- type (treeNode ), pointer :: node_ , nodeSatellite_
- class (galacticStructureStandard ), pointer :: self_
- !$omp threadprivate(self_,node_,nodeSatellite_,massTarget,surfaceDensityTarget)
-
-contains
-
- function standardConstructorParameters(parameters) result(self)
- !!{
- Constructor for the ``standard'' galactic structure class which takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type (galacticStructureStandard) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(darkMatterProfileClass ), pointer :: darkMatterProfile_
-
- !![
-
-
-
- !!]
- self=galacticStructureStandard(cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfile_)
- !![
-
-
-
-
- !!]
- return
- end function standardConstructorParameters
-
- function standardConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfile_) result(self)
- !!{
- Internal constructor for the ``standard'' galactic structure class.
- !!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- implicit none
- type(galacticStructureStandard) :: self
- class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class(darkMatterHaloScaleClass), intent(in ), target :: darkMatterHaloScale_
- class(darkMatterProfileClass ), intent(in ), target :: darkMatterProfile_
- !![
-
- !!]
-
- self%potentialOffsetComputed =.false.
- self%radiusEnclosingMassPrevious =-huge(0.0d0)
- self%radiusEnclosingSurfaceDensityPrevious=-huge(0.0d0)
- self%uniqueIDPrevious =-1_kind_int8
- self%finderMass =rootFinder( &
- & rootFunction =massEnclosedRoot , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
- & rangeExpandType =rangeExpandMultiplicative , &
- & toleranceAbsolute =0.0d+0 , &
- & toleranceRelative =1.0d-6 &
- & )
- self%finderSurfaceDensity =rootFinder( &
- & rootFunction =surfaceDensityRoot , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandType =rangeExpandMultiplicative , &
- & toleranceAbsolute =0.0d+0 , &
- & toleranceRelative =1.0d-6 &
- & )
- return
- end function standardConstructorInternal
-
- subroutine standardAutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(galacticStructureStandard), intent(inout) :: self
-
- call calculationResetEvent%attach(self,standardCalculationReset,openMPThreadBindingAllLevels,label='galacticStructureStandard')
- return
- end subroutine standardAutoHook
-
- subroutine standardDestructor(self)
- !!{
- Destructor for the ``standard'' galactic structure class.
- !!}
- use :: Events_Hooks, only : calculationResetEvent
- implicit none
- type(galacticStructureStandard), intent(inout) :: self
-
- !![
-
-
-
- !!]
- if (calculationResetEvent%isAttached(self,standardCalculationReset)) call calculationResetEvent%detach(self,standardCalculationReset)
- return
- end subroutine standardDestructor
-
- subroutine standardCalculationReset(self,node,uniqueID)
- !!{
- Reset calculations for galactic structure potentials.
- !!}
- use :: Galacticus_Nodes, only : treeNode
- use :: Kind_Numbers , only : kind_int8
- implicit none
- class (galacticStructureStandard), intent(inout) :: self
- type (treeNode ), intent(in ) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%potentialOffsetComputed =.false.
- self%radiusEnclosingMassPrevious =-huge(0.0d0)
- self%radiusEnclosingSurfaceDensityPrevious=-huge(0.0d0)
- self%uniqueIDPrevious =uniqueID
- return
- end subroutine standardCalculationReset
-
- double precision function standardDensity(self,node,position,coordinateSystem,componentType,massType,weightBy,weightIndex) result(density)
- !!{
- Compute the density (of given {\normalfont \ttfamily massType}) at the specified {\normalfont \ttfamily position}. Assumes that galactic structure has already
- been computed.
- !!}
- use :: Coordinate_Systems , only : Coordinates_Cartesian_To_Spherical, Coordinates_Cylindrical_To_Spherical
- use :: Galactic_Structure_Options, only : componentTypeAll , coordinateSystemCartesian , coordinateSystemCylindrical, coordinateSystemSpherical , &
- & massTypeAll , weightByLuminosity , weightByMass , enumerationCoordinateSystemType
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : optimizeForDensitySummation , reductionSummation , treeNode
- !![
-
- !!]
- include 'galactic_structure.density.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- double precision , intent(in ), dimension(3) :: position
- procedure (densityComponent ), pointer :: densityComponent_
- type (enumerationCoordinateSystemType) :: coordinateSystemActual
- double precision :: densityComponent__
-
- ! Determine position in spherical coordinate system to use.
- if (present(coordinateSystem)) then
- coordinateSystemActual=coordinateSystem
- else
- coordinateSystemActual=coordinateSystemSpherical
- end if
- select case (coordinateSystemActual%ID)
- case (coordinateSystemSpherical %ID)
- positionSpherical_=position
- case (coordinateSystemCylindrical%ID)
- positionSpherical_=Coordinates_Cylindrical_To_Spherical(position)
- case (coordinateSystemCartesian %ID)
- positionSpherical_=Coordinates_Cartesian_To_Spherical (position)
- case default
- call Error_Report('unknown coordinate system type'//{introspection:location})
- end select
- call self%defaults(componentType=componentType,massType=massType,weightBy=weightBy,weightIndex=weightIndex)
- ! Call routines to supply the densities for all components.
- densityComponent_ => densityComponent
- density = node%mapDouble0(densityComponent_,reductionSummation,optimizeFor=optimizeForDensitySummation)
- !![
-
- node,positionSpherical_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_
- density=density+densityComponent__
- !!]
- include 'galactic_structure.density.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardDensity
-
- double precision function densityComponent(component)
- !!{
- Unary function returning the density in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- densityComponent=component%density(positionSpherical_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_)
- return
- end function densityComponent
-
- double precision function standardDensitySphericalAverage(self,node,radius,componentType,massType,weightBy,weightIndex) result(density)
- !!{
- Compute the density (of given {\normalfont \ttfamily massType}) at the specified {\normalfont \ttfamily position}. Assumes that galactic structure has already
- been computed.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll , weightByLuminosity, weightByMass
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : optimizeForDensitySphericalAverageSummation, reductionSummation, treeNode
- !![
-
- !!]
- include 'galactic_structure.density_spherical_average.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- procedure (densitySphericalAverageComponent), pointer :: densitySphericalAverageComponent_
- double precision :: densitySphericalAverageComponent__
-
- call self%defaults(radius=radius,componentType=componentType,massType=massType,weightBy=weightBy,weightIndex=weightIndex)
- ! Call routines to supply the densities for all components.
- densitySphericalAverageComponent_ => densitySphericalAverageComponent
- density = node%mapDouble0(densitySphericalAverageComponent_,reductionSummation,optimizeFor=optimizeForDensitySphericalAverageSummation)
- !![
-
- node,radius,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_
- density=density+densitySphericalAverageComponent__
- !!]
- include 'galactic_structure.density_spherical_average.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardDensitySphericalAverage
-
- double precision function densitySphericalAverageComponent(component)
- !!{
- Unary function returning the spherically-averaged density in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- densitySphericalAverageComponent=component%densitySphericalAverage(galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_)
- return
- end function densitySphericalAverageComponent
-
- double precision function standardMassEnclosed(self,node,radius,componentType,massType,weightBy,weightIndex) result(massEnclosed)
- !!{
- Compute the mass within a given radius, or the total mass if no radius is specified.
- !!}
- use :: Galacticus_Nodes, only : optimizeForEnclosedMassSummation, reductionSummation
- !![
-
- !!]
- include 'galactic_structure.enclosed_mass.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- double precision , intent(in ), optional :: radius
- procedure (massComponentEnclosed ), pointer :: massComponentEnclosed_
- double precision :: massComponent
-
- call self%defaults(radius,componentType,massType,weightBy,weightIndex)
- ! Compute the contribution from components directly, by mapping a function over all components.
- massComponentEnclosed_ => massComponentEnclosed
- massEnclosed = node %mapDouble0(massComponentEnclosed_,reductionSummation,optimizeFor=optimizeForEnclosedMassSummation)
- ! Call routines to supply the masses for all components.
- !![
-
- node,galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_
- massEnclosed=massEnclosed+massComponent
- !!]
- include 'galactic_structure.enclosed_mass.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardMassEnclosed
-
- double precision function massComponentEnclosed(component)
- !!{
- Unary function returning the enclosed mass in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- massComponentEnclosed=component%enclosedMass(galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_)
- return
- end function massComponentEnclosed
-
- double precision function standardRadiusEnclosingMass(self,node,mass,massFractional,componentType,massType,weightBy,weightIndex)
- !!{
- Return the radius enclosing a given mass (or fractional mass) in {\normalfont \ttfamily node}.
- !!}
- use :: Dark_Matter_Profile_Structure_Tasks, only : Dark_Matter_Profile_Radius_Enclosing_Mass
- use :: Display , only : displayMessage , verbosityLevelWarn
- use :: Galactic_Structure_Options , only : componentTypeDarkHalo , massTypeDark
- use :: Error , only : Error_Report
- use :: ISO_Varying_String , only : assignment(=) , operator(//) , varying_string
- use :: String_Handling , only : operator(//)
- implicit none
- class (galacticStructureStandard ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- double precision , intent(in ), optional :: massFractional, mass
- double precision :: radiusGuess
- type (varying_string ) :: message
- character (len=11 ) :: massLabel
-
- call self%defaults(componentType=componentType,massType=massType,weightBy=weightBy,weightIndex=weightIndex)
- ! Determine what mass to use.
- if (present(mass)) then
- if (present(massFractional)) call Error_Report('only one mass or massFractional can be specified'//{introspection:location})
- massTarget=mass
- else if (present(massFractional)) then
- massTarget=massFractional*self%massEnclosed(node,componentType=galacticStructureState_%state%componentType_,massType=galacticStructureState_%state%massType_,weightBy=galacticStructureState_%state%weightBy_,weightIndex=galacticStructureState_%state%weightIndex_)
- else
- call Error_Report('either mass or massFractional must be specified'//{introspection:location})
- end if
- if (massTarget <= 0.0d0) then
- standardRadiusEnclosingMass=0.0d0
- call self%restore()
- return
- end if
- self_ => self
- node_ => node
- ! If the dark matter component is queried and its density profile is unaffected by baryons, compute the radius from the dark
- ! matter profile. Otherwise, find the radius numerically.
- if ( &
- & galacticStructureState_%state%componentType_ == componentTypeDarkHalo &
- & .or. &
- & galacticStructureState_%state%massType_ == massTypeDark &
- & ) then
- if (.not.associated(self%darkMatterProfile_)) call Error_Report('object is not expecting dark matter requests'//{introspection:location})
- ! Use the function provided by the dark matter profile structure tasks module here. This ensures precise consistency
- ! between calculations here and in the enclosed mass function.
- standardRadiusEnclosingMass=Dark_Matter_Profile_Radius_Enclosing_Mass(node_,massTarget)
- else
- ! Solve for the radius.
- if (massEnclosedRoot(0.0d0) >= 0.0d0) then
- message='Enclosed mass in galaxy (ID='
- write (massLabel,'(e10.4)') self%massEnclosed(node_,0.0d0,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_)
- message=message//node%index()//') seems to be finite ('//trim(massLabel)
- write (massLabel,'(e10.4)') massTarget
- message=message//') at zero radius (was seeking ' //trim(massLabel)
- message=message//') - returning zero radius.'
- call displayMessage(message,verbosityLevelWarn)
- standardRadiusEnclosingMass=0.0d0
- call self%restore()
- return
- end if
- if (self%radiusEnclosingMassPrevious >= 0.0d0) then
- radiusGuess=self %radiusEnclosingMassPrevious
- else
- radiusGuess=self%darkMatterHaloScale_%radiusVirial (node)
- end if
- self%radiusEnclosingMassPrevious=self%finderMass%find (rootGuess=radiusGuess)
- self%uniqueIDPrevious =node %uniqueID ( )
- standardRadiusEnclosingMass =self %radiusEnclosingMassPrevious
- end if
- call self%restore()
- return
- end function standardRadiusEnclosingMass
-
- double precision function massEnclosedRoot(radius)
- !!{
- Root function used in solving for the radius that encloses a given mass.
- !!}
- double precision, intent(in ) :: radius
-
- massEnclosedRoot=+self_%massEnclosed(node_,radius,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_) &
- & - massTarget
- return
- end function massEnclosedRoot
-
- double precision function standardVelocityRotation(self,node,radius,componentType,massType) result(velocityRotation)
- !!{
- Compute the rotation curve a given radius.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
- use :: Galacticus_Nodes , only : optimizeForRotationCurveSummation, reductionSummation, treeNode
- !![
-
- !!]
- include 'galactic_structure.rotation_curve.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , intent(in ) :: radius
- procedure (velocityRotationComponent ) , pointer :: velocityRotationComponent_
- double precision :: velocityRotationComponent__, rotationCurveSquared
-
- call self%defaults(radius=radius,componentType=componentType,massType=massType)
- velocityRotationComponent_ => velocityRotationComponent
- rotationCurveSquared = node%mapDouble0(velocityRotationComponent_,reductionSummation,optimizeFor=optimizeForRotationCurveSummation)
- !![
-
- node,galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_
- rotationCurveSquared=rotationCurveSquared+velocityRotationComponent__**2
- !!]
- include 'galactic_structure.rotation_curve.tasks.inc'
- !![
-
- !!]
- ! We've added velocities in quadrature, so now take the square root.
- velocityRotation=sqrt(rotationCurveSquared)
- call self%restore()
- return
- end function standardVelocityRotation
-
- double precision function velocityRotationComponent(component)
- !!{
- Unary function returning the squared rotation curve in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- velocityRotationComponent=component%rotationCurve(galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_)**2
- return
- end function velocityRotationComponent
-
- double precision function standardVelocityRotationGradient(self,node,radius,componentType,massType) result(velocityRotationGradient)
- !!{
- Solve for the rotation curve gradient at a given radius. Assumes the galactic structure has already been computed.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
- use :: Galacticus_Nodes , only : optimizeForRotationCurveGradientSummation, reductionSummation, treeNode
- !![
-
- !!]
- include 'galactic_structure.rotation_curve.gradient.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , intent(in ) :: radius
- procedure (velocityRotationGradientComponent) , pointer :: velocityRotationGradientComponent_
- double precision :: velocityRotationGradientComponent__, velocityRotation
-
- call self%defaults(radius=radius,componentType=componentType,massType=massType)
- ! Call routines to supply the gradient for all components' rotation curves. Specifically, the returned quantities are
- ! dV²/dr so that they can be summed directly.
- velocityRotationGradientComponent_ => velocityRotationGradientComponent
- velocityRotationGradient = node%mapDouble0(velocityRotationGradientComponent_,reductionSummation,optimizeFor=optimizeForRotationCurveGradientSummation)
- !![
-
- node,galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_
- velocityRotationGradient=velocityRotationGradient+velocityRotationGradientComponent__
- !!]
- include 'galactic_structure.rotation_curve.gradient.tasks.inc'
- !![
-
- !!]
- ! Convert the summed dV²/dr to dV/dr.
- velocityRotation=+self%velocityRotation(node,radius,componentType,massType)
- if (velocityRotation > 0.0d0) then
- velocityRotationGradient=+0.5d0 &
- & *velocityRotationGradient &
- & /velocityRotation
- else if (velocityRotationGradient /= 0.0d0) then
- call Error_Report('rotation curve is zero, but gradient is non-zero'//{introspection:location})
- end if
- call self%restore()
- return
- end function standardVelocityRotationGradient
-
- double precision function velocityRotationGradientComponent(component)
- !!{
- Unary function returning the gradient of the squared rotation curve in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- velocityRotationGradientComponent=component%rotationCurveGradient(galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_)
- return
- end function velocityRotationGradientComponent
-
- double precision function standardPotential(self,node,radius,componentType,massType,status) result(potential)
- !!{
- Solve for the gravitational potential at a given radius. Assumes the galactic structure has already been computed.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll , structureErrorCodeSuccess
- use :: Galacticus_Nodes , only : optimizeForPotentialSummation, reductionSummation
- !![
-
- !!]
- include 'galactic_structure.potential.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent( out), optional :: status
- procedure (potentialComponent ), pointer :: potentialComponent_
- double precision :: potentialComponent__
-
- ! Initialize status.
- if (present(status)) status=structureErrorCodeSuccess
- ! Initialize pointer to function that supplies the potential for all components.
- potentialComponent_ => potentialComponent
- ! Reset calculations if this is a new node.
- if (node%uniqueID() /= self%uniqueIDPrevious) call self%calculationReset(node,node%uniqueID())
- ! Evaluate the potential at the halo virial radius.
- if (.not.self%potentialOffsetComputed) then
- call self%defaults(componentType=componentTypeAll,massType=massTypeAll,radius=self%darkMatterHaloScale_%radiusVirial(node))
- status_ =structureErrorCodeSuccess
- potential =node%mapDouble0(potentialComponent_,reductionSummation,optimizeFor=optimizeForPotentialSummation)
- if (status_ /= structureErrorCodeSuccess) status=status_
- !![
-
- node,galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,status
- potential=potential+potentialComponent__
- !!]
- include 'galactic_structure.potential.tasks.inc'
- !![
-
- !!]
- call self%restore()
- ! Compute the potential offset such that the total gravitational potential at the virial radius is -V² where V is the
- ! virial velocity.
- self%potentialOffset =-potential-self%darkMatterHaloScale_%velocityVirial(node)**2
- self%potentialOffsetComputed=.true.
- end if
- call self%defaults(radius=radius,componentType=componentType,massType=massType)
- ! Determine which component type to use.
- if (present(componentType)) then
- galacticStructureState_%state%componentType_=componentType
- else
- galacticStructureState_%state%componentType_=componentTypeAll
- end if
- status_ = structureErrorCodeSuccess
- potential=+node%mapDouble0(potentialComponent_,reductionSummation,optimizeFor=optimizeForPotentialSummation) &
- & +self%potentialOffset
- if (status_ /= structureErrorCodeSuccess) status=status_
- include 'galactic_structure.potential.tasks.inc'
- call self%restore()
- return
- end function standardPotential
-
- double precision function potentialComponent(component)
- !!{
- Unary function returning the potential in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- potentialComponent=component%potential(galacticStructureState_%state%radius_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,status_)
- return
- end function potentialComponent
-
- double precision function standardSurfaceDensity(self,node,position,coordinateSystem,componentType,massType,weightBy,weightIndex) result(surfaceDensity)
- !!{
- Compute the surface density of given {\normalfont \ttfamily massType}) at the specified {\normalfont \ttfamily position}.
- !!}
- use :: Coordinate_Systems , only : Coordinates_Cartesian_To_Cylindrical, Coordinates_Spherical_To_Cylindrical
- use :: Galactic_Structure_Options, only : componentTypeAll , coordinateSystemCartesian , coordinateSystemCylindrical, coordinateSystemSpherical , &
- & massTypeAll , weightByMass , weightIndexNull , enumerationCoordinateSystemType
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : optimizeForSurfaceDensitySummation , optimizeforsurfacedensitysummation , reductionSummation , reductionsummation , &
- & treeNode
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- double precision , intent(in ), dimension(3) :: position
- procedure (surfaceDensityComponent ), pointer :: surfaceDensityComponent_
- !![
-
- !!]
-
- call self%defaults(componentType=componentType,massType=massType,weightBy=weightBy,weightIndex=weightIndex)
- ! Determine position in cylindrical coordinate system to use.
- select case (coordinateSystem_%ID)
- case (coordinateSystemSpherical %ID)
- positionCylindrical_=Coordinates_Spherical_To_Cylindrical (position)
- case (coordinateSystemCylindrical%ID)
- positionCylindrical_=position
- case (coordinateSystemCartesian %ID)
- positionCylindrical_=Coordinates_Cartesian_To_Cylindrical(position)
- case default
- call Error_Report('unknown coordinate system type'//{introspection:location})
- end select
- ! Call routines to supply the densities for all components.
- surfaceDensityComponent_ => surfaceDensityComponent
- surfaceDensity = node%mapDouble0(surfaceDensityComponent_,reductionSummation,optimizeFor=optimizeForSurfaceDensitySummation)
- call self%restore()
- return
- end function standardSurfaceDensity
-
- double precision function surfaceDensityComponent(component)
- !!{
- Unary function returning the surface density in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- surfaceDensityComponent=component%surfaceDensity(positionCylindrical_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_)
- return
- end function surfaceDensityComponent
-
- double precision function standardRadiusEnclosingSurfaceDensity(self,node,surfaceDensity,componentType,massType,weightBy,weightIndex) result(radius)
- !!{
- Return the radius enclosing a given surface density in {\normalfont \ttfamily node}.
- !!}
- use :: Kind_Numbers, only : kind_int8
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- implicit none
- class (galacticStructureStandard ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- double precision , intent(in ) :: surfaceDensity
- double precision :: radiusGuess
-
- call self%defaults(componentType=componentType,massType=massType,weightBy=weightBy,weightIndex=weightIndex)
- self_ => self
- node_ => node
- surfaceDensityTarget = surfaceDensity
- if (self%radiusEnclosingSurfaceDensityPrevious >= 0.0d0) then
- radiusGuess=self %radiusEnclosingSurfaceDensityPrevious
- else
- radiusGuess=self%darkMatterHaloScale_%radiusVirial (node)
- end if
- self%radiusEnclosingSurfaceDensityPrevious=self%finderSurfaceDensity%find(rootGuess=radiusGuess)
- radius=self%radiusEnclosingSurfaceDensityPrevious
- call self%restore()
- return
- end function standardRadiusEnclosingSurfaceDensity
-
- double precision function surfaceDensityRoot(radius)
- !!{
- Root function used in solving for the radius that encloses a given surface density.
- !!}
- use :: Galactic_Structure_Options, only : coordinateSystemCylindrical
- implicit none
- double precision, intent(in ) :: radius
-
- ! Evaluate the root function.
- surfaceDensityRoot=+self_%surfaceDensity (node_,[radius,0.0d0,0.0d0],coordinateSystemCylindrical,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_,galacticStructureState_%state%weightBy_,galacticStructureState_%state%weightIndex_) &
- & - surfaceDensityTarget
- return
- end function surfaceDensityRoot
-
- function standardAcceleration(self,node,positionCartesian,componentType,massType) result(acceleration)
- !!{
- Compute the gravitational acceleration at a given position.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
- use :: Galacticus_Nodes , only : optimizeForAccelerationSummation, reductionSummation, treeNode
- !![
-
- !!]
- include 'galactic_structure.acceleration.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- double precision , dimension(3) :: acceleration
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- integer , parameter :: accelerationSize =3
- procedure (accelerationComponent ), pointer :: accelerationComponent_
- double precision , dimension(3) :: accelerationComponent__
-
- call self%defaults(componentType=componentType,massType=massType)
- positionCartesian_ = positionCartesian
- accelerationComponent_ => accelerationComponent
- acceleration = node%mapDouble1(accelerationComponent_,accelerationSize,reductionSummation,optimizeFor=optimizeForAccelerationSummation)
- !![
-
- node,positionCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_
- acceleration=acceleration+accelerationComponent__
- !!]
- include 'galactic_structure.acceleration.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardAcceleration
-
- function accelerationComponent(component,resultSize)
- !!{
- Function returning the acceleration in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- integer , intent(in ) :: resultSize
- class (nodeComponent), intent(inout) :: component
- double precision , dimension(resultSize) :: accelerationComponent
-
- accelerationComponent=component%acceleration(positionCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_)
- return
- end function accelerationComponent
-
- function standardTidalTensor(self,node,positionCartesian,componentType,massType) result(tidalTensor)
- !!{
- Compute the gravitational tidal tensor at a given position.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
- use :: Galacticus_Nodes , only : optimizeForTidalTensorSummation, reductionSummation, treeNode
- use :: Tensors , only : tensorRank2Dimension3Symmetric , assignment(=)
- !![
-
- !!]
- include 'galactic_structure.tidal_tensor.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- type (tensorRank2Dimension3Symmetric) :: tidalTensor
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- procedure (tidalTensorComponent ), pointer :: tidalTensorComponent_
- type (tensorRank2Dimension3Symmetric) :: tidalTensorComponent__
-
- call self%defaults(componentType=componentType,massType=massType)
- positionCartesian_ = positionCartesian
- tidalTensorComponent_ => tidalTensorComponent
- tidalTensor = node%mapTensorR2D3(tidalTensorComponent_,reductionSummation,optimizeFor=optimizeForTidalTensorSummation)
- !![
-
- node,positionCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_
- tidalTensor=tidalTensor+tidalTensorComponent__
- !!]
- include 'galactic_structure.tidal_tensor.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardTidalTensor
-
- function tidalTensorComponent(component)
- !!{
- Function returning the tidal tensor in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- use :: Tensors , only : tensorRank2Dimension3Symmetric
- implicit none
- class(nodeComponent ), intent(inout) :: component
- type (tensorRank2Dimension3Symmetric) :: tidalTensorComponent
-
- tidalTensorComponent=component%tidalTensor(positionCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_)
- return
- end function tidalTensorComponent
-
- function standardChandrasekharIntegral(self,node,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType) result(chandrasekharIntegral)
- !!{
- Compute the integral appearing in the \cite{chandrasekhar_dynamical_1943} dynamical friction model:
- \begin{equation}
- \rho(\boldsymbol{x}_\mathrm{s}) \int \mathrm{d}\boldsymbol{v} f(\boldsymbol{v}) {\boldsymbol{v}-\boldsymbol{v}_\mathrm{s} \over |\boldsymbol{v}-\boldsymbol{v}_\mathrm{s}|^3},
- \end{equation}
- where $\rho(\boldsymbol{x}_\mathrm{s})$ is the density at the position of the perturber, $\boldsymbol{x}_\mathrm{s}$,
- $f(\boldsymbol{v})$ is the velocity distribution function at velocity $\boldsymbol{v}$, and $\boldsymbol{v}_\mathrm{s}$ is
- the velocity of the perturber.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
- use :: Galacticus_Nodes , only : optimizeForChandrasekharIntegralSummation, reductionSummation, treeNode
- !![
-
- !!]
- include 'galactic_structure.chandrasekharIntegral.tasks.modules.inc'
- !![
-
- !!]
- implicit none
- double precision , dimension(3) :: chandrasekharIntegral
- class (galacticStructureStandard ), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node , nodeSatellite
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- integer , parameter :: chandrasekharIntegralSize =3
- procedure (chandrasekharIntegralComponent), pointer :: chandrasekharIntegralComponent_
- double precision , dimension(3) :: chandrasekharIntegralComponent__
-
- call self%defaults(componentType=componentType,massType=massType)
- positionCartesian_ = positionCartesian
- velocityCartesian_ = velocityCartesian
- nodeSatellite_ => nodeSatellite
- chandrasekharIntegralComponent_ => chandrasekharIntegralComponent
- chandrasekharIntegral = node%mapDouble1(chandrasekharIntegralComponent_,chandrasekharIntegralSize,reductionSummation,optimizeFor=optimizeForChandrasekharIntegralSummation)
- !![
-
- node,nodeSatellite,positionCartesian_,velocityCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_
- chandrasekharIntegral=chandrasekharIntegral+chandrasekharIntegralComponent__
- !!]
- include 'galactic_structure.chandrasekharIntegral.tasks.inc'
- !![
-
- !!]
- call self%restore()
- return
- end function standardChandrasekharIntegral
-
- function chandrasekharIntegralComponent(component,resultSize)
- !!{
- Function returning the Chandrasekhar integral in a component. Suitable for mapping over components.
- !!}
- use :: Galacticus_Nodes, only : nodeComponent
- implicit none
- integer , intent(in ) :: resultSize
- class (nodeComponent), intent(inout) :: component
- double precision , dimension(resultSize) :: chandrasekharIntegralComponent
-
- chandrasekharIntegralComponent=component%chandrasekharIntegral(nodeSatellite_,positionCartesian_,velocityCartesian_,galacticStructureState_%state%componentType_,galacticStructureState_%state%massType_)
- return
- end function chandrasekharIntegralComponent
-
- double precision function standardVelocityDispersion(self,node,radius,radiusOuter,componentType,massType) result(velocityDispersion)
- !!{
- Returns the velocity dispersion of the specified {\normalfont \ttfamily componentType} in {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius}.
- !!}
- use :: Galactic_Structure_Options, only : radiusLarge
- use :: Numerical_Integration , only : integrator
- use :: Numerical_Constants_Math , only : Pi
- class (galacticStructureStandard ), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius , radiusOuter
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: densitySphericalAverage, densityVelocityVariance, &
- & massTotal
- type (integrator ) :: integrator_
-
- self_ => self
- node_ => node
- call self%defaults(componentType=componentType,massType=massType)
- massTotal=self%massEnclosed(node,radiusLarge,componentType=componentType,massType=massType)
- ! Return with zero dispersion if the component is massless.
- if (massTotal <= 0.0d0) then
- velocityDispersion=0.0d0
- return
- end if
- ! Integrate the Jeans equation.
- integrator_ =integrator (integrandVelocityDispersion,toleranceRelative=1.0d-3)
- densityVelocityVariance=integrator_%integrate(radius ,radiusOuter )
- ! Get the density at this radius.
- densitySphericalAverage=self_%densitySphericalAverage( &
- & node_ , &
- & radius , &
- & componentType=galacticStructureState_%state%componentType_, &
- & massType =galacticStructureState_%state%massType_ &
- & )
- ! Check for zero density.
- if (densitySphericalAverage <= 0.0d0) then
- velocityDispersion=0.0d0
- else
- velocityDispersion=sqrt(max(densityVelocityVariance,0.0d0)/densitySphericalAverage)
- end if
- call self%restore()
- return
- end function standardVelocityDispersion
-
- double precision function integrandVelocityDispersion(radius)
- !!{
- Integrand function used for finding velocity dispersions using Jeans equation.
- !!}
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- double precision, intent(in ) :: radius
-
- if (radius == 0.0d0) then
- integrandVelocityDispersion=0.0d0
- else
- integrandVelocityDispersion=+gravitationalConstantGalacticus &
- & *self_%massEnclosed ( &
- & node_ , &
- & radius &
- & ) &
- & /radius**2 &
- & *self_%densitySphericalAverage( &
- & node_ , &
- & radius , &
- & componentType=galacticStructureState_%state%componentType_, &
- & massType =galacticStructureState_%state%massType_ &
- & )
- end if
- return
- end function integrandVelocityDispersion
-
- subroutine standardDefaults(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Set the default values for options in the enclosed mass functions.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll, massTypeAll, weightByLuminosity, weightByMass, &
- & radiusLarge
- use :: Error , only : Error_Report
- implicit none
- class (galacticStructureStandard ), intent(inout) :: self
- double precision , intent(in ), optional :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- !![
-
-
-
-
-
- !!]
-
- ! Expand the state stack if necessary.
- if (.not.associated(galacticStructureState_)) then
- if (.not.associated(galacticStructureStateHead_)) allocate(galacticStructureStateHead_)
- galacticStructureState_ => galacticStructureStateHead_
- else
- if (.not.associated(galacticStructureState_%next)) then
- allocate(galacticStructureState_%next)
- galacticStructureState_%next%previous => galacticStructureState_
- end if
- galacticStructureState_ => galacticStructureState_%next
- end if
- ! Set defaults.
- galacticStructureState_%state%radius_ =radius_
- galacticStructureState_%state%massType_ =massType_
- galacticStructureState_%state%componentType_ =componentType_
- galacticStructureState_%state%weightBy_ =weightBy_
- select case (weightBy_%ID)
- case (weightByLuminosity%ID)
- if (.not.present(weightIndex)) call Error_Report('weightIndex should be specified for luminosity weighting'//{introspection:location})
- galacticStructureState_%state%weightIndex_=weightIndex_
- end select
- return
- end subroutine standardDefaults
-
- subroutine standardRestore(self)
- !!{
- Restore the previous state from the stack.
- !!}
- implicit none
- class(galacticStructureStandard), intent(inout) :: self
- !$GLC attributes unused :: self
-
- galacticStructureState_ => galacticStructureState_%previous
- return
- end subroutine standardRestore
diff --git a/source/galactic.structure.utilities.F90 b/source/galactic.structure.utilities.F90
deleted file mode 100644
index 57ab6a1664..0000000000
--- a/source/galactic.structure.utilities.F90
+++ /dev/null
@@ -1,417 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 of globally-accessible functions supporting the galactic structure class.
-!!}
-
-module Galactic_Structure_Utilities
- !!{
- Provides globally-accessible functions supporting the galactic structure class.
- !!}
- private
- public :: galacticStructureConstruct , galacticStructureMassEnclosed , galacticStructureDestruct , galacticStructureDeepCopy , &
- & galacticStructureDeepCopyReset , galacticStructureDeepCopyFinalize , galacticStructureStateStore, galacticStructureStateRestore, &
- & galacticStructureVelocityRotation, galacticStructureVelocityRotationGradient, galacticStructureDensity
-
- ! Module-scope pointer to our task object. This is used for reference counting so that debugging information is consistent
- ! between the increments and decrements.
- class(*), pointer :: galacticStructure__
- !$omp threadprivate(galacticStructure__)
-
-contains
-
- !![
-
- galacticStructureConstruct
- void
- Input_Parameters, only : inputParameters
- type (inputParameters), intent(inout), target :: parameters
- class(* ), intent( out), pointer :: galacticStructure_
-
- !!]
- subroutine galacticStructureConstruct(parameters,galacticStructure_)
- !!{
- Build a {\normalfont \ttfamily galacticStructure} object from a given parameter set. This is a globally-callable function
- to allow us to subvert the class/module hierarchy.
- !!}
- use :: Error , only : Error_Report
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass, galacticStructure
- implicit none
- type (inputParameters), intent(inout), target :: parameters
- class(* ), intent( out), pointer :: galacticStructure_
- type (inputParameters) , pointer :: parametersCurrent
-
- parametersCurrent => parameters
- do while (.not.parametersCurrent%isPresent('galacticStructure').and.associated(parametersCurrent%parent))
- parametersCurrent => parametersCurrent%parent
- end do
- if (.not.parametersCurrent%isPresent('galacticStructure')) parametersCurrent => parameters
- galacticStructure__ => galacticStructure(parameters)
- select type (galacticStructure__)
- class is (galacticStructureClass)
- !![
-
- !!]
- call galacticStructure__%autoHook()
- class default
- call Error_Report('unexpected class'//{introspection:location})
- end select
- galacticStructure_ => galacticStructure__
- return
- end subroutine galacticStructureConstruct
-
- !![
-
- galacticStructureMassEnclosed
- double precision
- Galacticus_Nodes , only : treeNode
- Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), optional :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
- !!]
- double precision function galacticStructureMassEnclosed(galacticStructure_,node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Compute the mass enclosed for a {\normalfont \ttfamily galacticStructure} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Galacticus_Nodes , only : treeNode
- implicit none
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
- double precision , intent(in ), optional :: radius
-
- select type (galacticStructure_)
- class is (galacticStructureClass)
- galacticStructureMassEnclosed=galacticStructure_%massEnclosed(node,radius,componentType,massType,weightBy,weightIndex)
- class default
- galacticStructureMassEnclosed=0.0d0
- call Error_Report('unexpected class'//{introspection:location})
- end select
- return
- end function galacticStructureMassEnclosed
-
- !![
-
- galacticStructureDensity
- double precision
- Galacticus_Nodes , only : treeNode
- Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType, enumerationCoordinateSystemType
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), dimension(3) :: position
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- integer , intent(in ), optional :: weightIndex
-
- !!]
- double precision function galacticStructureDensity(galacticStructure_,node,position,coordinateSystem,componentType,massType,weightBy,weightIndex)
- !!{
- Compute the density for a {\normalfont \ttfamily galacticStructure} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType, enumerationCoordinateSystemType
- use :: Galacticus_Nodes , only : treeNode
- implicit none
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (enumerationWeightByType ), intent(in ), optional :: weightBy
- type (enumerationCoordinateSystemType), intent(in ), optional :: coordinateSystem
- integer , intent(in ), optional :: weightIndex
- double precision , intent(in ), dimension(3) :: position
-
- select type (galacticStructure_)
- class is (galacticStructureClass)
- galacticStructureDensity=galacticStructure_%density(node,position,coordinateSystem,componentType,massType,weightBy,weightIndex)
- class default
- galacticStructureDensity=0.0d0
- call Error_Report('unexpected class'//{introspection:location})
- end select
- return
- end function galacticStructureDensity
-
- !![
-
- galacticStructureVelocityRotation
- double precision
- Galacticus_Nodes , only : treeNode
- Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), optional :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- !!]
- double precision function galacticStructureVelocityRotation(galacticStructure_,node,radius,componentType,massType)
- !!{
- Compute the rotation velocity for a {\normalfont \ttfamily galacticStructure} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- implicit none
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , intent(in ), optional :: radius
-
- select type (galacticStructure_)
- class is (galacticStructureClass)
- galacticStructureVelocityRotation=galacticStructure_%velocityRotation(node,radius,componentType,massType)
- class default
- galacticStructureVelocityRotation=0.0d0
- call Error_Report('unexpected class'//{introspection:location})
- end select
- return
- end function galacticStructureVelocityRotation
-
- !![
-
- galacticStructureVelocityRotationGradient
- double precision
- Galacticus_Nodes , only : treeNode
- Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ), optional :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- !!]
- double precision function galacticStructureVelocityRotationGradient(galacticStructure_,node,radius,componentType,massType)
- !!{
- Compute the rotation velocity gradient for a {\normalfont \ttfamily galacticStructure} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Galacticus_Nodes , only : treeNode
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- implicit none
- class (* ), intent(inout) :: galacticStructure_
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , intent(in ), optional :: radius
-
- select type (galacticStructure_)
- class is (galacticStructureClass)
- galacticStructureVelocityRotationGradient=galacticStructure_%velocityRotationGradient(node,radius,componentType,massType)
- class default
- galacticStructureVelocityRotationGradient=0.0d0
- call Error_Report('unexpected class'//{introspection:location})
- end select
- return
- end function galacticStructureVelocityRotationGradient
-
- !![
-
- galacticStructureDestruct
- void
- class(*), intent(inout), pointer :: galacticStructure_
-
- !!]
- subroutine galacticStructureDestruct(galacticStructure_)
- !!{
- Destruct a {\normalfont \ttfamily taskEvolveForests} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class(*), intent(inout), pointer :: galacticStructure_
-
- galacticStructure__ => galacticStructure_
- select type (galacticStructure__)
- class is (galacticStructureClass)
- !![
-
- !!]
- class default
- call Error_Report('unexpected class'//{introspection:location})
- end select
- return
- end subroutine galacticStructureDestruct
-
- !![
-
- galacticStructureStateRestore
- void
- ISO_C_Binding, only : c_ptr, c_size_t
- class (* ), intent(inout) :: self
- integer , intent(in ) :: stateFile
- type (c_ptr ), intent(in ) :: gslStateFile
- integer(c_size_t), intent(in ) :: stateOperationID
-
- !!]
- subroutine galacticStructureStateRestore(self,stateFile,gslStateFile,stateOperationID)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class (* ), intent(inout) :: self
- integer , intent(in ) :: stateFile
- type (c_ptr ), intent(in ) :: gslStateFile
- integer(c_size_t), intent(in ) :: stateOperationID
-
- select type (self)
- class is (galacticStructureClass)
- call self%stateRestore(stateFile,gslStateFile,stateOperationID)
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine galacticStructureStateRestore
-
- !![
-
- galacticStructureStateStore
- void
- ISO_C_Binding, only : c_ptr, c_size_t
- class (* ), intent(inout) :: self
- integer , intent(in ) :: stateFile
- type (c_ptr ), intent(in ) :: gslStateFile
- integer(c_size_t), intent(in ) :: stateOperationID
-
- !!]
- subroutine galacticStructureStateStore(self,stateFile,gslStateFile,stateOperationID)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class (* ), intent(inout) :: self
- integer , intent(in ) :: stateFile
- type (c_ptr ), intent(in ) :: gslStateFile
- integer(c_size_t), intent(in ) :: stateOperationID
-
- select type (self)
- class is (galacticStructureClass)
- call self%stateStore(stateFile,gslStateFile,stateOperationID)
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine galacticStructureStateStore
-
- !![
-
- galacticStructureDeepCopyReset
- void
- class(*), intent(inout) :: self
-
- !!]
- subroutine galacticStructureDeepCopyReset(self)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class(*), intent(inout) :: self
-
- select type (self)
- class is (galacticStructureClass)
- call self%deepCopyReset()
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine galacticStructureDeepCopyReset
-
- !![
-
- galacticStructureDeepCopyFinalize
- void
- class(*), intent(inout) :: self
-
- !!]
- subroutine galacticStructureDeepCopyFinalize(self)
- !!{
- Finalize a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class(*), intent(inout) :: self
-
- select type (self)
- class is (galacticStructureClass)
- call self%deepCopyFinalize()
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine galacticStructureDeepCopyFinalize
-
- !![
-
- galacticStructureDeepCopy
- void
- class(*), intent(inout) :: self, destination
-
- !!]
- subroutine galacticStructureDeepCopy(self,destination)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- implicit none
- class(*), intent(inout) :: self, destination
-
- select type (self)
- class is (galacticStructureClass)
- select type (destination)
- class is (galacticStructureClass)
- call self%deepCopy(destination)
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine galacticStructureDeepCopy
-
-end module Galactic_Structure_Utilities
diff --git a/source/galactic_structure.options.F90 b/source/galactic_structure.options.F90
index e7f543a3bd..3489fd994c 100644
--- a/source/galactic_structure.options.F90
+++ b/source/galactic_structure.options.F90
@@ -51,14 +51,15 @@ module Galactic_Structure_Options
yes
yes
yes
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
!!]
@@ -85,8 +86,9 @@ module Galactic_Structure_Options
structureErrorCode
Error codes for galactic structure functions.
-
-
+
+
+
!!]
diff --git a/source/galactic_structure.radius_solver.equilibrium.F90 b/source/galactic_structure.radius_solver.equilibrium.F90
index 6c7585b46d..4fdc83d551 100644
--- a/source/galactic_structure.radius_solver.equilibrium.F90
+++ b/source/galactic_structure.radius_solver.equilibrium.F90
@@ -21,10 +21,8 @@
Implementation of an ``equilibrium'' solver for galactic structure.
!!}
- use :: Dark_Matter_Profiles , only : darkMatterProfileClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -40,9 +38,7 @@
& solveForInactiveProperties , convergenceFailureIsFatal
double precision :: solutionTolerance
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileClass ), pointer :: darkMatterProfile_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: equilibriumDestructor
procedure :: solve => equilibriumSolve
@@ -78,9 +74,7 @@ function equilibriumConstructorParameters(parameters) result(self)
type (galacticStructureSolverEquilibrium) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileClass ), pointer :: darkMatterProfile_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: useFormationHalo , includeBaryonGravity , &
& solveForInactiveProperties, convergenceFailureIsFatal
double precision :: solutionTolerance
@@ -117,22 +111,18 @@ function equilibriumConstructorParameters(parameters) result(self)
-
-
!!]
- self=galacticStructureSolverEquilibrium(convergenceFailureIsFatal,useFormationHalo,includeBaryonGravity,solutionTolerance,solveForInactiveProperties,darkMatterHaloScale_,darkMatterProfile_,darkMatterProfileDMO_,galacticStructure_)
+ self=galacticStructureSolverEquilibrium(convergenceFailureIsFatal,useFormationHalo,includeBaryonGravity,solutionTolerance,solveForInactiveProperties,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
-
!!]
return
end function equilibriumConstructorParameters
- function equilibriumConstructorInternal(convergenceFailureIsFatal,useFormationHalo,includeBaryonGravity,solutionTolerance,solveForInactiveProperties,darkMatterHaloScale_,darkMatterProfile_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function equilibriumConstructorInternal(convergenceFailureIsFatal,useFormationHalo,includeBaryonGravity,solutionTolerance,solveForInactiveProperties,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily equilibrium} galactic structure solver class.
!!}
@@ -142,11 +132,9 @@ function equilibriumConstructorInternal(convergenceFailureIsFatal,useFormationHa
& solveForInactiveProperties, convergenceFailureIsFatal
double precision , intent(in ) :: solutionTolerance
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (darkMatterProfileClass ), intent(in ), target :: darkMatterProfile_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -180,9 +168,7 @@ subroutine equilibriumDestructor(self)
!![
-
-
!!]
if ( preDerivativeEvent%isAttached(self,equilibriumSolvePreDeriativeHook)) call preDerivativeEvent%detach(self,equilibriumSolvePreDeriativeHook)
if ( postEvolveEvent%isAttached(self,equilibriumSolveHook )) call postEvolveEvent%detach(self,equilibriumSolveHook )
@@ -304,31 +290,33 @@ subroutine radiusSolve(node,specificAngularMomentum,radiusGet,radiusSet,velocity
Solve for the equilibrium radius of the given component.
!!}
use :: Display , only : displayVerbosity , displayVerbositySet, verbosityLevelStandard
- use :: Galactic_Structure_Options , only : massTypeBaryonic , radiusLarge
+ use :: Galactic_Structure_Options , only : massTypeBaryonic , radiusLarge , massTypeDark , componentTypeDarkHalo
+ use :: Mass_Distributions , only : massDistributionClass
use :: Error , only : Error_Report
use :: ISO_Varying_String , only : varying_string
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: String_Handling , only : operator(//)
implicit none
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- procedure (solverGet ), intent(in ) , pointer :: radiusGet , velocityGet
- procedure (solverSet ), intent(in ) , pointer :: radiusSet , velocitySet
- double precision , dimension(:,:), allocatable, save :: radiusHistory
+ type (treeNode ), intent(inout) :: node
+ double precision , intent(in ) :: specificAngularMomentum
+ procedure (solverGet ), intent(in ) , pointer :: radiusGet , velocityGet
+ procedure (solverSet ), intent(in ) , pointer :: radiusSet , velocitySet
+ double precision , dimension(:,:), allocatable, save :: radiusHistory
!$omp threadprivate(radiusHistory)
- double precision , dimension(:,:), allocatable :: radiusHistoryTemporary
- double precision , dimension(: ), allocatable :: radiusStoredTmp , velocityStoredTmp
- integer , parameter :: storeIncrement =10
- integer , parameter :: iterationsForBisectionMinimum =10
- integer , parameter :: activeComponentMaximumIncrement= 2
- integer :: activeComponentMaximumCurrent
- character (len=14 ) :: label
- type (varying_string ), save :: message
+ double precision , dimension(:,:), allocatable :: radiusHistoryTemporary
+ double precision , dimension(: ), allocatable :: radiusStoredTmp , velocityStoredTmp
+ class (massDistributionClass), pointer :: massDistribution_
+ integer , parameter :: storeIncrement =10
+ integer , parameter :: iterationsForBisectionMinimum =10
+ integer , parameter :: activeComponentMaximumIncrement= 2
+ integer :: activeComponentMaximumCurrent
+ character (len=14 ) :: label
+ type (varying_string ), save :: message
!$omp threadprivate(message)
- double precision :: baryonicVelocitySquared , darkMatterMassFinal, &
- & darkMatterVelocitySquared , velocity , &
- & radius , radiusNew , &
- & specificAngularMomentumMaximum
+ double precision :: baryonicVelocitySquared , darkMatterMassFinal, &
+ & darkMatterVelocitySquared , velocity , &
+ & radius , radiusNew , &
+ & specificAngularMomentumMaximum
! Count the number of active components.
countComponentsActive=countComponentsActive+1
@@ -343,18 +331,22 @@ subroutine radiusSolve(node,specificAngularMomentum,radiusGet,radiusSet,velocity
radius=radiusGet(node)
if (radius <= 0.0d0) then
! No previous radius was set, so make a simple estimate of sizes of all components ignoring equilibrium contraction and self-gravity.
+ massDistribution_ => self%darkMatterProfileDMO_%get(node)
! First check that there is a solution within a reasonable radius.
- specificAngularMomentumMaximum=+self%darkMatterProfileDMO_%circularVelocity(node_,radiusLarge) &
- & * radiusLarge
+ specificAngularMomentumMaximum=+massDistribution_%rotationCurve(radiusLarge) &
+ & * radiusLarge
if (specificAngularMomentumMaximum < specificAngularMomentum) then
! No solution exists even within a very large radius. Use a simple estimate of the virial radius.
radius=self%darkMatterHaloScale_%radiusVirial (node_ )
else
- ! Find the radius in the dark matter profile with the required specific angular momentum
- radius=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum(node_,specificAngularMomentum)
+ ! Find the radius in the dark matter profile with the required specific angular momentum.
+ radius=massDistribution_%radiusFromSpecificAngularMomentum(specificAngularMomentum)
end if
! Find the velocity at this radius.
- velocity=self%darkMatterProfileDMO_%circularVelocity (node_,radius )
+ velocity=massDistribution_%rotationCurve(radius)
+ !![
+
+ !!]
else
! A previous radius was set, so use it, and the previous circular velocity, as the initial guess.
velocity=velocityGet(node)
@@ -386,14 +378,22 @@ subroutine radiusSolve(node,specificAngularMomentum,radiusGet,radiusSet,velocity
! On subsequent iterations do the full calculation providing component has non-zero specific angular momentum.
if (specificAngularMomentum <= 0.0d0) return
! Get current radius of the component.
- radius =radiusGet(node)
+ radius=radiusGet(node)
! Find the enclosed mass in the dark matter halo.
- darkMatterMassFinal =self%darkMatterProfile_%enclosedMass(node_,radius)
+ massDistribution_ => node %massDistribution (componentTypeDarkHalo,massTypeDark)
+ darkMatterMassFinal = massDistribution_%massEnclosedBySphere(radius )
+ !![
+
+ !!]
! Compute dark matter contribution to rotation curve.
darkMatterVelocitySquared=gravitationalConstantGalacticus*darkMatterMassFinal/radius
! Compute baryonic contribution to rotation curve.
if (self%includeBaryonGravity) then
- baryonicVelocitySquared=self%galacticStructure_%velocityRotation(node,radius,massType=massTypeBaryonic)**2
+ massDistribution_ => node %massDistribution(massType=massTypeBaryonic)
+ baryonicVelocitySquared = massDistribution_%rotationCurve ( radius )**2
+ !![
+
+ !!]
else
baryonicVelocitySquared=0.0d0
end if
diff --git a/source/galactic_structure.radius_solver.fixed.F90 b/source/galactic_structure.radius_solver.fixed.F90
index 3959fdc0e3..19255db895 100644
--- a/source/galactic_structure.radius_solver.fixed.F90
+++ b/source/galactic_structure.radius_solver.fixed.F90
@@ -22,9 +22,8 @@
proportion to specific angular momentum).
!!}
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -60,7 +59,6 @@
double precision :: factor
type (enumerationRadiusFixedType) :: radiusFixed
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
contains
final :: fixedDestructor
@@ -89,7 +87,6 @@ function fixedConstructorParameters(parameters) result(self)
type (galacticStructureSolverFixed) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
double precision :: factor
type (varying_string ) :: radiusFixed
@@ -109,20 +106,18 @@ function fixedConstructorParameters(parameters) result(self)
-
!!]
- self=galacticStructureSolverFixed(factor,enumerationRadiusFixedEncode(char(radiusFixed),includesPrefix=.false.),darkMatterHaloScale_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=galacticStructureSolverFixed(factor,enumerationRadiusFixedEncode(char(radiusFixed),includesPrefix=.false.),darkMatterHaloScale_,virialDensityContrast_)
!![
-
!!]
return
end function fixedConstructorParameters
- function fixedConstructorInternal(factor,radiusFixed,darkMatterHaloScale_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function fixedConstructorInternal(factor,radiusFixed,darkMatterHaloScale_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily fixed} galactic structure solver class.
!!}
@@ -132,10 +127,9 @@ function fixedConstructorInternal(factor,radiusFixed,darkMatterHaloScale_,darkMa
double precision , intent(in ) :: factor
type (enumerationRadiusFixedType ), intent(in ) :: radiusFixed
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
!![
-
+
!!]
if (.not.enumerationRadiusFixedIsValid(radiusFixed)) call Error_Report('invalid radiusFixed'//{introspection:location})
@@ -170,7 +164,6 @@ subroutine fixedDestructor(self)
!![
-
!!]
if ( preDerivativeEvent%isAttached(self,fixedSolvePreDeriativeHook)) call preDerivativeEvent%detach(self,fixedSolvePreDeriativeHook)
@@ -254,35 +247,42 @@ subroutine radiusSolve(node,specificAngularMomentum,radiusGet,radiusSet,velocity
!!{
Solve for the equilibrium radius of the given component.
!!}
- use :: Dark_Matter_Halo_Spins, only : Dark_Matter_Halo_Angular_Momentum_Scale
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentSpin, treeNode
+ use :: Dark_Matter_Halo_Spins , only : Dark_Matter_Halo_Angular_Momentum_Scale
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentSpin, treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeDarkMatterOnly , massTypeDark
implicit none
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- procedure (solverGet ), intent(in ), pointer :: radiusGet , velocityGet
- procedure (solverSet ), intent(in ), pointer :: radiusSet , velocitySet
- class (nodeComponentSpin ) , pointer :: spin
- class (nodeComponentBasic) , pointer :: basic
- double precision :: radius , velocity
+ type (treeNode ), intent(inout) :: node
+ double precision , intent(in ) :: specificAngularMomentum
+ procedure (solverGet ), intent(in ), pointer :: radiusGet , velocityGet
+ procedure (solverSet ), intent(in ), pointer :: radiusSet , velocitySet
+ class (nodeComponentSpin ) , pointer :: spin
+ class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass) , pointer :: massDistribution_
+ double precision :: radius , velocity
!$GLC attributes unused :: radiusGet, velocityGet, specificAngularMomentum
! Find the radius of the component, assuming radius is a fixed fraction of radius times spin parameter.
spin => node%spin()
select case (self%radiusFixed%ID)
case (radiusFixedVirial %ID)
- velocity = +self %darkMatterHaloScale_ %velocityVirial (node )
- radius = +self %darkMatterHaloScale_ %radiusVirial ( node ) &
- & *self %factor &
- & *spin %angularMomentum ( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale ( node , self %darkMatterProfileDMO_ )
+ velocity = +self %darkMatterHaloScale_ %velocityVirial ( node )
+ radius = +self %darkMatterHaloScale_ %radiusVirial ( node ) &
+ & *self %factor &
+ & *spin %angularMomentum ( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale ( node , self %darkMatterHaloScale_ )
case (radiusFixedTurnaround%ID)
- basic => node %basic ( )
- velocity = +self%darkMatterProfileDMO_ %circularVelocityMaximum ( node )
- radius = +self%darkMatterHaloScale_ %radiusVirial ( node ) &
- & *self%virialDensityContrast_%turnAroundOverVirialRadii(mass=basic%mass(),time=basic%timeLastIsolated ()) &
- & *self %factor &
- & *spin %angularMomentum ( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale ( node , self %darkMatterProfileDMO_ )
+ massDistribution_ => node %massDistribution ( componentTypeDarkMatterOnly, massTypeDark )
+ basic => node %basic ( )
+ velocity = +massDistribution_ %velocityRotationCurveMaximum( )
+ radius = +self %darkMatterHaloScale_ %radiusVirial ( node ) &
+ & *self %virialDensityContrast_%turnAroundOverVirialRadii (mass=basic%mass() ,time=basic%timeLastIsolated ()) &
+ & *self %factor &
+ & *spin %angularMomentum ( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale ( node , self %darkMatterHaloScale_ )
+ !![
+
+ !!]
end select
! Set the component size to new radius and velocity.
call radiusSet (node,radius )
diff --git a/source/galactic_structure.radius_solver.simple.F90 b/source/galactic_structure.radius_solver.simple.F90
index 759abc1dc4..4dc68b5a2d 100644
--- a/source/galactic_structure.radius_solver.simple.F90
+++ b/source/galactic_structure.radius_solver.simple.F90
@@ -260,23 +260,29 @@ subroutine radiusSolve(node,specificAngularMomentum,radiusGet,radiusSet,velocity
!!{
Solve for the equilibrium radius of the given component.
!!}
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: specificAngularMomentum
- procedure (solverGet), intent(in ), pointer :: radiusGet , velocityGet
- procedure (solverSet), intent(in ), pointer :: radiusSet , velocitySet
- double precision :: radius , velocity
+ type (treeNode ), intent(inout) :: node
+ double precision , intent(in ) :: specificAngularMomentum
+ procedure (solverGet ), intent(in ), pointer :: radiusGet , velocityGet
+ procedure (solverSet ), intent(in ), pointer :: radiusSet , velocitySet
+ class (massDistributionClass) , pointer :: massDistribution_
+ double precision :: radius , velocity
!$GLC attributes unused :: radiusGet, velocityGet
! Return immediately if the specific angular momentum is zero.
if (specificAngularMomentum <= 0.0d0) return
+ massDistribution_ => self%darkMatterProfileDMO_%get(haloNode)
! Find the radius in the dark matter profile with the required specific angular momentum
- radius=self%darkMatterProfileDMO_%radiusFromSpecificAngularMomentum(haloNode,specificAngularMomentum)
+ radius =massDistribution_%radiusFromSpecificAngularMomentum(specificAngularMomentum)
! Find the velocity at this radius.
- velocity=self%darkMatterProfileDMO_%circularVelocity(haloNode,radius)
+ velocity=massDistribution_%rotationCurve (radius )
! Set the component size to new radius and velocity.
call radiusSet (node,radius )
call velocitySet(node,velocity)
+ !![
+
+ !!]
return
end subroutine radiusSolve
diff --git a/source/halo_model.projected_correlation_function.F90 b/source/halo_model.projected_correlation_function.F90
index 4ab7dc4dc1..29430c4aa6 100644
--- a/source/halo_model.projected_correlation_function.F90
+++ b/source/halo_model.projected_correlation_function.F90
@@ -305,10 +305,13 @@ double precision function powerSpectrumOneHaloIntegrand(massHalo)
!!}
use :: Conditional_Mass_Functions, only : haloModelGalaxyTypeCentral, haloModelGalaxyTypeSatellite
use :: Calculations_Resets , only : Calculations_Reset
- implicit none
- double precision, intent(in ) :: massHalo
- double precision :: darkMatterProfileKSpace, numberCentrals , &
- & numberSatellites , wavenumberMaximum
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
+ double precision , intent(in ) :: massHalo
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: darkMatterProfileKSpace, numberCentrals , &
+ & numberSatellites , wavenumberMaximum, &
+ & radiusVirial
call Calculations_Reset(node)
call basic % massSet(massHalo )
@@ -320,17 +323,22 @@ double precision function powerSpectrumOneHaloIntegrand(massHalo)
if (waveNumber(iWavenumber) > wavenumberMaximum) then
powerSpectrumOneHaloIntegrand=0.0d0
else
- darkMatterProfileKSpace=darkMatterProfileDMO_%kSpace(node,waveNumber(iWavenumber)/expansionFactor)
- numberCentrals =max( &
- & +0.0d0 , &
- & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum,haloModelGalaxyTypeCentral ) &
- & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum,haloModelGalaxyTypeCentral ) &
- & )
- numberSatellites =max( &
- & +0.0d0 , &
- & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum,haloModelGalaxyTypeSatellite) &
- & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum,haloModelGalaxyTypeSatellite) &
- & )
+ massDistribution_ => darkMatterProfileDMO_%get (node )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node )
+ darkMatterProfileKSpace = massDistribution_ %fourierTransform(radiusVirial,waveNumber(iWavenumber)/expansionFactor)
+ numberCentrals = max( &
+ & +0.0d0 , &
+ & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum,haloModelGalaxyTypeCentral ) &
+ & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum,haloModelGalaxyTypeCentral ) &
+ & )
+ numberSatellites = max( &
+ & +0.0d0 , &
+ & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum,haloModelGalaxyTypeSatellite) &
+ & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum,haloModelGalaxyTypeSatellite) &
+ & )
+ !![
+
+ !!]
! Note that we include 2 times the central-satellite term since we want to count each pair twice (i.e. central-satellite and
! then satellite-central). This is consistent with the N(N-1) counting for the satellite-satellite term, and with the
! counting in the two-halo term.
@@ -352,8 +360,8 @@ double precision function powerSpectrumTwoHaloTimeIntegrand(timePrime)
!!{
Time integrand for the two-halo term in the power spectrum.
!!}
- use :: Display , only : displayMessage , verbosityLevelWarn, displayMagenta, displayReset
- use :: Error, only : errorStatusSuccess
+ use :: Display, only : displayMessage , verbosityLevelWarn, displayMagenta, displayReset
+ use :: Error , only : errorStatusSuccess
implicit none
double precision , intent(in ) :: timePrime
type (integrator) :: integratorTime
@@ -387,9 +395,11 @@ double precision function powerSpectrumTwoHaloIntegrand(massHalo)
Integrand for the two-halo term in the power spectrum.
!!}
use :: Calculations_Resets, only : Calculations_Reset
- implicit none
- double precision, intent(in ) :: massHalo
- double precision :: wavenumberMaximum
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
+ double precision , intent(in ) :: massHalo
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: wavenumberMaximum, radiusVirial
call Calculations_Reset(node)
call basic % massSet(massHalo )
@@ -401,15 +411,19 @@ double precision function powerSpectrumTwoHaloIntegrand(massHalo)
if (waveNumber(iWavenumber) > wavenumberMaximum) then
powerSpectrumTwoHaloIntegrand=0.0d0
else
- powerSpectrumTwoHaloIntegrand= &
- & +haloMassFunction_ %differential(time,massHalo ) &
- & *darkMatterHaloBias_ %bias (node ) &
- & *darkMatterProfileDMO_%kSpace (node,waveNumber(iWavenumber)/expansionFactor) &
- & *max( &
- & +0.0d0 , &
- & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum) &
- & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum) &
- & )
+ massDistribution_ => darkMatterProfileDMO_%get (node )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node )
+ powerSpectrumTwoHaloIntegrand = +haloMassFunction_ %differential (time,massHalo ) &
+ & *darkMatterHaloBias_ %bias (node ) &
+ & *massDistribution_ %fourierTransform(radiusVirial,waveNumber(iWavenumber)/expansionFactor) &
+ & *max( &
+ & +0.0d0 , &
+ & +conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMinimum) &
+ & -conditionalMassFunction_%massFunction(massHalo,projectedCorrelationFunctionMassMaximum) &
+ & )
+ !![
+
+ !!]
end if
return
end function powerSpectrumTwoHaloIntegrand
diff --git a/source/hot_halo.cold_mode.mass_distribution.F90 b/source/hot_halo.cold_mode.mass_distribution.F90
new file mode 100644
index 0000000000..3b60ec8a75
--- /dev/null
+++ b/source/hot_halo.cold_mode.mass_distribution.F90
@@ -0,0 +1,52 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 provides a hot halo cold mode mass distribution class.
+!!}
+
+module Hot_Halo_Cold_Mode_Mass_Distributions
+ !!{
+ Provides an object which provides a hot halo cold mode mass distribution class.
+ !!}
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : enumerationWeightByType
+ private
+
+ !![
+
+ hotHaloColdModeMassDistribution
+ Hot Halo Cold Mode Mass Distributions
+
+ Object implementing hot halo cold mode mass distributions.
+
+ betaProfile
+
+ Return the mass distribution of the hot halo cold mode component.
+ class(massDistributionClass)
+ yes
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+
+ !!]
+
+end module Hot_Halo_Cold_Mode_Mass_Distributions
diff --git a/source/hot_halo.cold_mode.mass_distribution.beta_profile.F90 b/source/hot_halo.cold_mode.mass_distribution.beta_profile.F90
new file mode 100644
index 0000000000..e6d04520ff
--- /dev/null
+++ b/source/hot_halo.cold_mode.mass_distribution.beta_profile.F90
@@ -0,0 +1,202 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 .
+
+!!{
+An implementation of the hot halo mass distribution class for $\beta$-profile distributions.
+!!}
+
+ use :: Hot_Halo_Cold_Mode_Density_Core_Radii, only : hotHaloColdModeCoreRadiiClass
+ use :: Mass_Distributions , only : massDistributionBetaProfile
+
+ !![
+
+
+ A hot halo cold mod mass distribution class which adopts a spherically symmetric $\beta$-profile density profile for the hot
+ halo. Specifically,
+ \begin{equation}
+ \rho_\mathrm{hot halo}(r) \propto \left[ r^2 + r_\mathrm{core}^2 \right]^{3\beta/2},
+ \end{equation}
+ where the core radius, $r_\mathrm{core}$, is set using the selected cored profile core radius method (see
+ \refPhysics{hotHaloColdModeMassDistributionCoreRadius}). The value of $\beta$ is specified by the {\normalfont
+ \ttfamily [beta]} parameter. The profile is normalized such that the current mass in the hot gas profile is contained
+ within the outer radius of the hot halo, $r_\mathrm{hot, outer}$.
+
+
+ !!]
+ type, extends(hotHaloColdModeMassDistributionClass) :: hotHaloColdModeMassDistributionBetaProfile
+ !!{
+ A $\beta$-profile implementation of the hot halo mass distribution class.
+ !!}
+ private
+ double precision :: beta
+ class (hotHaloColdModeCoreRadiiClass), pointer :: hotHaloColdModeCoreRadii_ => null()
+ contains
+ final :: betaProfileDestructor
+ procedure :: get => betaProfileGet
+ end type hotHaloColdModeMassDistributionBetaProfile
+
+ interface hotHaloColdModeMassDistributionBetaProfile
+ !!{
+ Constructors for the $\beta$-profile hot halo cold mode mass distribution class.
+ !!}
+ module procedure betaProfileConstructorParameters
+ module procedure betaProfileConstructorInternal
+ end interface hotHaloColdModeMassDistributionBetaProfile
+
+contains
+
+ function betaProfileConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily betaProfile} hot halo cold mode mass distributionclass which builds the object from a
+ parameter set.
+ !!}
+ use :: Array_Utilities , only : operator(.intersection.)
+ use :: Error , only : Component_List , Error_Report
+ use :: Galacticus_Nodes, only : defaultHotHaloComponent
+ use :: Input_Parameters, only : inputParameter , inputParameters
+ implicit none
+ type (hotHaloColdModeMassDistributionBetaProfile) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ logical , save :: initialized =.false.
+ class (hotHaloColdModeCoreRadiiClass ), pointer :: hotHaloColdModeCoreRadii_
+ double precision :: beta
+
+ if (.not.initialized) then
+ !$omp critical(betaProfileColdModeInitialized)
+ if (.not.initialized) then
+ ! Check that required property is gettable.
+ if ( &
+ & .not.( &
+ & defaultHotHaloComponent% massColdIsGettable() &
+ & .and. &
+ & defaultHotHaloComponent%outerRadiusIsGettable() &
+ & ) &
+ & ) call Error_Report &
+ & ( &
+ & 'This method requires that the "massCold" property of the hot halo is gettable.'// &
+ & Component_List( &
+ & 'hotHalo' , &
+ & defaultHotHaloComponent% massColdAttributeMatch(requireGettable=.true.) &
+ & .intersection. &
+ & defaultHotHaloComponent%outerRadiusAttributeMatch(requireGettable=.true.) &
+ & ) // &
+ & {introspection:location} &
+ & )
+ initialized=.true.
+ end if
+ !$omp end critical(betaProfileColdModeInitialized)
+ end if
+
+ !![
+
+ beta
+ 2.0d0/3.0d0
+ The value of $\beta$ in $\beta$-profile hot halo cold mode mass distributions.
+
+
+
+ !!]
+ self=hotHaloColdModeMassDistributionBetaProfile(beta,hotHaloColdModeCoreRadii_)
+ !![
+
+
+ !!]
+ return
+ end function betaProfileConstructorParameters
+
+ function betaProfileConstructorInternal(beta,hotHaloColdModeCoreRadii_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily betaProfile} hot halo mass distribution class.
+ !!}
+ implicit none
+ type (hotHaloColdModeMassDistributionBetaProfile) :: self
+ double precision , intent(in ) :: beta
+ class (hotHaloColdModeCoreRadiiClass ), intent(in ), target :: hotHaloColdModeCoreRadii_
+ !![
+
+ !!]
+
+ return
+ end function betaProfileConstructorInternal
+
+ subroutine betaProfileDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily betaProfile} hot halo mass distribution class.
+ !!}
+ implicit none
+ type(hotHaloColdModeMassDistributionBetaProfile), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine betaProfileDestructor
+
+ function betaProfileGet(self,node,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the $\beta$-profile hot halo mass distribution for the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo , treeNode
+ use :: Galactic_Structure_Options, only : componentTypeColdHalo, massTypeGaseous, weightByMass
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloColdModeMassDistributionBetaProfile), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ double precision :: radiusScale , radiusOuter, &
+ & mass
+ !![
+
+ !!]
+
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get properties of the hot halo.
+ radiusScale = self %hotHaloColdModeCoreRadii_%radius (node)
+ hotHalo => node %hotHalo ( )
+ radiusOuter = hotHalo %outerRadius( )
+ mass = hotHalo %massCold ( )
+ ! If outer radius is non-positive return a null profile.
+ if (radiusOuter <= 0.0d0 .or. mass <= 0.0d0) return
+ ! Create the beta-profile distribution.
+ allocate(massDistributionBetaProfile :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionBetaProfile)
+ !![
+
+
+ massDistributionBetaProfile( &
+ & beta =self%beta , &
+ & coreRadius = radiusScale , &
+ & mass = mass , &
+ & outerRadius = radiusOuter , &
+ & truncateAtOuterRadius= .true. , &
+ & componentType = componentTypeColdHalo, &
+ & massType = massTypeGaseous &
+ & )
+
+
+ !!]
+ end select
+ return
+ end function betaProfileGet
diff --git a/source/hot_halo.mass_distribution.Enzo_hydrostatic.F90 b/source/hot_halo.mass_distribution.Enzo_hydrostatic.F90
index 3e6ceeb97b..c921135390 100644
--- a/source/hot_halo.mass_distribution.Enzo_hydrostatic.F90
+++ b/source/hot_halo.mass_distribution.Enzo_hydrostatic.F90
@@ -49,18 +49,8 @@
class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
class(hotHaloMassDistributionCoreRadiusClass), pointer :: hotHaloMassDistributionCoreRadius_ => null()
contains
- !![
-
-
-
- !!]
- final :: enzoHydrostaticDestructor
- procedure :: densityNormalization => enzoHydrostaticDensityNormalization
- procedure :: density => enzoHydrostaticDensity
- procedure :: densityLogSlope => enzoHydrostaticDensityLogSlope
- procedure :: enclosedMass => enzoHydrostaticEnclosedMass
- procedure :: radialMoment => enzoHydrostaticRadialMoment
- procedure :: rotationNormalization => enzoHydrostaticRotationNormalization
+ final :: enzoHydrostaticDestructor
+ procedure :: get => enzoHydrostaticGet
end type hotHaloMassDistributionEnzoHydrostatic
interface hotHaloMassDistributionEnzoHydrostatic
@@ -71,11 +61,6 @@
module procedure enzoHydrostaticConstructorInternal
end interface hotHaloMassDistributionEnzoHydrostatic
- type (treeNode ), pointer :: node_
- double precision :: radiusScale
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- !$omp threadprivate(node_,radiusScale,hotHaloTemperatureProfile_)
-
contains
function enzoHydrostaticConstructorParameters(parameters) result(self)
@@ -131,202 +116,55 @@ subroutine enzoHydrostaticDestructor(self)
return
end subroutine enzoHydrostaticDestructor
- double precision function enzoHydrostaticDensityNormalization(self,node)
- !!{
- Return the density normalization in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
- use :: Numerical_Integration, only : integrator
- implicit none
- class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- double precision , parameter :: toleranceRelative=1.0d-3
- type (integrator ) :: integrator_
- double precision :: radiusInner , radiusOuter
-
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius (node)
- hotHaloTemperatureProfile_ => self %hotHaloTemperatureProfile_
- node_ => node
- hotHalo => node %hotHalo ( )
- if ( &
- & hotHalo%mass () <= 0.0d0 &
- & .or. &
- & hotHalo%outerRadius() <= 0.0d0 &
- & ) then
- enzoHydrostaticDensityNormalization=0.0d0
- else
- integrator_ =integrator (enzoHydrostaticEnclosedMassIntegrand,toleranceRelative=toleranceRelative)
- radiusInner =+0.0d0
- radiusOuter =+hotHalo %outerRadius( )
- enzoHydrostaticDensityNormalization=+hotHalo %mass ( ) &
- & /integrator_%integrate (radiusInner ,radiusOuter )
- end if
- return
- end function enzoHydrostaticDensityNormalization
-
- double precision function enzoHydrostaticEnclosedMassIntegrand(radius)
- !!{
- Integrand used in finding the normalization of the {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- double precision, intent(in ) :: radius
- double precision :: temperature, radiusEffective
-
- if (radius <= 0.0d0) then
- enzoHydrostaticEnclosedMassIntegrand=0.0d0
- else
- radiusEffective =max(radius,radiusScale)
- temperature =hotHaloTemperatureProfile_%temperature(node_,radiusEffective)
- enzoHydrostaticEnclosedMassIntegrand=+4.0d0 &
- & *Pi &
- & /temperature &
- & *radius **2 &
- & /radiusEffective**3
- end if
- return
- end function enzoHydrostaticEnclosedMassIntegrand
-
- double precision function enzoHydrostaticDensity(self,node,radius)
+ function enzoHydrostaticGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the density in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
+ Return the Enzo hydrostatic hot halo mass distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo , treeNode
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous, weightByMass
+ use :: Mass_Distributions , only : massDistributionEnzoHydrostatic
implicit none
- class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: temperature , radiusScale, &
- & radiusEffective
-
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius (node )
- radiusEffective = max(radius,radiusScale)
- temperature = +self%hotHaloTemperatureProfile_ %temperature(node,radiusEffective)
- enzoHydrostaticDensity = +self%densityNormalization (node ) &
- & /temperature &
- & /radiusEffective **3
- return
- end function enzoHydrostaticDensity
-
- double precision function enzoHydrostaticDensityLogSlope(self,node,radius)
- !!{
- Return the logarithmic slope of the density profile in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass
- distribution.
- !!}
- implicit none
- class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: radiusScale
-
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius (node )
- if (radius > radiusScale) then
- enzoHydrostaticDensityLogSlope = -self%hotHaloTemperatureProfile_ %temperatureLogSlope(node,radius) &
- & -3.0d0
- else
- enzoHydrostaticDensityLogSlope = +0.0d0
- end if
- return
- end function enzoHydrostaticDensityLogSlope
-
- double precision function enzoHydrostaticEnclosedMass(self,node,radius)
- !!{
- Return the enclosed mass in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
- use :: Numerical_Integration, only : integrator
- implicit none
- class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- double precision , parameter :: toleranceRelative=1.0d-3
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- type (integrator ) :: integrator_
- double precision :: radiusInner , radiusOuter
-
- hotHalo => node%hotHalo()
- if (radius > hotHalo%outerRadius()) then
- enzoHydrostaticEnclosedMass=hotHalo%mass()
- else
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius(node)
- hotHaloTemperatureProfile_ => self%hotHaloTemperatureProfile_
- node_ => node
- radiusInner = +0.0d0
- radiusOuter = +radius
- integrator_ = integrator (enzoHydrostaticEnclosedMassIntegrand,toleranceRelative=toleranceRelative)
- enzoHydrostaticEnclosedMass = +self %densityNormalization(node ) &
- & *integrator_%integrate (radiusInner ,radiusOuter )
- end if
- return
- end function enzoHydrostaticEnclosedMass
-
- double precision function enzoHydrostaticRadialMoment(self,node,moment,radius)
- !!{
- Return a radial moment of an {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
- use :: Numerical_Integration, only : integrator
- implicit none
- class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment , radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
- double precision , parameter :: toleranceRelative=1.0d-3
- type (integrator ) :: integrator_
- double precision :: radiusInner , radiusOuter, &
- & radiusScale
-
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius (node)
- hotHalo => node %hotHalo( )
- radiusInner = 0.0d0
- radiusOuter = min( &
- & radius , &
- & hotHalo%outerRadius() &
- & )
- integrator_ = integrator (enzoHydrostaticRadialMomentIntegrand,toleranceRelative=toleranceRelative)
- enzoHydrostaticRadialMoment = +self %densityNormalization(node ) &
- & *integrator_%integrate (radiusInner ,radiusOuter )
- return
-
- contains
-
- double precision function enzoHydrostaticRadialMomentIntegrand(radius)
- !!{
- Integrand used in finding the normalization of the {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
- !!}
- implicit none
- double precision, intent(in ) :: radius
- double precision :: temperature, radiusEffective
-
- if (radius <= 0.0d0) then
- enzoHydrostaticRadialMomentIntegrand=0.0d0
- else
- radiusEffective =max(radius,radiusScale)
- temperature =self%hotHaloTemperatureProfile_%temperature(node_,radiusEffective)
- enzoHydrostaticRadialMomentIntegrand=+radius **moment &
- & /radiusEffective**3 &
- & /temperature
- end if
- return
- end function enzoHydrostaticRadialMomentIntegrand
-
- end function enzoHydrostaticRadialMoment
-
- double precision function enzoHydrostaticRotationNormalization(self,node)
- !!{
- Return the relation between specific angular momentum and rotation velocity (assuming a
- rotation velocity that is constant in radius) for {\normalfont \ttfamily node}. Specifically, the
- normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class(hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloMassDistributionEnzoHydrostatic), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ double precision :: radiusScale , radiusOuter, &
+ & mass
+ !![
+
+ !!]
- hotHalo => node%hotHalo()
- enzoHydrostaticRotationNormalization= &
- & self%radialMoment(node,2.0d0,hotHalo%outerRadius()) &
- & /self%radialMoment(node,3.0d0,hotHalo%outerRadius())
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get properties of the hot halo.
+ radiusScale = self %hotHaloMassDistributionCoreRadius_%radius (node)
+ hotHalo => node %hotHalo ( )
+ radiusOuter = hotHalo %outerRadius( )
+ mass = hotHalo %mass ( )
+ ! If outer radius is non-positive return a null profile.
+ if (radiusOuter <= 0.0d0 .or. mass <= 0.0d0) return
+ ! Create the mass distribution.
+ allocate(massDistributionEnzoHydrostatic :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionEnzoHydrostatic)
+ !![
+
+
+ massDistributionEnzoHydrostatic( &
+ & mass = mass , &
+ & radiusOuter = radiusOuter , &
+ & radiusScale = radiusScale , &
+ & truncateAtOuterRadius= .true. , &
+ & componentType = componentTypeHotHalo, &
+ & massType = massTypeGaseous &
+ & )
+
+
+ !!]
+ end select
return
- end function enzoHydrostaticRotationNormalization
+ end function enzoHydrostaticGet
diff --git a/source/hot_halo.mass_distribution.F90 b/source/hot_halo.mass_distribution.F90
index 06fdb8647a..43dfcafb68 100644
--- a/source/hot_halo.mass_distribution.F90
+++ b/source/hot_halo.mass_distribution.F90
@@ -25,15 +25,10 @@ module Hot_Halo_Mass_Distributions
!!{
Provides an object which provides a hot halo mass distribution class.
!!}
- use :: Galacticus_Nodes , only : treeNode
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : enumerationWeightByType
private
- public :: hotHaloMassDistributionDensity , hotHaloMassDistributionRotationCurve , &
- & hotHaloMassDistributionEnclosedMass , hotHaloMassDistributionRotationCurveGradient , &
- & hotHaloMassDistributionAcceleration , hotHaloMassDistributionAccelerationTidalTensor, &
- & hotHaloMassDistributionChandrasekharIntegral, hotHaloMassDistributionThreadInitialize , &
- & hotHaloMassDistributionThreadUninitialize , hotHaloMassDistributionDefaultStateStore , &
- & hotHaloMassDistributionDefaultStateRestore , hotHaloMassDistributionDensitySphericalAverage
!![
@@ -43,431 +38,15 @@ module Hot_Halo_Mass_Distributions
Object implementing hot halo mass distributions.
betaProfile
-
- Return the density of the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
+
+ Return the mass distribution of the hot halo.
+ class(massDistributionClass)
yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Return the logarithmic slope of the density of the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Return the mass enclosed in the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
- yes
- type (treeNode), intent(inout), target :: node
- double precision , intent(in ) :: radius
-
-
- Return the specified radial{\normalfont \ttfamily moment} of the density of the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: moment, radius
-
-
- Return the integral of the square of the density of the hot halo from zero to the given {\normalfont \ttfamily radius}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Returns the relation between specific angular momentum and rotation velocity (assuming a rotation velocity that is constant in radius) for {\normalfont \ttfamily node}. Specifically, the normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$.
- double precision
- yes
- type(treeNode), intent(inout) :: node
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
!!]
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- !$omp threadprivate(hotHaloMassDistribution_,hotHaloTemperatureProfile_)
-
- contains
-
- !![
-
- hotHaloMassDistributionThreadInitialize
-
- !!]
- subroutine hotHaloMassDistributionThreadInitialize(parameters_)
- !!{
- Initializes the hot halo profile structure tasks module.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type(inputParameters), intent(inout) :: parameters_
-
- !![
-
-
- !!]
- return
- end subroutine hotHaloMassDistributionThreadInitialize
-
- !![
-
- hotHaloMassDistributionThreadUninitialize
-
- !!]
- subroutine hotHaloMassDistributionThreadUninitialize()
- !!{
- Uninitializes the hot halo profile structure tasks module.
- !!}
- implicit none
-
- !![
-
-
- !!]
- return
- end subroutine hotHaloMassDistributionThreadUninitialize
-
- !![
-
- hotHaloMassDistributionEnclosedMass
-
- !!]
- double precision function hotHaloMassDistributionEnclosedMass(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for a hot halo profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeHotHalo , massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , radiusLarge , weightByMass, enumerationComponentTypeType, &
- & enumerationMassTypeType, enumerationWeightByType
- use :: Galacticus_Nodes , only : nodeComponentHotHalo , treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
- !$GLC attributes unused :: weightIndex
-
- ! Return zero mass if the requested mass type or component is not matched.
- hotHaloMassDistributionEnclosedMass=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeHotHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
- ! Return the enclosed mass.
- if (radius >= radiusLarge) then
- hotHalo => node %hotHalo()
- hotHaloMassDistributionEnclosedMass = hotHalo%mass ()
- else
- hotHaloMassDistributionEnclosedMass = max(hotHaloMassDistribution_%enclosedMass(node,radius),0.0d0)
- end if
- return
- end function hotHaloMassDistributionEnclosedMass
-
- !![
-
- hotHaloMassDistributionAcceleration
-
- !!]
- function hotHaloMassDistributionAcceleration(node,positionCartesian,componentType,massType)
- !!{
- Computes the acceleration due to a hot halo profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: hotHaloMassDistributionAcceleration
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
-
- radius =+sqrt(sum(positionCartesian**2))
- hotHaloMassDistributionAcceleration=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *hotHaloMassDistributionEnclosedMass(node,radius,componentType,massType,weightByMass,weightIndexNull) &
- & *positionCartesian &
- & /radius**3
- return
- end function hotHaloMassDistributionAcceleration
-
- !![
-
- hotHaloMassDistributionAccelerationTidalTensor
-
- !!]
- function hotHaloMassDistributionAccelerationTidalTensor(node,positionCartesian,componentType,massType)
- !!{
- Computes the tidalTensor due to the cold mode halo.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorIdentityR2D3Sym, assignment(=) , operator(*)
- use :: Vectors , only : Vector_Outer_Product
- implicit none
- type (tensorRank2Dimension3Symmetric) :: hotHaloMassDistributionAccelerationTidalTensor
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision , dimension(3) :: positionSpherical
- double precision :: radius , massEnclosed, &
- & density
- type (tensorRank2Dimension3Symmetric) :: positionTensor
-
- radius =sqrt(sum(positionCartesian**2))
- positionSpherical=[radius,0.0d0,0.0d0]
- massEnclosed =hotHaloMassDistributionEnclosedMass(node,radius ,componentType,massType,weightByMass,weightIndexNull)
- density =hotHaloMassDistributionDensity (node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- positionTensor =Vector_Outer_Product ( positionCartesian,symmetrize=.true. )
- hotHaloMassDistributionAccelerationTidalTensor=+gravitationalConstantGalacticus &
- & *( &
- & -(massEnclosed /radius**3)*tensorIdentityR2D3Sym &
- & +(massEnclosed*3.0d0 /radius**5)*positionTensor &
- & -(density *4.0d0*Pi/radius**2)*positionTensor &
- & )
- return
- end function hotHaloMassDistributionAccelerationTidalTensor
-
- !![
-
- hotHaloMassDistributionChandrasekharIntegral
-
- !!]
- function hotHaloMassDistributionChandrasekharIntegral(node,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
- !!{
- Computes the Chandrasekhar integral due to the hot halo.
- !!}
- use :: Galactic_Structure_Options, only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Ideal_Gases_Thermodynamics, only : Ideal_Gas_Sound_Speed
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- double precision , dimension(3) :: hotHaloMassDistributionChandrasekharIntegral
- type (treeNode ), intent(inout) :: node , nodeSatellite
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- double precision , dimension(3) :: positionSpherical
- double precision , parameter :: XvMaximum =10.0d0
- double precision :: radius , velocity , &
- & density , xV
- !$GLC attributes unused :: radiusHalfMass, nodeSatellite
-
- hotHaloMassDistributionChandrasekharIntegral=0.0d0
- radius = sqrt(sum(positionCartesian**2))
- velocity = sqrt(sum(velocityCartesian**2))
- if (velocity <= 0.0d0) return
- positionSpherical = [radius,0.0d0,0.0d0]
- density = hotHaloMassDistributionDensity(node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (density <= 0.0d0) return
- xV =+velocity &
- & /Ideal_Gas_Sound_Speed(hotHaloTemperatureProfile_%temperature(node,radius)) &
- & /sqrt(2.0d0)
- hotHaloMassDistributionChandrasekharIntegral=-density &
- & *velocityCartesian &
- & /velocity **3
- if (Xv <= XvMaximum) &
- & hotHaloMassDistributionChandrasekharIntegral=+hotHaloMassDistributionChandrasekharIntegral &
- & *( &
- & +erf ( xV ) &
- & -2.0d0 &
- & * xV &
- & *exp (-xV**2) &
- & /sqrt( Pi ) &
- & )
- return
- end function hotHaloMassDistributionChandrasekharIntegral
-
- !![
-
- hotHaloMassDistributionRotationCurve
-
- !!]
- double precision function hotHaloMassDistributionRotationCurve(node,radius,componentType,massType)
- !!{
- Computes the rotation curve at a given radius for the hot halo density profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentMass
-
- ! Compute rotation curve if radius is non-zero.
- hotHaloMassDistributionRotationCurve=0.0d0
- if (radius > 0.0d0) then
- componentMass=hotHaloMassDistributionEnclosedMass(node,radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) &
- & hotHaloMassDistributionRotationCurve=+sqrt( &
- & +gravitationalConstantGalacticus &
- & *componentMass &
- & /radius &
- & )
- end if
- return
- end function hotHaloMassDistributionRotationCurve
-
- !![
-
- hotHaloMassDistributionRotationCurveGradient
-
- !!]
- double precision function hotHaloMassDistributionRotationCurveGradient(node,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient at a given radius for the hot halo density profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentDensity, componentMass
-
- ! Set to zero by default.
- hotHaloMassDistributionRotationCurveGradient=0.0d0
- ! Compute if a spheroid is present.
- if (radius > 0.0d0) then
- componentMass=hotHaloMassDistributionEnclosedMass(node,radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) then
- componentDensity = hotHaloMassDistribution_%density(node,radius)
- hotHaloMassDistributionRotationCurveGradient=+gravitationalConstantGalacticus &
- & *( &
- & -componentMass &
- & /radius **2 &
- & +4.0d0 &
- & *Pi &
- & *radius &
- & *componentDensity &
- & )
- end if
- end if
- return
- end function hotHaloMassDistributionRotationCurveGradient
-
- !![
-
- hotHaloMassDistributionDensity
-
- !!]
- double precision function hotHaloMassDistributionDensity(node,positionSpherical,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for a hot halo profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeHotHalo, massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionSpherical(3)
- !$GLC attributes unused :: weightIndex
-
- hotHaloMassDistributionDensity=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeHotHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
-
- hotHaloMassDistributionDensity=max(hotHaloMassDistribution_%density(node,positionSpherical(1)),0.0d0)
- return
- end function hotHaloMassDistributionDensity
-
- !![
-
- hotHaloMassDistributionDensitySphericalAverage
-
- !!]
- double precision function hotHaloMassDistributionDensitySphericalAverage(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the spherically-averaged density at a given radius for a hot halo profile.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeHotHalo, massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: weightIndex
-
- hotHaloMassDistributionDensitySphericalAverage=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeHotHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
-
- hotHaloMassDistributionDensitySphericalAverage=max(hotHaloMassDistribution_%density(node,radius),0.0d0)
- return
- end function hotHaloMassDistributionDensitySphericalAverage
-
- !![
-
- hotHaloMassDistributionDefaultStateStore
-
- !!]
- subroutine hotHaloMassDistributionDefaultStateStore(stateFile,gslStateFile,stateOperationID)
- !!{
- Store object state,
- !!}
- use :: Display , only : displayMessage, verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
- implicit none
- integer , intent(in ) :: stateFile
- integer(c_size_t), intent(in ) :: stateOperationID
- type (c_ptr ), intent(in ) :: gslStateFile
-
- call displayMessage('Storing state for: hot halo mass distribution',verbosity=verbosityLevelInfo)
- !![
-
- !!]
- return
- end subroutine hotHaloMassDistributionDefaultStateStore
-
- !![
-
- hotHaloMassDistributionDefaultStateRestore
-
- !!]
- subroutine hotHaloMassDistributionDefaultStateRestore(stateFile,gslStateFile,stateOperationID)
- !!{
- Retrieve object state.
- !!}
- use :: Display , only : displayMessage, verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
- implicit none
- integer , intent(in ) :: stateFile
- integer(c_size_t), intent(in ) :: stateOperationID
- type (c_ptr ), intent(in ) :: gslStateFile
-
- call displayMessage('Retrieving state for: hot halo mass distribution',verbosity=verbosityLevelInfo)
- !![
-
- !!]
- return
- end subroutine hotHaloMassDistributionDefaultStateRestore
-
end module Hot_Halo_Mass_Distributions
diff --git a/source/hot_halo.mass_distribution.PatejLoeb2015.F90 b/source/hot_halo.mass_distribution.PatejLoeb2015.F90
index 9384ac6587..87b3cc2fba 100644
--- a/source/hot_halo.mass_distribution.PatejLoeb2015.F90
+++ b/source/hot_halo.mass_distribution.PatejLoeb2015.F90
@@ -38,12 +38,8 @@
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
double precision :: gamma , radiusShock
contains
- final :: patejLoeb2015Destructor
- procedure :: density => patejLoeb2015Density
- procedure :: densityLogSlope => patejLoeb2015DensityLogSlope
- procedure :: enclosedMass => patejLoeb2015EnclosedMass
- procedure :: radialMoment => patejLoeb2015RadialMoment
- procedure :: rotationNormalization => patejLoeb2015RotationNormalization
+ final :: patejLoeb2015Destructor
+ procedure :: get => patejLoeb2015Get
end type hotHaloMassDistributionPatejLoeb2015
interface hotHaloMassDistributionPatejLoeb2015
@@ -165,193 +161,60 @@ subroutine patejLoeb2015Destructor(self)
return
end subroutine patejLoeb2015Destructor
- double precision function patejLoeb2015Density(self,node,radius)
+ function patejLoeb2015Get(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the density in a {\normalfont \ttfamily patejLoeb2015} hot halo mass distribution.
+ Return the \cite{patej_simple_2015} hot halo mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, nodeComponentHotHalo, treeNode
+ use :: Mass_Distributions , only : massDistributionPatejLoeb2015
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo, massTypeGaseous, weightByMass
implicit none
- class (hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterHaloProfile
- double precision :: radiusShock , densityNormalization, &
- & radiusOuter , radiusDarkMatter
-
- ! Get the hot halo and dark matter profile components.
- hotHalo => node%hotHalo ()
- darkMatterHaloProfile => node%darkMatterProfile()
- ! Find the shock and outer radii.
- radiusShock =+self %radiusShock &
- & *self %darkMatterHaloScale_%radiusVirial(node )
- radiusOuter =+hotHalo %outerRadius ( )
- ! Find the density normalization.
- radiusDarkMatter =+radiusShock &
- & *(radiusOuter/radiusShock)**self%gamma
- densityNormalization=+hotHalo %mass ( ) &
- & /self %darkMatterProfileDMO_ %enclosedMass(node,radiusDarkMatter)
- ! Compute the density.
- patejLoeb2015Density=+self%gamma &
- & *densityNormalization &
- & *( &
- & +radius &
- & /radiusShock &
- & )**(3.0d0*self%gamma-3.0d0) &
- & *self%darkMatterProfileDMO_%density( &
- & node , &
- & +radiusShock &
- & *( &
- & +radius &
- & /radiusShock &
- & )**self%gamma &
- & )
- return
- end function patejLoeb2015Density
-
- double precision function patejLoeb2015DensityLogSlope(self,node,radius)
- !!{
- Return the density in a {\normalfont \ttfamily patejLoeb2015} hot halo mass distribution.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, treeNode
- implicit none
- class (hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterHaloProfile
- double precision :: radiusShock
-
- ! Get the dark matter profile component.
- darkMatterHaloProfile => node%darkMatterProfile()
- ! Find the shock radius.
- radiusShock =+self %radiusShock &
- & *self%darkMatterHaloScale_%radiusVirial(node )
- ! Compute the log slope of density.
- patejLoeb2015DensityLogSlope=+3.0d0 &
- & *(self%gamma-1.0d0) &
- & + self%gamma &
- & * self%darkMatterProfileDMO_%densityLogSlope( &
- & node , &
- & +radiusShock &
- & *( &
- & +radius &
- & /radiusShock &
- & )**self%gamma &
- & )
- return
- end function patejLoeb2015DensityLogSlope
-
- double precision function patejLoeb2015EnclosedMass(self,node,radius)
- !!{
- Return the enclosed mass in a {\normalfont \ttfamily patejLoeb2015} hot halo mass distribution.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, nodeComponentHotHalo, treeNode
- implicit none
- class (hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterHaloProfile
- double precision :: radiusShock , densityNormalization, &
- & radiusOuter , radiusScale , &
- & radiusDarkMatter
-
- ! Get the hot halo and dark matter profile components.
- hotHalo => node%hotHalo ()
- darkMatterHaloProfile => node%darkMatterProfile()
- ! Find the shock, outer, and scale radii.
- radiusShock =+self %radiusShock &
- & *self%darkMatterHaloScale_%radiusVirial(node )
- radiusOuter = hotHalo %outerRadius ( )
- radiusScale = darkMatterHaloProfile%scale ( )
- ! Find the density normalization.
- radiusDarkMatter =+radiusShock &
- & *(radiusOuter/radiusShock)**self%gamma
- densityNormalization =+ hotHalo %mass ( ) &
- & /self%darkMatterProfileDMO_ %enclosedMass(node,radiusDarkMatter)
- ! Compute the corresponding radius in the dark matter halo.
- radiusDarkMatter =+radiusShock &
- & *(radius /radiusShock)**self%gamma
- ! Compute the enclosed mass (eqn. 4 of Patej & Loeb 2015).
- patejLoeb2015EnclosedMass=+densityNormalization &
- & *self%darkMatterProfileDMO_%enclosedMass( &
- & node , &
- & radiusDarkMatter &
- & )
- return
- end function patejLoeb2015EnclosedMass
-
- double precision function patejLoeb2015RadialMoment(self,node,moment,radius)
- !!{
- Compute a radial moment in a {\normalfont \ttfamily patejLoeb2015} hot halo mass distribution.
- For this profile we have:
- \begin{equation}
- \rho_\mathrm{g}(r) = f \Gamma (r/s)^{3 \Gamma - 3} \rho_\mathrm{DM}(s[r/s]^\Gamma).
- \end{equation}
- Defining $R=s[r/s]^\Gamma$, such that $r/s = (R/s)^{1/\Gamma}$, and $\mathrm{d}r = \Gamma^{-1} (R/s)^{1/\Gamma-1} \mathrm{d}R$, then
- \begin{equation}
- \int r^m \rho_\mathrm{g}(r) \mathrm{d}r = f s^{(m-2)(\Gamma-1)/\Gamma} \int R^{(2\Gamma-2+m)/\Gamma} \rho_\mathrm{DM}(R) \mathrm{d}R,
- \end{equation}
- or
- \begin{equation}
- \mathcal{R}_\mathrm{g}(r;m) = f s^{(m-2)(\Gamma-1)/\Gamma} \mathcal{R}_\mathrm{DM}(r;(2\Gamma-2+m)/\Gamma),
- \end{equation}
- where $\mathcal{R}(r;m)$ is the $m^\mathrm{th}$ radial moment of the density profile.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, nodeComponentHotHalo, treeNode
- implicit none
- class (hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment , radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterHaloProfile
- double precision :: radiusShock , densityNormalization, &
- & radiusOuter , radiusScale , &
- & radiusDarkMatter
-
- ! Get the hot halo and dark matter profile components.
- hotHalo => node%hotHalo ()
- darkMatterHaloProfile => node%darkMatterProfile()
- ! Find the shock, outer, and scale radii.
- radiusShock =+self %radiusShock &
- & *self%darkMatterHaloScale_%radiusVirial(node )
- radiusOuter = hotHalo %outerRadius ( )
- radiusScale = darkMatterHaloProfile%scale ( )
- ! Find the density normalization.
- radiusDarkMatter =+radiusShock &
- & *(radiusOuter/radiusShock)**self%gamma
- densityNormalization=+ hotHalo %mass ( ) &
- & /self%darkMatterProfileDMO_ %enclosedMass(node,radiusDarkMatter)
- ! Compute the corresponding radius in the dark matter halo.
- radiusDarkMatter=+( &
- & +radiusShock &
- & /radiusScale &
- & )**(1.0d0-self%gamma) &
- & *( &
- & +min(radius,hotHalo%outerRadius()) &
- & /radiusScale &
- & )** self%gamma
- ! Compute the radial moment.
- patejLoeb2015RadialMoment=+densityNormalization &
- & *radiusShock**((self%gamma-1.0d0)*(moment-2.0d0)/self%gamma) &
- & *self%darkMatterProfileDMO_%radialMoment(node,moment,radiusDarkMatter)
- return
- end function patejLoeb2015RadialMoment
-
- double precision function patejLoeb2015RotationNormalization(self,node)
- !!{
- Returns the relation between specific angular momentum and rotation velocity (assuming a
- rotation velocity that is constant in radius) for {\normalfont \ttfamily node}. Specifically, the
- normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class(hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloMassDistributionPatejLoeb2015), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistributionDMO
+ double precision :: radiusOuter , mass, &
+ & radiusShock
+ !![
+
+ !!]
- hotHalo => node%hotHalo ( )
- patejLoeb2015RotationNormalization = +self%radialMoment(node,2.0d0,hotHalo%outerRadius()) &
- & /self%radialMoment(node,3.0d0,hotHalo%outerRadius())
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get properties of the hot halo.
+ hotHalo => node %hotHalo ()
+ radiusOuter = hotHalo%outerRadius()
+ mass = hotHalo%mass ()
+ ! If outer radius is non-positive return a null profile.
+ if (radiusOuter <= 0.0d0 .or. mass <= 0.0d0) return
+ radiusShock = +self %radiusShock &
+ & *self%darkMatterHaloScale_ %radiusVirial(node)
+ massDistributionDMO => self%darkMatterProfileDMO_%get (node)
+ ! Create the mass distribution.
+ allocate(massDistributionPatejLoeb2015 :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionPatejLoeb2015)
+ !![
+
+
+ massDistributionPatejLoeb2015( &
+ & mass = mass , &
+ & gamma =self%gamma , &
+ & radiusShock = radiusShock , &
+ & radiusOuter = radiusOuter , &
+ & massDistribution_= massDistributionDMO , &
+ & componentType = componentTypeHotHalo, &
+ & massType = massTypeGaseous &
+ & )
+
+
+
+ !!]
+ end select
return
- end function patejLoeb2015RotationNormalization
+ end function patejLoeb2015Get
diff --git a/source/hot_halo.mass_distribution.Ricotti2000.F90 b/source/hot_halo.mass_distribution.Ricotti2000.F90
index 85925b3be0..a5957d475a 100644
--- a/source/hot_halo.mass_distribution.Ricotti2000.F90
+++ b/source/hot_halo.mass_distribution.Ricotti2000.F90
@@ -48,8 +48,8 @@ with parameters selected using the fitting function of \cite{ricotti_feedback_20
class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
contains
- final :: ricotti2000Destructor
- procedure :: initialize => ricotti2000Initialize
+ final :: ricotti2000Destructor
+ procedure :: get => ricotti2000Get
end type hotHaloMassDistributionRicotti2000
interface hotHaloMassDistributionRicotti2000
@@ -161,28 +161,40 @@ subroutine ricotti2000Destructor(self)
return
end subroutine ricotti2000Destructor
- subroutine ricotti2000Initialize(self,node)
+ function ricotti2000Get(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Initialize the {\normalfont \ttfamily ricotti2000} hot halo density profile for the given {\normalfont \ttfamily
- node}. Parameterizations of $\beta$ and core radius are taken from section 2.1 of \cite{ricotti_feedback_2000}.
+ Return the {\normalfont \ttfamily ricotti2000} hot halo mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile, nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo, nodeComponentDarkMatterProfile
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo, massTypeGaseous , weightByMass
implicit none
- class (hotHaloMassDistributionRicotti2000 ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentHotHalo ), pointer :: hotHalo
- class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
- double precision , parameter :: virialToGasTemperatureRatio=1.0d0
- double precision :: mass , radiusOuter , &
- & radiusScale , radiusVirial , &
- & radiusCore , concentration, &
- & b , beta
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloMassDistributionRicotti2000), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ double precision , parameter :: virialToGasTemperatureRatio=1.0d0
+ double precision :: mass , radiusOuter , &
+ & radiusScale , radiusVirial , &
+ & radiusCore , concentration, &
+ & b , beta
+ !![
+
+ !!]
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
! Compute parameters of the profile.
hotHalo => node %hotHalo ( )
darkMatterProfile => node %darkMatterProfile( )
radiusOuter = hotHalo %outerRadius ( )
mass = hotHalo %mass ( )
+ ! If outer radius is non-positive return a null profile.
+ if (radiusOuter <= 0.0d0 .or. mass <= 0.0d0) return
radiusScale = darkMatterProfile %scale ( )
radiusVirial = self %darkMatterHaloScale_%radiusVirial (node)
concentration = +radiusVirial &
@@ -211,11 +223,24 @@ subroutine ricotti2000Initialize(self,node)
mass=0.0d0
radiusOuter=1.0d0
end if
- self%distribution=massDistributionBetaProfile( &
- & beta =beta , &
- & coreRadius =radiusCore , &
- & mass =mass , &
- & outerRadius=radiusOuter &
- & )
+ allocate(massDistributionBetaProfile :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionBetaProfile)
+ !![
+
+
+ massDistributionBetaProfile( &
+ & beta =beta , &
+ & coreRadius =radiusCore , &
+ & mass =mass , &
+ & outerRadius =radiusOuter , &
+ & truncateAtOuterRadius=.true. , &
+ & componentType =componentTypeHotHalo, &
+ & massType =massTypeGaseous &
+ & )
+
+
+ !!]
+ end select
return
- end subroutine ricotti2000Initialize
+ end function ricotti2000Get
diff --git a/source/hot_halo.mass_distribution.beta_profile.F90 b/source/hot_halo.mass_distribution.beta_profile.F90
index 3f172ee3d6..3bda9487b3 100644
--- a/source/hot_halo.mass_distribution.beta_profile.F90
+++ b/source/hot_halo.mass_distribution.beta_profile.F90
@@ -45,22 +45,10 @@
!!}
private
double precision :: beta
- type (massDistributionBetaProfile ) :: distribution
class (hotHaloMassDistributionCoreRadiusClass), pointer :: hotHaloMassDistributionCoreRadius_ => null()
contains
- !![
-
-
-
- !!]
- final :: betaProfileDestructor
- procedure :: initialize => betaProfileInitialize
- procedure :: density => betaProfileDensity
- procedure :: densityLogSlope => betaProfileDensityLogSlope
- procedure :: enclosedMass => betaProfileEnclosedMass
- procedure :: radialMoment => betaProfileRadialMoment
- procedure :: densitySquaredIntegral => betaProfileDensitySquaredIntegral
- procedure :: rotationNormalization => betaProfileRotationNormalization
+ final :: betaProfileDestructor
+ procedure :: get => betaProfileGet
end type hotHaloMassDistributionBetaProfile
interface hotHaloMassDistributionBetaProfile
@@ -160,159 +148,55 @@ subroutine betaProfileDestructor(self)
return
end subroutine betaProfileDestructor
- subroutine betaProfileInitialize(self,node)
+ function betaProfileGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Initialize the $\beta$-profile hot halo density profile for the given {\normalfont \ttfamily node}.
+ Return the $\beta$-profile hot halo mass distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo, massTypeGaseous, weightByMass
implicit none
- class (hotHaloMassDistributionBetaProfile ), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentHotHalo ), pointer :: hotHalo
- double precision :: radiusScale, radiusOuter, &
- & mass
-
- radiusScale = self%hotHaloMassDistributionCoreRadius_%radius (node)
- hotHalo => node %hotHalo ( )
- radiusOuter = hotHalo %outerRadius( )
- mass = hotHalo %mass ( )
- if (radiusOuter <= 0.0d0) then
- ! If outer radius is non-positive, set mass to zero and outer radius to an arbitrary value.
- mass=0.0d0
- radiusOuter=1.0d0
- end if
- self%distribution=massDistributionBetaProfile( &
- & beta =self%beta , &
- & coreRadius =radiusScale, &
- & mass =mass , &
- & outerRadius=radiusOuter &
- & )
- return
- end subroutine betaProfileInitialize
-
- double precision function betaProfileDensity(self,node,radius)
- !!{
- Return the density in a single-betaProfile hot halo mass distribution.
- !!}
- use :: Coordinates, only : assignment(=), coordinateSpherical
- implicit none
- class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (coordinateSpherical ) :: position
-
- call self%initialize(node)
- position =[radius,0.0d0,0.0d0]
- betaProfileDensity=self%distribution%density(position)
- return
- end function betaProfileDensity
-
- double precision function betaProfileDensityLogSlope(self,node,radius)
- !!{
- Return the logarithmic slope of the density of the hot halo at the given {\normalfont \ttfamily radius}.
- !!}
- use :: Coordinates, only : assignment(=), coordinateSpherical
- implicit none
- class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- type (coordinateSpherical ) :: position
-
- call self%initialize(node)
- position =[radius,0.0d0,0.0d0]
- betaProfileDensityLogSlope=self%distribution%densityGradientRadial(position,logarithmic=.true.)
- return
- end function betaProfileDensityLogSlope
-
- double precision function betaProfileEnclosedMass(self,node,radius)
- !!{
- Return the mass enclosed in the hot halo at the given {\normalfont \ttfamily radius}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ) , pointer :: hotHalo
-
- hotHalo => node%hotHalo()
- if (radius > hotHalo%outerRadius()) then
- betaProfileEnclosedMass=hotHalo%mass()
- else
- call self%initialize(node)
- betaProfileEnclosedMass=self%distribution%massEnclosedBySphere(radius)
- end if
- return
- end function betaProfileEnclosedMass
-
- double precision function betaProfileRadialMoment(self,node,moment,radius)
- !!{
- Return the radial moment of the density profile of the hot halo to the given {\normalfont \ttfamily radius}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment , radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
-
- call self%initialize(node)
- hotHalo => node%hotHalo()
- betaProfileRadialMoment= &
- & self% &
- & distribution% &
- & densityRadialMoment( &
- & moment , &
- & radiusMinimum=0.0d0 , &
- & radiusMaximum=min( &
- & radius , &
- & hotHalo%outerRadius() &
- & ) &
- & )
- return
- end function betaProfileRadialMoment
-
- double precision function betaProfileDensitySquaredIntegral(self,node,radius)
- !!{
- Return the integral of the square of the density profile of the hot halo to the given {\normalfont \ttfamily radius}.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ), pointer :: hotHalo
-
- call self%initialize(node)
- hotHalo => node%hotHalo()
- betaProfileDensitySquaredIntegral= &
- & self% &
- & distribution% &
- & densitySquareIntegral( &
- & radiusMinimum=0.0d0 , &
- & radiusMaximum=min( &
- & radius , &
- & hotHalo%outerRadius() &
- & ) &
- & )
- return
- end function betaProfileDensitySquaredIntegral
-
- double precision function betaProfileRotationNormalization(self,node)
- !!{
- Returns the relation between specific angular momentum and rotation velocity (assuming a
- rotation velocity that is constant in radius) for {\normalfont \ttfamily node}. Specifically, the
- normalization, $A$, returned is such that $V_\mathrm{rot} = A J/M$.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentHotHalo, treeNode
- implicit none
- class(hotHaloMassDistributionBetaProfile), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class(nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloMassDistributionBetaProfile), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ double precision :: radiusScale , radiusOuter, &
+ & mass
+ !![
+
+ !!]
- hotHalo => node%hotHalo()
- betaProfileRotationNormalization= &
- & self%radialMoment(node,2.0d0,hotHalo%outerRadius()) &
- & /self%radialMoment(node,3.0d0,hotHalo%outerRadius())
+ ! Assume a null distribution by default.
+ massDistribution_ => null()
+ ! If weighting is not by mass, return a null profile.
+ if (weightBy_ /= weightByMass) return
+ ! Get properties of the hot halo.
+ radiusScale = self %hotHaloMassDistributionCoreRadius_%radius (node)
+ hotHalo => node %hotHalo ( )
+ radiusOuter = hotHalo %outerRadius( )
+ mass = hotHalo %mass ( )
+ ! If outer radius is non-positive return a null profile.
+ if (radiusOuter <= 0.0d0 .or. mass <= 0.0d0) return
+ ! Create the beta-profile distribution.
+ allocate(massDistributionBetaProfile :: massDistribution_)
+ select type(massDistribution_)
+ type is (massDistributionBetaProfile)
+ !![
+
+
+ massDistributionBetaProfile( &
+ & beta =self%beta , &
+ & coreRadius = radiusScale , &
+ & mass = mass , &
+ & outerRadius = radiusOuter , &
+ & truncateAtOuterRadius= .true. , &
+ & componentType = componentTypeHotHalo, &
+ & massType = massTypeGaseous &
+ & )
+
+
+ !!]
+ end select
return
- end function betaProfileRotationNormalization
+ end function betaProfileGet
diff --git a/source/hot_halo.mass_distribution.null.F90 b/source/hot_halo.mass_distribution.null.F90
index de915f85a5..fbafd14b59 100644
--- a/source/hot_halo.mass_distribution.null.F90
+++ b/source/hot_halo.mass_distribution.null.F90
@@ -35,11 +35,7 @@
!!}
private
contains
- procedure :: density => nullDensity
- procedure :: densityLogSlope => nullDensityLogSlope
- procedure :: enclosedMass => nullEnclosedMass
- procedure :: radialMoment => nullRadialMoment
- procedure :: rotationNormalization => nullRotationNormalization
+ procedure :: get => nullGet
end type hotHaloMassDistributionNull
interface hotHaloMassDistributionNull
@@ -67,74 +63,17 @@ function nullConstructorParameters(parameters) result(self)
return
end function nullConstructorParameters
- double precision function nullDensity(self,node,radius)
+ function nullGet(self,node,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the density in a null hot halo mass distribution.
+ Return a null hot halo mass distribution.
!!}
implicit none
- class (hotHaloMassDistributionNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (hotHaloMassDistributionNull), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
- nullDensity=0.0d0
+ massDistribution_ => null()
return
- end function nullDensity
-
- double precision function nullDensityLogSlope(self,node,radius)
- !!{
- Return the logarithmic slope of the density of the hot halo at the given {\normalfont \ttfamily radius}.
- !!}
- implicit none
- class (hotHaloMassDistributionNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- nullDensityLogSlope=0.0d0
- return
- end function nullDensityLogSlope
-
- double precision function nullEnclosedMass(self,node,radius)
- !!{
- Return the mass enclosed in the hot halo at the given {\normalfont \ttfamily radius}.
- !!}
- implicit none
- class (hotHaloMassDistributionNull), intent(inout) :: self
- type (treeNode ), intent(inout), target :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- nullEnclosedMass=0.0d0
- return
- end function nullEnclosedMass
-
- double precision function nullRadialMoment(self,node,moment,radius)
- !!{
- Return the density of the hot halo at the given {\normalfont \ttfamily radius}.
- !!}
- implicit none
- class (hotHaloMassDistributionNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: moment, radius
- !$GLC attributes unused :: self, node, radius, moment
-
- nullRadialMoment=0.0d0
- return
- end function nullRadialMoment
-
- double precision function nullRotationNormalization(self,node)
- !!{
- Returns the relation between specific angular momentum and rotation velocity (assuming a rotation velocity that is constant
- in radius) for {\normalfont \ttfamily node}. Specifically, the normalization, $A$, returned is such that $V_\mathrm{rot} =
- A J/M$.
- !!}
- implicit none
- class(hotHaloMassDistributionNull), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- !$GLC attributes unused :: self, node
-
- nullRotationNormalization=0.0d0
- return
- end function nullRotationNormalization
-
+ end function nullGet
diff --git a/source/hot_halo.outflow_reincorporation.velocity_maximum_scaling.F90 b/source/hot_halo.outflow_reincorporation.velocity_maximum_scaling.F90
index 970838c9d1..6254c583ca 100644
--- a/source/hot_halo.outflow_reincorporation.velocity_maximum_scaling.F90
+++ b/source/hot_halo.outflow_reincorporation.velocity_maximum_scaling.F90
@@ -229,12 +229,14 @@ double precision function velocityMaximumScalingRate(self,node)
!!{
Return the rate of mass reincorporation for outflowed gas in the hot halo.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentHotHalo, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, treeNode
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class (hotHaloOutflowReincorporationVelocityMaximumScaling), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: timeScale
! Check if node differs from previous one for which we performed calculations.
@@ -242,13 +244,17 @@ double precision function velocityMaximumScalingRate(self,node)
! Get required components.
! Compute velocity maximum factor.
if (.not.self%velocityMaximumComputed) then
- self%velocityMaximumFactor = self%velocityExponentiator %exponentiate(self%darkMatterProfileDMO_ %circularVelocityMaximum(node ))
+ massDistribution_ => self%darkMatterProfileDMO_ %get (node )
+ self%velocityMaximumFactor = self%velocityExponentiator %exponentiate(massDistribution_%velocityRotationCurveMaximum ( ))
self%velocityMaximumComputed = .true.
+ !![
+
+ !!]
end if
! Compute expansion factor factor.
if (.not.self%expansionFactorComputed) then
- basic => node %basic ( )
- self%expansionFactorFactor = 1.0d0/self%expansionFactorExponentiator%exponentiate(self%cosmologyFunctions_ %expansionFactor (basic%time()))
+ basic => node %basic ( )
+ self%expansionFactorFactor = 1.0d0/self%expansionFactorExponentiator%exponentiate(self %cosmologyFunctions_ %expansionFactor(basic%time()))
self%expansionFactorComputed = .true.
end if
! Compute the rate.
diff --git a/source/hot_halo.ram_pressure_force.Font2008.F90 b/source/hot_halo.ram_pressure_force.Font2008.F90
index 085a82214b..6f04f5aa1c 100644
--- a/source/hot_halo.ram_pressure_force.Font2008.F90
+++ b/source/hot_halo.ram_pressure_force.Font2008.F90
@@ -21,8 +21,7 @@
Implements a model of ram pressure stripping of hot halos based on the methods of \cite{font_colours_2008}.
!!}
- use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
- use :: Galactic_Structure , only : galacticStructureClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -42,8 +41,7 @@
Implementation of a hot halo ram pressure force class which follows the model of \cite{font_colours_2008}.
!!}
private
- class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
final :: font2008Destructor
procedure :: force => font2008Force
@@ -67,32 +65,28 @@ function font2008ConstructorParameters(parameters) result(self)
implicit none
type (hotHaloRamPressureForceFont2008) :: self
type (inputParameters ), intent(inout) :: parameters
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(galacticStructureClass ), pointer :: galacticStructure_
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
!![
-
-
+
!!]
- self=hotHaloRamPressureForceFont2008(hotHaloMassDistribution_,galacticStructure_)
+ self=hotHaloRamPressureForceFont2008(darkMatterHaloScale_)
!![
-
-
+
!!]
return
end function font2008ConstructorParameters
- function font2008ConstructorInternal(hotHaloMassDistribution_,galacticStructure_) result(self)
+ function font2008ConstructorInternal(darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily font2008} hot halo ram pressure force class.
!!}
implicit none
type (hotHaloRamPressureForceFont2008) :: self
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
return
@@ -106,8 +100,7 @@ subroutine font2008Destructor(self)
type(hotHaloRamPressureForceFont2008), intent(inout) :: self
!![
-
-
+
!!]
return
end subroutine font2008Destructor
@@ -116,16 +109,21 @@ double precision function font2008Force(self,node)
!!{
Return a ram pressure force due to the hot halo using the model of \cite{font_colours_2008}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentSatellite , treeNode
- use :: Kepler_Orbits , only : keplerOrbit
- use :: Satellite_Orbits, only : Satellite_Orbit_Extremum_Phase_Space_Coordinates, extremumPericenter
+ use :: Galacticus_Nodes , only : nodeComponentSatellite , treeNode
+ use :: Kepler_Orbits , only : keplerOrbit
+ use :: Satellite_Orbits , only : Satellite_Orbit_Extremum_Phase_Space_Coordinates, extremumPericenter
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
class (hotHaloRamPressureForceFont2008), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentSatellite ), pointer :: satellite
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
type (keplerOrbit ) :: orbit
- double precision :: radiusOrbital, velocityOrbital
+ double precision :: radiusOrbital , velocityOrbital
! Find the host node.
nodeHost => node %parent
@@ -134,9 +132,14 @@ double precision function font2008Force(self,node)
! Get the orbit for this node.
orbit = satellite%virialOrbit()
! Get the orbital radius and velocity at pericenter.
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%darkMatterHaloScale_)
! Find the ram pressure force at pericenter.
- font2008Force=+self%hotHaloMassDistribution_%density(nodeHost,radiusOrbital) &
- & *velocityOrbital**2
+ coordinates = [radiusOrbital,0.0d0,0.0d0]
+ massDistribution_ => nodeHost %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ font2008Force = +massDistribution_%density (coordinates ) &
+ & *velocityOrbital **2
+ !![
+
+ !!]
return
end function font2008Force
diff --git a/source/hot_halo.ram_pressure_force.orbital_position.F90 b/source/hot_halo.ram_pressure_force.orbital_position.F90
index d372411a04..ac1e1c78d5 100644
--- a/source/hot_halo.ram_pressure_force.orbital_position.F90
+++ b/source/hot_halo.ram_pressure_force.orbital_position.F90
@@ -21,9 +21,6 @@
Implements a model of the ram pressure stripping force from hot halos based on orbital position within the host halo.
!!}
- use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
- use :: Galactic_Structure , only : galacticStructureClass
-
!![
@@ -42,10 +39,7 @@
Implementation of a hot halo ram pressure force class based on orbital position within the host halo.
!!}
private
- class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
- final :: orbitalPositionDestructor
procedure :: force => orbitalPositionForce
end type hotHaloRamPressureForceOrbitalPosition
@@ -65,25 +59,17 @@ function orbitalPositionConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (hotHaloRamPressureForceOrbitalPosition) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(galacticStructureClass ), pointer :: galacticStructure_
+ type(hotHaloRamPressureForceOrbitalPosition) :: self
+ type(inputParameters ), intent(inout) :: parameters
- !![
-
-
- !!]
- self=hotHaloRamPressureForceOrbitalPosition(hotHaloMassDistribution_,galacticStructure_)
+ self=hotHaloRamPressureForceOrbitalPosition()
!![
-
-
!!]
return
end function orbitalPositionConstructorParameters
- function orbitalPositionConstructorInternal(hotHaloMassDistribution_,galacticStructure_) result(self)
+ function orbitalPositionConstructorInternal() result(self)
!!{
Internal constructor for the {\normalfont \ttfamily orbitalPosition} hot halo ram pressure force class.
!!}
@@ -91,12 +77,7 @@ function orbitalPositionConstructorInternal(hotHaloMassDistribution_,galacticStr
use :: Error , only : Error_Report , Component_List
use :: Galacticus_Nodes, only : defaultSatelliteComponent
implicit none
- type (hotHaloRamPressureForceOrbitalPosition) :: self
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
+ type (hotHaloRamPressureForceOrbitalPosition) :: self
! Ensure that required methods are supported.
if ( &
@@ -119,31 +100,22 @@ function orbitalPositionConstructorInternal(hotHaloMassDistribution_,galacticStr
return
end function orbitalPositionConstructorInternal
- subroutine orbitalPositionDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily orbitalPosition} hot halo ram pressure force class.
- !!}
- implicit none
- type(hotHaloRamPressureForceOrbitalPosition), intent(inout) :: self
-
- !![
-
-
- !!]
- return
- end subroutine orbitalPositionDestructor
-
double precision function orbitalPositionForce(self,node)
!!{
Return a ram pressure force due to the hot halo based on orbital position within the host halo.
!!}
- use :: Galacticus_Nodes, only : nodeComponentSatellite
- use :: Vectors , only : Vector_Magnitude
+ use :: Galacticus_Nodes , only : nodeComponentSatellite
+ use :: Vectors , only : Vector_Magnitude
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
class (hotHaloRamPressureForceOrbitalPosition), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentSatellite ), pointer :: satellite
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
double precision :: radiusOrbital, velocityOrbital
! Find the host node.
@@ -153,8 +125,13 @@ double precision function orbitalPositionForce(self,node)
! Compute orbital position and velocity.
radiusOrbital = +Vector_Magnitude(satellite%position())
velocityOrbital = +Vector_Magnitude(satellite%velocity())
- ! Find the ram pressure force at this orbital radius.
- orbitalPositionForce = +self%hotHaloMassDistribution_%density (nodeHost,radiusOrbital) &
- & * velocityOrbital **2
+ ! Find the ram pressure force this orbital radius.
+ coordinates = [radiusOrbital,0.0d0,0.0d0]
+ massDistribution_ => nodeHost %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ orbitalPositionForce = +massDistribution_%density (coordinates ) &
+ & *velocityOrbital **2
+ !![
+
+ !!]
return
end function orbitalPositionForce
diff --git a/source/hot_halo.ram_pressure_force.relative_position.F90 b/source/hot_halo.ram_pressure_force.relative_position.F90
index e54fbaf8d6..6888a6006c 100644
--- a/source/hot_halo.ram_pressure_force.relative_position.F90
+++ b/source/hot_halo.ram_pressure_force.relative_position.F90
@@ -22,7 +22,6 @@
!!}
use :: Hot_Halo_Mass_Distributions, only : hotHaloMassDistributionClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -43,7 +42,6 @@
!!}
private
class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: relativePositionDestructor
procedure :: force => relativePositionForce
@@ -68,22 +66,19 @@ function relativePositionConstructorParameters(parameters) result(self)
type (hotHaloRamPressureForceRelativePosition) :: self
type (inputParameters ), intent(inout) :: parameters
class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=hotHaloRamPressureForceRelativePosition(hotHaloMassDistribution_,galacticStructure_)
+ self=hotHaloRamPressureForceRelativePosition(hotHaloMassDistribution_)
!![
-
!!]
return
end function relativePositionConstructorParameters
- function relativePositionConstructorInternal(hotHaloMassDistribution_,galacticStructure_) result(self)
+ function relativePositionConstructorInternal(hotHaloMassDistribution_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily relativePosition} hot halo ram pressure force class.
!!}
@@ -93,9 +88,8 @@ function relativePositionConstructorInternal(hotHaloMassDistribution_,galacticSt
implicit none
type (hotHaloRamPressureForceRelativePosition) :: self
class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
! Ensure that required methods are supported.
@@ -128,7 +122,6 @@ subroutine relativePositionDestructor(self)
!![
-
!!]
return
end subroutine relativePositionDestructor
@@ -137,17 +130,22 @@ double precision function relativePositionForce(self,node) result(force)
!!{
Return a ram pressure force due to the hot halo based on orbital position within the host halo.
!!}
- use :: Galacticus_Nodes, only : nodeComponentPosition, nodeComponentBasic
- use :: Vectors , only : Vector_Magnitude
+ use :: Galacticus_Nodes , only : nodeComponentPosition, nodeComponentBasic
+ use :: Vectors , only : Vector_Magnitude
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
implicit none
class (hotHaloRamPressureForceRelativePosition), intent(inout) :: self
type (treeNode ), intent(inout) :: node
- class (nodeComponentPosition ), pointer :: position , positionHost
- class (nodeComponentBasic ), pointer :: basic , basicPrevious , &
- & basicCurrent , basicHost
- type (treeNode ), pointer :: nodeHost , nodeHostPrevious, &
+ class (nodeComponentPosition ), pointer :: position , positionHost
+ class (nodeComponentBasic ), pointer :: basic , basicPrevious , &
+ & basicCurrent , basicHost
+ type (treeNode ), pointer :: nodeHost , nodeHostPrevious, &
& nodeHostCurrent
- double precision :: radiusRelative , velocityRelative
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: radiusRelative , velocityRelative
! Find the host node. Seek the descendant of the node closest in time to our satellite node. This is necessary as satellites
! can evolve ahead of their hosts.
@@ -183,8 +181,13 @@ double precision function relativePositionForce(self,node) result(force)
! Compute orbital position and velocity.
radiusRelative = +Vector_Magnitude(position%position()-positionHost%position())
velocityRelative = +Vector_Magnitude(position%velocity()-positionHost%velocity())
- ! Find the ram pressure force at this orbital radius.
- force = +self%hotHaloMassDistribution_%density (nodeHost,radiusRelative) &
- & * velocityRelative **2
+ ! Find the ram pressure force this orbital radius.
+ coordinates = [radiusRelative,0.0d0,0.0d0]
+ massDistribution_ => nodeHost %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ force = +massDistribution_%density (coordinates ) &
+ & *velocityRelative **2
+ !![
+
+ !!]
return
end function relativePositionForce
diff --git a/source/hot_halo.ram_pressure_stripping.Font2008.F90 b/source/hot_halo.ram_pressure_stripping.Font2008.F90
index f360ca90f9..4085d564ff 100644
--- a/source/hot_halo.ram_pressure_stripping.Font2008.F90
+++ b/source/hot_halo.ram_pressure_stripping.Font2008.F90
@@ -22,12 +22,11 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Ram_Pressure_Forces, only : hotHaloRamPressureForce , hotHaloRamPressureForceClass
- use :: Galactic_Structure , only : galacticStructureClass
+ use :: Hot_Halo_Ram_Pressure_Forces, only : hotHaloRamPressureForce , hotHaloRamPressureForceClass
use :: Kind_Numbers , only : kind_int8
use :: Root_Finder , only : rootFinder
-
+ use :: Mass_Distributions , only : massDistributionClass
+
!![
@@ -51,8 +50,6 @@
private
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (hotHaloRamPressureForceClass), pointer :: hotHaloRamPressureForce_ => null()
- class (hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: formFactor
logical :: solverFailureIsFatal
integer (kind_int8 ) :: uniqueIDLast = -1
@@ -74,8 +71,9 @@
! Global variables used in root finding.
class (hotHaloRamPressureStrippingFont2008), pointer :: self_
type (treeNode ), pointer :: node_
+ class (massDistributionClass ), pointer :: massDistribution__, massDistributionGas__
double precision :: forceRamPressure
- !$omp threadprivate(self_,node_,forceRamPressure)
+ !$omp threadprivate(self_,node_,forceRamPressure,massDistribution__, massDistributionGas__)
contains
@@ -89,8 +87,6 @@ function font2008ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (hotHaloRamPressureForceClass ), pointer :: hotHaloRamPressureForce_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: formFactor
logical :: solverFailureIsFatal
@@ -110,21 +106,17 @@ function font2008ConstructorParameters(parameters) result(self)
-
-
!!]
- self=hotHaloRamPressureStrippingFont2008(formFactor,solverFailureIsFatal,darkMatterHaloScale_,hotHaloRamPressureForce_,hotHaloMassDistribution_,galacticStructure_)
+ self=hotHaloRamPressureStrippingFont2008(formFactor,solverFailureIsFatal,darkMatterHaloScale_,hotHaloRamPressureForce_)
!![
-
-
!!]
return
end function font2008ConstructorParameters
- function font2008ConstructorInternal(formFactor,solverFailureIsFatal,darkMatterHaloScale_,hotHaloRamPressureForce_,hotHaloMassDistribution_,galacticStructure_) result(self)
+ function font2008ConstructorInternal(formFactor,solverFailureIsFatal,darkMatterHaloScale_,hotHaloRamPressureForce_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily font2008} hot halo ram pressure stripping class.
!!}
@@ -132,13 +124,11 @@ function font2008ConstructorInternal(formFactor,solverFailureIsFatal,darkMatterH
type (hotHaloRamPressureStrippingFont2008) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (hotHaloRamPressureForceClass ), intent(in ), target :: hotHaloRamPressureForce_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: formFactor
logical , intent(in ) :: solverFailureIsFatal
double precision , parameter :: toleranceAbsolute =0.0d+0, toleranceRelative=1.0d-3
!![
-
+
!!]
! Solver for the ram pressure stripping radius.
@@ -160,8 +150,6 @@ subroutine font2008Destructor(self)
!![
-
-
!!]
return
end subroutine font2008Destructor
@@ -170,11 +158,12 @@ double precision function font2008RadiusStripped(self,node)
!!{
Return the ram pressure stripping radius due to the hot halo using the model of \cite{font_colours_2008}.
!!}
- use :: Display , only : displayMessage , verbosityLevelSilent
- use :: Error , only : Error_Report , errorStatusSuccess , GSL_Error_Details
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
- use :: String_Handling , only : operator(//)
- use :: Functions_Global, only : State_Retrieve_ , State_Store_ , mergerTreeStateStore_ , State_Set_
+ use :: Display , only : displayMessage , verbosityLevelSilent
+ use :: Error , only : Error_Report , errorStatusSuccess , GSL_Error_Details
+ use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
+ use :: String_Handling , only : operator(//)
+ use :: Functions_Global , only : State_Retrieve_ , State_Store_ , mergerTreeStateStore_ , State_Set_
+ use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll , componentTypeHotHalo , massTypeGaseous
implicit none
class (hotHaloRamPressureStrippingFont2008), intent(inout), target :: self
@@ -194,10 +183,13 @@ double precision function font2008RadiusStripped(self,node)
! Test whether node is a satellite.
if (node%isSatellite().and.node%isPhysicallyPlausible) then
! Set a pointer to the satellite node.
- self_ => self
- node_ => node
+ self_ => self
+ node_ => node
+ ! Get the hot halo mass distributions.
+ massDistribution__ => node%massDistribution(componentTypeAll ,massTypeAll )
+ massDistributionGas__ => node%massDistribution(componentTypeHotHalo,massTypeGaseous)
! Get the ram pressure force due to the hot halo.
- forceRamPressure = self%hotHaloRamPressureForce_%force(node)
+ forceRamPressure = self%hotHaloRamPressureForce_%force(node)
! Find the radial range within which the ram pressure radius must lie.
radiusVirialRoot=font2008RadiusSolver(radiusVirial)
if (radiusVirialRoot >= 0.0d0) then
@@ -288,6 +280,10 @@ double precision function font2008RadiusStripped(self,node)
self%radiusLast=font2008RadiusStripped
end if
end if
+ !![
+
+
+ !!]
else
! If node is not a satellite, or is not physically plausible, return a ram pressure stripping radius equal to the virial radius.
font2008RadiusStripped=radiusVirial
@@ -299,21 +295,28 @@ double precision function font2008RadiusSolver(radius)
!!{
Root function used in finding the ram pressure stripping radius.
!!}
- use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
- double precision, intent(in ) :: radius
- double precision :: massEnclosed , forceBindingGravitational, &
- & densityHotHalo
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+ double precision :: massEnclosed , forceBindingGravitational, &
+ & densityHotHalo
! Get the hot halo mass distribution.
- massEnclosed =+self_%galacticStructure_ %massEnclosed(node_,radius,massType=massTypeAll,componentType=componentTypeAll)
- densityHotHalo =+self_%hotHaloMassDistribution_%density (node_,radius )
- forceBindingGravitational=+self_%formFactor &
- & *gravitationalConstantGalacticus &
- & *massEnclosed &
- & *densityHotHalo &
- & /radius
+ coordinates = [radius,0.0d0,0.0d0]
+ densityHotHalo = massDistributionGas__%density(coordinates)
+ if (densityHotHalo > 0.0d0) then
+ massEnclosed =+massDistribution__%massEnclosedBySphere(radius)
+ forceBindingGravitational=+self_%formFactor &
+ & *gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & *densityHotHalo &
+ & /radius
+ else
+ forceBindingGravitational=0.0d0
+ end if
if (forceBindingGravitational >= 0.0d0) then
font2008RadiusSolver=forceBindingGravitational-forceRamPressure
else
diff --git a/source/hot_halo.ram_pressure_stripping.timescale.ram_pressure_acceleration.F90 b/source/hot_halo.ram_pressure_stripping.timescale.ram_pressure_acceleration.F90
index dba81735c7..7237922c8e 100644
--- a/source/hot_halo.ram_pressure_stripping.timescale.ram_pressure_acceleration.F90
+++ b/source/hot_halo.ram_pressure_stripping.timescale.ram_pressure_acceleration.F90
@@ -23,7 +23,6 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
use :: Hot_Halo_Ram_Pressure_Forces, only : hotHaloRamPressureForceClass
!![
@@ -49,8 +48,7 @@
acceleration.
!!}
private
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(hotHaloMassDistributionClass), pointer :: hotHaloMassDistribution_ => null()
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class(hotHaloRamPressureForceClass), pointer :: hotHaloRamPressureForce_ => null()
contains
final :: ramPressureAccelerationDestructor
@@ -76,35 +74,31 @@ function ramPressureAccelerationConstructorParameters(parameters) result(self)
type (hotHaloRamPressureTimescaleRamPressureAcceleration) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
class(hotHaloRamPressureForceClass ), pointer :: hotHaloRamPressureForce_
!![
-
!!]
- self=hotHaloRamPressureTimescaleRamPressureAcceleration(darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloRamPressureForce_)
+ self=hotHaloRamPressureTimescaleRamPressureAcceleration(darkMatterHaloScale_,hotHaloRamPressureForce_)
!![
-
!!]
return
end function ramPressureAccelerationConstructorParameters
- function ramPressureAccelerationConstructorInternal(darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloRamPressureForce_) result(self)
+ function ramPressureAccelerationConstructorInternal(darkMatterHaloScale_,hotHaloRamPressureForce_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily ramPressureAcceleration} hot halo ram pressure timescale class.
!!}
implicit none
type (hotHaloRamPressureTimescaleRamPressureAcceleration) :: self
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
class(hotHaloRamPressureForceClass ), intent(in ), target :: hotHaloRamPressureForce_
!![
-
+
!!]
return
@@ -119,7 +113,6 @@ subroutine ramPressureAccelerationDestructor(self)
!![
-
!!]
return
@@ -133,9 +126,12 @@ double precision function ramPressureAccelerationTimescale(self,node)
that radius, and $P_\mathrm{ram}$ is the ram pressure force (per unit area). The surface density is approximated as
$\Sigma_\mathrm{outer} \approx r_\mathrm{outer} \rho_\mathrm{outer}$, where $\rho_\mathrm{outer}$ is the density at the outer radius.
!!}
- use :: Galacticus_Nodes , only : nodeComponentHotHalo, treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
+ use :: Galacticus_Nodes , only : nodeComponentHotHalo , treeNode
+ use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class (hotHaloRamPressureTimescaleRamPressureAcceleration), intent(inout) :: self
type (treeNode ), intent(inout) :: node
@@ -143,16 +139,23 @@ double precision function ramPressureAccelerationTimescale(self,node)
type (treeNode ), pointer :: nodeHost
double precision , parameter :: timescaleInfinite =huge(1.0d0)
double precision , parameter :: velocityStrippingMaximum= 1.0d1
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
double precision :: radiusOuter , densityOuter , &
& forceRamPressure , surfaceDensityOuter
! Evaluate surface density and ram pressure force.
- hotHalo => node %hotHalo ( )
- radiusOuter = hotHalo %outerRadius( )
- densityOuter = self %hotHaloMassDistribution_%density (node,radiusOuter)
- forceRamPressure = self %hotHaloRamPressureForce_%force (node )
+ massDistribution_ => node %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ hotHalo => node %hotHalo ( )
+ radiusOuter = hotHalo %outerRadius ( )
+ coordinates = [radiusOuter,0.0d0,0.0d0]
+ densityOuter = massDistribution_ %density (coordinates )
+ forceRamPressure = self %hotHaloRamPressureForce_%force (node )
surfaceDensityOuter = +radiusOuter &
& *densityOuter
+ !![
+
+ !!]
! Exit with infinite timescale for zero ram pressure force.
if (forceRamPressure <= 0.0d0) then
ramPressureAccelerationTimescale=timescaleInfinite
diff --git a/source/hot_halo.temperature_profile.Enzo_hydrostatic.F90 b/source/hot_halo.temperature_profile.Enzo_hydrostatic.F90
index e8440bbfc8..0d46a7f54d 100644
--- a/source/hot_halo.temperature_profile.Enzo_hydrostatic.F90
+++ b/source/hot_halo.temperature_profile.Enzo_hydrostatic.F90
@@ -26,13 +26,13 @@
!![
- A hot halo temperature profile class wjocj implements the ``hydrostatic'' temperature profile available in the \gls{enzo}
+ A hot halo temperature profile class that implements the ``hydrostatic'' temperature profile available in the \gls{enzo}
code. Specifically,
\begin{equation}
T(r) = \hbox{max}\left( {\mathrm{G} M(<r) \mu m_\mathrm{H} \over 3 \mathrm{k_B} r} , T_\mathrm{min} \right),
\end{equation}
where $M(<r)$ is the total mass enclosed within radius $r$, $\mu$ is the primordial mean atomic mass, and
- $T_\mathrm{min}=100$~K is a temperature floor introduced so as to avoid the temperature reaching arbitrarily low masses.
+ $T_\mathrm{min}=100$~K is a temperature floor introduced so as to avoid the temperature reaching arbitrarily low values.
!!]
@@ -43,9 +43,8 @@
private
class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
contains
- final :: enzoHydrostaticDestructor
- procedure :: temperature => enzoHydrostaticTemperature
- procedure :: temperatureLogSlope => enzoHydrostaticTemperatureLogSlope
+ final :: enzoHydrostaticDestructor
+ procedure :: get => enzoHydrostaticGet
end type hotHaloTemperatureProfileEnzoHydrostatic
interface hotHaloTemperatureProfileEnzoHydrostatic
@@ -108,72 +107,30 @@ subroutine enzoHydrostaticDestructor(self)
return
end subroutine enzoHydrostaticDestructor
- double precision function enzoHydrostaticTemperature(self,node,radius)
+ function enzoHydrostaticGet(self,node) result(kinematicsDistribution_)
!!{
- Return the density in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass distribution.
+ Return the virial hot halo temperature distribution for the given {\normalfont \ttfamily node}.
!!}
- use :: Numerical_Constants_Astronomical, only : meanAtomicMassPrimordial, gravitationalConstantGalacticus
- use :: Numerical_Constants_Atomic , only : massHydrogenAtom
- use :: Numerical_Constants_Physical , only : boltzmannsConstant
- use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions, only : massDistributionClass, kinematicsDistributionEnzoHydrostatic
implicit none
- class (hotHaloTemperatureProfileEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: enclosedMass
-
- if (radius == 0.0d0) then
- enzoHydrostaticTemperature=temperatureMinimum
- else
- enclosedMass =self%darkMatterProfileDMO_%enclosedMass( &
- & node , &
- & radius &
- & )
- enzoHydrostaticTemperature=max( &
- & +kilo **2 &
- & *gravitationalConstantGalacticus &
- & *enclosedMass &
- & *meanAtomicMassPrimordial &
- & *massHydrogenAtom &
- & /3.0d0 &
- & /boltzmannsConstant &
- & /radius , &
- & temperatureMinimum &
- & )
- end if
- return
- end function enzoHydrostaticTemperature
-
- double precision function enzoHydrostaticTemperatureLogSlope(self,node,radius)
- !!{
- Return the logarithmic slope of the density profile in a {\normalfont \ttfamily enzoHydrostatic} hot halo mass
- distribution.
- !!}
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (hotHaloTemperatureProfileEnzoHydrostatic), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: enclosedMass, density
-
- if (self%temperature(node,radius) <= temperatureMinimum) then
- enzoHydrostaticTemperatureLogSlope=0.0d0
- else
- enclosedMass = self%darkMatterProfileDMO_%enclosedMass( &
- & node , &
- & radius &
- & )
- density = self%darkMatterProfileDMO_%density ( &
- & node , &
- & radius &
- & )
- enzoHydrostaticTemperatureLogSlope=+4.0d0 &
- & *Pi &
- & *radius **3 &
- & *density &
- & /enclosedMass &
- & -1.0d0
- end if
+ class(kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ class(hotHaloTemperatureProfileEnzoHydrostatic), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+ class(massDistributionClass ), pointer :: massDistribution_
+
+ ! Create an isothermal kinematics distribution.
+ allocate(kinematicsDistributionEnzoHydrostatic :: kinematicsDistribution_)
+ select type(kinematicsDistribution_)
+ type is (kinematicsDistributionEnzoHydrostatic)
+ massDistribution_ => self%darkMatterProfileDMO_%get(node)
+ !![
+
+
+ kinematicsDistributionEnzoHydrostatic(massDistribution_=massDistribution_)
+
+
+
+ !!]
+ end select
return
- end function enzoHydrostaticTemperatureLogSlope
-
+ end function enzoHydrostaticGet
diff --git a/source/hot_halo.temperature_profile.F90 b/source/hot_halo.temperature_profile.F90
index e6c4c517ac..6e5108fe9b 100644
--- a/source/hot_halo.temperature_profile.F90
+++ b/source/hot_halo.temperature_profile.F90
@@ -25,7 +25,8 @@ module Hot_Halo_Temperature_Profiles
!!{
Provides a hot halo temperature profile class.
!!}
- use :: Galacticus_Nodes, only : treeNode
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Mass_Distributions, only : kinematicsDistributionClass
private
!![
@@ -36,19 +37,11 @@ module Hot_Halo_Temperature_Profiles
Class implementing hot halo temperature profiles.
virial
-
- Return the temperature of the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
+
+ Return the temperature distribution of the hot halo.
+ class(kinematicsDistributionClass)
yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Return the logarithmic slope of the temperature of the hot halo at the given {\normalfont \ttfamily radius}.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
+ type(treeNode), intent(inout) :: node
!!]
diff --git a/source/hot_halo.temperature_profile.virial.F90 b/source/hot_halo.temperature_profile.virial.F90
index fedf5b0bd6..2fef2b4ca3 100644
--- a/source/hot_halo.temperature_profile.virial.F90
+++ b/source/hot_halo.temperature_profile.virial.F90
@@ -38,9 +38,8 @@
private
class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
- final :: virialDestructor
- procedure :: temperature => virialTemperature
- procedure :: temperatureLogSlope => virialTemperatureLogSlope
+ final :: virialDestructor
+ procedure :: get => virialGet
end type hotHaloTemperatureProfileVirial
interface hotHaloTemperatureProfileVirial
@@ -101,32 +100,31 @@ subroutine virialDestructor(self)
return
end subroutine virialDestructor
- double precision function virialTemperature(self,node,radius)
+ function virialGet(self,node) result(kinematicsDistribution_)
!!{
- Return the density in a {\normalfont \ttfamily virial} hot halo mass distribution.
+ Return the virial hot halo temperature distribution for the given {\normalfont \ttfamily node}.
!!}
+ use :: Mass_Distributions , only : kinematicsDistributionIsothermal
+ use :: Numerical_Constants_Astronomical, only : meanAtomicMassPrimordial
implicit none
- class (hotHaloTemperatureProfileVirial), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: radius
-
- virialTemperature=self%darkMatterHaloScale_%temperatureVirial(node)
- return
- end function virialTemperature
-
- double precision function virialTemperatureLogSlope(self,node,radius)
- !!{
- Return the logarithmic slope of the density profile in a {\normalfont \ttfamily virial} hot halo mass
- distribution.
- !!}
- implicit none
- class (hotHaloTemperatureProfileVirial), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- virialTemperatureLogSlope=0.0d0
+ class(kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ class(hotHaloTemperatureProfileVirial), intent(inout) :: self
+ type (treeNode ), intent(inout) :: node
+
+ ! Create an isothermal kinematics distribution.
+ allocate(kinematicsDistributionIsothermal :: kinematicsDistribution_)
+ select type(kinematicsDistribution_)
+ type is (kinematicsDistributionIsothermal)
+ !![
+
+
+ kinematicsDistributionIsothermal( &
+ & temperature_ =self%darkMatterHaloScale_%temperatureVirial (node), &
+ & massAtomicMean= meanAtomicMassPrimordial &
+ & )
+
+
+ !!]
+ end select
return
- end function virialTemperatureLogSlope
-
+ end function virialGet
diff --git a/source/interface.CLASS.F90 b/source/interface.CLASS.F90
index a177827411..7bfca8bb55 100644
--- a/source/interface.CLASS.F90
+++ b/source/interface.CLASS.F90
@@ -594,7 +594,7 @@ double precision function Interface_CLASS_Normalization(cosmologyParameters_) re
write (classParameterFile,'(a)' ) ''
close(classParameterFile)
! Run CLASS.
- call System_Command_Do(classPath//"class "//parameterFile//" >& "//workPath//"/class.log")
+ call System_Command_Do(classPath//"class "//parameterFile//" > "//workPath//"/class.log")
! Read the CLASS output.
found=.false.
open(newUnit=classLog,file=char(workPath)//"/class.log",form="formatted",status="old",iostat=status)
diff --git a/source/kinematic_distributions.Burkert.F90 b/source/kinematic_distributions.Burkert.F90
new file mode 100644
index 0000000000..a43153001c
--- /dev/null
+++ b/source/kinematic_distributions.Burkert.F90
@@ -0,0 +1,171 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for Burkert mass distributions.
+ !!}
+
+ !![
+
+ A kinematic distribution class for Burkert mass distributions.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionBurkert
+ !!{
+ A kinematics distribution for Burkert distributions.
+ !!}
+ contains
+ procedure :: isCollisional => burkertIsCollisional
+ procedure :: velocityDispersion1D => burkertVelocityDispersion1D
+ end type kinematicsDistributionBurkert
+
+ interface kinematicsDistributionBurkert
+ !!{
+ Constructors for the {\normalfont \ttfamily burkert} kinematic distribution class.
+ !!}
+ module procedure burkertConstructorParameters
+ module procedure burkertConstructorInternal
+ end interface kinematicsDistributionBurkert
+
+contains
+
+ function burkertConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily burkert} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionBurkert) :: self
+ type (inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionBurkert()
+ !![
+
+ !!]
+ return
+ end function burkertConstructorParameters
+
+ function burkertConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily burkert} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionBurkert) :: self
+
+ return
+ end function burkertConstructorInternal
+
+ logical function burkertIsCollisional(self)
+ !!{
+ Return true indicating that the burkert kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionBurkert), intent(inout) :: self
+
+ burkertIsCollisional=.false.
+ return
+ end function burkertIsCollisional
+
+ double precision function burkertVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an Burkert kinematic distribution.
+ !!}
+ use :: Dilogarithms , only : Dilogarithm
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (kinematicsDistributionBurkert), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision :: radius
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating Burkert distribution we have an analytic solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionBurkert)
+ radius =+coordinates %rSpherical () &
+ & /massDistributionEmbedding%scaleLength
+ velocityDispersion=real( &
+ & +( &
+ & +sqrt( &
+ & +Pi &
+ & /3.0d0 &
+ & ) &
+ & *sqrt( &
+ & +( &
+ & +(+1.0d0+radius ) &
+ & *(+1.0d0+radius**2) &
+ & *( &
+ & +48.0d0 *Pi *radius &
+ & -11.0d0 *Pi**2*radius &
+ & -96.0d0 *atan(radius) &
+ & -96.0d0 *radius*atan(radius) &
+ & +12.0d0 *radius*log( +2.0d0 )**2 &
+ & - 4.0d0 *Pi *radius*log( +8.0d0 ) &
+ & +dcmplx(+ 6.0d0,- 6.0d0) *radius*log(dcmplx(0.0d0,-1.0d0)- radius)**2 &
+ & +dcmplx(+ 6.0d0,+ 6.0d0) *radius*log(dcmplx(0.0d0,+1.0d0)- radius)**2 &
+ & +dcmplx(+12.0d0,-12.0d0) *radius*log(dcmplx(0.0d0,-1.0d0)- radius) *log((+1.0d0+dcmplx(0.0d0,1.0d0)*radius)/2.0d0) &
+ & -dcmplx(+24.0d0,+24.0d0) *radius*atan(radius)*log(dcmplx(0.0d0,-2.0d0)/(dcmplx(0.0d0,-1.0d0)+radius)) &
+ & -dcmplx(+24.0d0,-24.0d0) *radius*atan(radius)*log(dcmplx(0.0d0,+2.0d0)/(dcmplx(0.0d0,+1.0d0)+radius)) &
+ & +dcmplx(+12.0d0,+12.0d0) *radius*log( dcmplx(+0.0d0,+1.0d0)-radius) *log(dcmplx(+0.0d0,-0.5d0)*(dcmplx(+0.0d0,+1.0d0)+radius )) &
+ & -dcmplx(+ 0.0d0,+24.0d0) *radius*log( +1.0d0 + dcmplx(+0.0d0,+1.0d0)*radius) *log(dcmplx(+0.5d0,-0.5d0)*( +1.0d0 +radius )) &
+ & +dcmplx(+ 0.0d0,+24.0d0) *radius*log( +1.0d0 - dcmplx(+0.0d0,+1.0d0)*radius) *log(dcmplx(+0.5d0,+0.5d0)*( +1.0d0 +radius )) &
+ & +96.0d0 *log( +1.0d0 +radius ) &
+ & +96.0d0 *radius*log( +1.0d0 +radius ) &
+ & -dcmplx(+ 0.0d0,+24.0d0) *radius*log(dcmplx(-0.5d0,+0.5d0)*(dcmplx(+0.0d0,-1.0d0)+radius)) *log( +1.0d0+radius ) &
+ & +dcmplx(+ 0.0d0,+24.0d0) *radius*log(dcmplx(-0.5d0,-0.5d0)*(dcmplx(+0.0d0,+1.0d0)+radius)) *log( +1.0d0+radius ) &
+ & -24.0d0 *radius*log( +1.0d0 +radius )**2+48.0d0*log( +1.0d0+radius**2 ) &
+ & -48.0d0 *radius *log( +1.0d0+radius**2 ) &
+ & -dcmplx(+12.0d0,-12.0d0) *radius*log( dcmplx(+0.0d0,-1.0d0)-radius ) * log( +1.0d0+radius**2 ) &
+ & -dcmplx(+12.0d0,+12.0d0) *radius*log( dcmplx(+0.0d0,+1.0d0)-radius ) * log( +1.0d0+radius**2 ) &
+ & -24.0d0 *radius*log( +1.0d0 +radius ) * log( +1.0d0+radius**2 ) &
+ & +dcmplx(+12.0d0,+12.0d0)*radius*Dilogarithm( +0.5d0 + dcmplx(0.0d0,+0.5d0)* radius ) &
+ & -96.0d0 *radius*Dilogarithm( -radius ) &
+ & -dcmplx(+ 0.0d0,+48.0d0)*radius*Dilogarithm( dcmplx(+0.0d0,-1.0d0) * radius ) &
+ & +dcmplx(+ 0.0d0,+48.0d0)*radius*Dilogarithm( dcmplx(+0.0d0,+1.0d0) * radius ) &
+ & -24.0d0 *radius*Dilogarithm( -radius**2 ) &
+ & -dcmplx(+ 0.0d0,+24.0d0)*radius*Dilogarithm( dcmplx(-0.5d0,+0.5d0) *(dcmplx(0.0d0,-1.0d0) +radius )) &
+ & +dcmplx(+12.0d0,+12.0d0)*radius*Dilogarithm((dcmplx(+0.0d0,-1.0d0)+radius)/(dcmplx(0.0d0,+1.0d0) +radius )) &
+ & +dcmplx(+ 0.0d0,+24.0d0)*radius*Dilogarithm( dcmplx(-0.5d0,-0.5d0) *(dcmplx(0.0d0,+1.0d0) +radius )) &
+ & +dcmplx(+12.0d0,-12.0d0)*radius*Dilogarithm( dcmplx(+0.0d0,-0.5d0) *(dcmplx(0.0d0,+1.0d0) +radius )) &
+ & +dcmplx(+12.0d0,-12.0d0)*radius*Dilogarithm((dcmplx(+0.0d0,+1.0d0)+radius)/(dcmplx(0.0d0,-1.0d0) +radius )) &
+ & -dcmplx(+ 0.0d0,+24.0d0)*radius*Dilogarithm( dcmplx(+0.5d0,-0.5d0) *(+1.0d0+radius )) &
+ & +dcmplx(+ 0.0d0,+24.0d0)*radius*Dilogarithm( dcmplx(+0.5d0,+0.5d0) *(+1.0d0+radius )) &
+ & ) &
+ & ) &
+ & /radius &
+ & ) &
+ & ) &
+ & /4.0d0 &
+ & ) &
+ & *sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *massDistributionEmbedding%densityNormalization &
+ & ) &
+ & * massDistributionEmbedding%scaleLength
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting a Burkert mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our Burkert distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function burkertVelocityDispersion1D
diff --git a/source/kinematic_distributions.Enzo_hydrostatic.F90 b/source/kinematic_distributions.Enzo_hydrostatic.F90
new file mode 100644
index 0000000000..a3c9cf1681
--- /dev/null
+++ b/source/kinematic_distributions.Enzo_hydrostatic.F90
@@ -0,0 +1,177 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class that mimics the ``hydrostatic'' solution from the Enzo code.
+ !!}
+
+ !![
+
+
+ A kinematic class that implements the ``hydrostatic'' temperature profile available in the \gls{enzo}
+ code. Specifically,
+ \begin{equation}
+ T(r) = \hbox{max}\left( {\mathrm{G} M(<r) \mu m_\mathrm{H} \over 3 \mathrm{k_B} r} , T_\mathrm{min} \right),
+ \end{equation}
+ where $M(<r)$ is the total mass enclosed within radius $r$, $\mu$ is the primordial mean atomic mass, and
+ $T_\mathrm{min}=100$~K is a temperature floor introduced so as to avoid the temperature reaching arbitrarily low values.
+
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionEnzoHydrostatic
+ !!{
+ A enzoHydrostatic kinematic distribution.
+ !!}
+ class(massDistributionClass), pointer :: massDistribution_ => null()
+ contains
+ final :: enzoHydrostaticDestructor
+ procedure :: isCollisional => enzoHydrostaticIsCollisional
+ procedure :: temperature => enzoHydrostaticTemperature
+ procedure :: temperatureGradientLogarithmic => enzoHydrostaticTemperature
+ end type kinematicsDistributionEnzoHydrostatic
+
+ interface kinematicsDistributionEnzoHydrostatic
+ !!{
+ Constructors for the {\normalfont \ttfamily enzoHydrostatic} kinematic distribution class.
+ !!}
+ module procedure enzoHydrostaticKinematicsConstructorParameters
+ module procedure enzoHydrostaticKinematicsConstructorInternal
+ end interface kinematicsDistributionEnzoHydrostatic
+
+ ! Minimum temperature allowed in this distribution.
+ double precision, parameter :: temperatureMinimum=1.0d2
+
+contains
+
+ function enzoHydrostaticKinematicsConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionEnzoHydrostatic) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(massDistributionClass ), pointer :: massDistribution_
+
+ !![
+
+ !!]
+ self=kinematicsDistributionEnzoHydrostatic(massDistribution_)
+ !![
+
+
+ !!]
+ return
+ end function enzoHydrostaticKinematicsConstructorParameters
+
+ function enzoHydrostaticKinematicsConstructorInternal(massDistribution_) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily enzoHydrostatic} kinematics distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionEnzoHydrostatic) :: self
+ class(massDistributionClass ), intent(in ), target :: massDistribution_
+ !![
+
+ !!]
+
+ return
+ end function enzoHydrostaticKinematicsConstructorInternal
+
+ subroutine enzoHydrostaticDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily enzoHydrostatic} kinematic distribution class.
+ !!}
+ type(kinematicsDistributionEnzoHydrostatic), intent(inout) :: self
+ implicit none
+
+ !![
+
+ !!]
+ return
+ end subroutine enzoHydrostaticDestructor
+
+ logical function enzoHydrostaticIsCollisional(self)
+ !!{
+ Return false indicating that the enzoHydrostatic kinematic distribution represents collisionless particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionEnzoHydrostatic), intent(inout) :: self
+
+ enzoHydrostaticIsCollisional=.true.
+ return
+ end function enzoHydrostaticIsCollisional
+
+ double precision function enzoHydrostaticTemperature(self,coordinates) result(temperature)
+ !!{
+ Return the temperature at the specified {\normalfont \ttfamily coordinates} in an Enzo hydrostatic kinematic distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : meanAtomicMassPrimordial, gravitationalConstantGalacticus
+ use :: Numerical_Constants_Atomic , only : massHydrogenAtom
+ use :: Numerical_Constants_Physical , only : boltzmannsConstant
+ use :: Numerical_Constants_Prefixes , only : kilo
+ implicit none
+ class (kinematicsDistributionEnzoHydrostatic), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: massEnclosed
+
+ if (coordinates%rSpherical() == 0.0d0) then
+ temperature =temperatureMinimum
+ else
+ massEnclosed=self%massDistribution_%massEnclosedBySphere(coordinates%rSpherical())
+ temperature =max( &
+ & +kilo **2 &
+ & *gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & *meanAtomicMassPrimordial &
+ & *massHydrogenAtom &
+ & /3.0d0 &
+ & /boltzmannsConstant &
+ & /coordinates%rSpherical() , &
+ & temperatureMinimum &
+ & )
+ end if
+ return
+ end function enzoHydrostaticTemperature
+
+ double precision function enzoHydrostaticTemperatureGradientLogarithmic(self,coordinates) result(temperatureGradientLogarithmic)
+ !!{
+ Return the logarithmic gradient of the temperature at the specified {\normalfont \ttfamily coordinates} in an Enzo hydrostatic kinematic distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (kinematicsDistributionEnzoHydrostatic), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: massEnclosed, density
+
+ if (self%temperature(coordinates) <= temperatureMinimum) then
+ temperatureGradientLogarithmic=temperatureMinimum
+ else
+ density =self%massDistribution_%density (coordinates )
+ massEnclosed =self%massDistribution_%massEnclosedBySphere(coordinates%rSpherical())
+ temperatureGradientLogarithmic=+ 4.0d0 &
+ & * Pi &
+ & *coordinates%rSpherical ()**3 &
+ & * density &
+ & / massEnclosed &
+ & - 1.0d0
+ end if
+ return
+ end function enzoHydrostaticTemperatureGradientLogarithmic
diff --git a/source/kinematic_distributions.Lam2013.F90 b/source/kinematic_distributions.Lam2013.F90
new file mode 100644
index 0000000000..7d170a668f
--- /dev/null
+++ b/source/kinematic_distributions.Lam2013.F90
@@ -0,0 +1,208 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for the \cite{lam_modeling_2013} model of halo accretion flows.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+ A kinematic distribution class for the \cite{lam_modeling_2013} model of halo accretion flows.
+
+ !!]
+ type, public, extends(kinematicsDistributionCollisionless) :: kinematicsDistributionLam2013
+ !!{
+ A kinematics distribution for the \cite{lam_modeling_2013} model of halo accretion flows.
+ !!}
+ class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
+ type (interpolator ) :: correlationFunctionVolumeAveraged_
+ double precision , allocatable, dimension(:) :: radius , correlationFunctionVolumeAveraged
+ double precision :: overdensityCritical , radiusVirial , &
+ & massVirial , scaleFactorVelocity , &
+ & redshift , time , &
+ & rateLinearGrowth
+ contains
+ final :: lam2013Destructor
+ procedure :: velocityRadial => lam2013VelocityRadial
+ end type kinematicsDistributionLam2013
+
+ interface kinematicsDistributionLam2013
+ !!{
+ Constructors for the {\normalfont \ttfamily lam2013} kinematic distribution class.
+ !!}
+ module procedure lam2013ConstructorParameters
+ module procedure lam2013ConstructorInternal
+ end interface kinematicsDistributionLam2013
+
+contains
+
+ function lam2013ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily lam2013} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionLam2013) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ double precision , allocatable , dimension(:) :: radius , correlationFunctionVolumeAveraged
+ double precision :: radiusVirial , massVirial , &
+ & overdensityCritical, redshift , &
+ & scaleFactorVelocity, rateLinearGrowth
+
+ !![
+
+ scaleFactorVelocity
+
+ 1.0d0
+ A scale factor to be applied to inflow velocities.
+
+
+ redshift
+ The redshift of the halo.
+
+
+
+ massVirial
+ The virial mass of the halo.
+
+
+
+ radiusVirial
+ The virial radius of the halo.
+
+
+
+ overdensityCritical
+ The critical overdensity.
+
+
+
+ rateLinearGrowth
+ The logarithmic derivative of the linear growth factor with respect to expansion factor.
+
+
+
+ radius
+ The radius in the tabulated volume-averaged correlation function.
+
+
+
+ correlationFunctionVolumeAveraged
+ The correlation in the tabulated volume-averaged correlation function.
+
+
+
+ !!]
+ self=kinematicsDistributionLam2013(massVirial,radiusVirial,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),overdensityCritical,rateLinearGrowth,scaleFactorVelocity,radius,correlationFunctionVolumeAveraged,cosmologyFunctions_)
+ !![
+
+
+ !!]
+ return
+ end function lam2013ConstructorParameters
+
+ function lam2013ConstructorInternal(massVirial,radiusVirial,time,overdensityCritical,rateLinearGrowth,scaleFactorVelocity,radius,correlationFunctionVolumeAveraged,cosmologyFunctions_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily lam2013} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionLam2013) :: self
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ double precision , intent(in ) :: radiusVirial , massVirial , &
+ & overdensityCritical, time , &
+ & scaleFactorVelocity, rateLinearGrowth
+ double precision , intent(in ), dimension(:) :: radius , correlationFunctionVolumeAveraged
+ !![
+
+ !!]
+
+ self%redshift =self%cosmologyFunctions_%redshiftFromExpansionFactor(self%cosmologyFunctions_%expansionFactor(time))
+ self%correlationFunctionVolumeAveraged_=interpolator(radius,correlationFunctionVolumeAveraged)
+ return
+ end function lam2013ConstructorInternal
+
+ subroutine lam2013Destructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily correlationFunction} accretion flow mass distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionLam2013), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine lam2013Destructor
+
+ double precision function lam2013VelocityRadial(self,coordinates,massDistributionEmbedding) result(velocityRadial)
+ !!{
+ Return the radial velocity at the specified {\normalfont \ttfamily coordinates} in the \cite{lam_modeling_2013} model for the accretion flow around a halo.
+ !!}
+ implicit none
+ class (kinematicsDistributionLam2013), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision :: massShell , radius, &
+ & densityContrastNonLinear
+
+ radius=coordinates%rSpherical()
+ ! Evaluate the mass in the shell outside the halo virial radius using equation (B4) of Lam et al. (2013).
+ if (radius > self%radiusVirial) then
+ massShell =+self %cosmologyFunctions_ %matterDensityEpochal (self%time) &
+ & *4.0d0 &
+ & *Pi &
+ & /3.0d0 &
+ & *( &
+ & + radius **3*(1.0d0+self%correlationFunctionVolumeAveraged_%interpolate( radius )) &
+ & -self%radiusVirial**3*(1.0d0+self%correlationFunctionVolumeAveraged_%interpolate(self%radiusVirial)) &
+ & )
+ else
+ massShell =+0.0d0
+ end if
+ ! Compute the nonlinear density contrast using equation (B1) of Lam et al. (2013).
+ densityContrastNonlinear=+( &
+ & +self%massVirial &
+ & + massShell &
+ & ) &
+ & /self%cosmologyFunctions_%matterDensityEpochal (self%time) &
+ & /( &
+ & +4.0d0 &
+ & *Pi &
+ & /3.0d0 &
+ & *radius**3 &
+ & )
+ ! Evaluate the inflow velocity in the spherical collapse model using equation (B2) of Lam et al. (2013).
+ velocityRadial =-self%scaleFactorVelocity &
+ & *self%cosmologyFunctions_%hubbleParameterEpochal (self%time) &
+ & *radius &
+ & *self%cosmologyFunctions_%expansionFactor (self%time) &
+ & *self%rateLinearGrowth &
+ & /3.0d0 &
+ & * self%overdensityCritical &
+ & *( &
+ & +densityContrastNonLinear**(1.0d0/self%overdensityCritical) &
+ & -1.0d0 &
+ & )
+ return
+ end function lam2013VelocityRadial
diff --git a/source/kinematic_distributions.NFW.F90 b/source/kinematic_distributions.NFW.F90
new file mode 100644
index 0000000000..69e8c6a82e
--- /dev/null
+++ b/source/kinematic_distributions.NFW.F90
@@ -0,0 +1,263 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for NFW mass distributions.
+ !!}
+
+ !![
+
+ A kinematic distribution class for NFW mass distributions.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionNFW
+ !!{
+ A kinematics distribution for NFW distributions.
+ !!}
+ logical :: useSeriesApproximation
+ contains
+ procedure :: isCollisional => nfwIsCollisional
+ procedure :: velocityDispersion1D => nfwVelocityDispersion1D
+ end type kinematicsDistributionNFW
+
+ interface kinematicsDistributionNFW
+ !!{
+ Constructors for the {\normalfont \ttfamily nfw} kinematic distribution class.
+ !!}
+ module procedure nfwConstructorParameters
+ module procedure nfwConstructorInternal
+ end interface kinematicsDistributionNFW
+
+contains
+
+ function nfwConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily nfw} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionNFW) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ logical :: useSeriesApproximation
+
+ !![
+
+ useSeriesApproximation
+ .false.
+ If true, use a fast series approximation to the velocity dispersion profile in an NFW mass distribution.
+
+
+ !!]
+ self=kinematicsDistributionNFW(useSeriesApproximation)
+ !![
+
+ !!]
+ return
+ end function nfwConstructorParameters
+
+ function nfwConstructorInternal(useSeriesApproximation) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily nfw} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionNFW) :: self
+ logical , intent(in ) :: useSeriesApproximation
+ !![
+
+ !!]
+
+ return
+ end function nfwConstructorInternal
+
+ logical function nfwIsCollisional(self)
+ !!{
+ Return true indicating that the nfw kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionNFW), intent(inout) :: self
+
+ nfwIsCollisional=.false.
+ return
+ end function nfwIsCollisional
+
+ double precision function nfwVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an NFW kinematic distribution.
+ !!}
+ use :: Dilogarithms , only : Dilogarithm
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (kinematicsDistributionNFW), intent(inout) , target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision , parameter :: minimumRadiusForExactSolution =1.0d-2
+ double precision , parameter :: maximumRadiusForExactSolution =1.0d+2
+ double precision , parameter :: nfwNormalizationFactorUnitRadius=-8.5d0+Pi**2-6.0d0*log(2.0d0)+6.0d0*log(2.0d0)**2 ! Precomputed NFW normalization factor for unit radius.
+ integer , parameter :: maximumExpansionOrder =7
+ double precision , dimension(maximumExpansionOrder+1) :: coefficient , radiusPower
+ double precision :: logRadius , onePlusRadius , &
+ & logOnePlusRadius , velocityDispersionSquare, &
+ & radius
+ integer :: i
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating NFW distribution we have an analytic solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionNFW)
+ radius =+coordinates %rSpherical () &
+ & /massDistributionEmbedding%scaleLength
+ if (self%useSeriesApproximation) then
+ if (radius == 0.0d0) then
+ velocityDispersionSquare=0.0d0
+ else
+ if (radius < 0.33d0) then
+ ! Expand around 0.
+ radiusPower(1)= 1.0d0
+ radiusPower(2)= radius
+ logRadius = log(radius)
+ coefficient(1)= 0.0d0
+ coefficient(2)= 1.0d0/ 4.0d0*(-23.0d0 + 2.0d0*Pi**2- 2.0d0*logRadius)
+ coefficient(3)= (-59.0d0/6.0d0 + Pi**2- logRadius)
+ coefficient(4)= 1.0d0/ 24.0d0*(-101.0d0 +12.0d0*Pi**2-12.0d0*logRadius)
+ coefficient(5)= 11.0d0/ 60.0d0
+ coefficient(6)=-13.0d0/ 240.0d0
+ coefficient(7)= 37.0d0/1400.0d0
+ coefficient(8)=-17.0d0/1050.0d0
+ else if (radius < 0.68d0) then
+ ! Expand around 1/2.
+ radiusPower(1)= 1.0d0
+ radiusPower(2)= radius-0.5d0
+ coefficient(1)= 9.2256912491493508d-2
+ coefficient(2)= 1.8995942538987498d-2
+ coefficient(3)=-6.1247239215578800d-2
+ coefficient(4)= 9.7544538830827322d-2
+ coefficient(5)=-1.4457663797045428d-1
+ coefficient(6)= 2.1545129876370470d-1
+ coefficient(7)=-3.2824371986452579d-1
+ coefficient(8)= 5.1242111712986012d-1
+ else if (radius < 1.35d0) then
+ ! Expand around 1.
+ radiusPower(1)= 1.0d0
+ radiusPower(2)= radius-1.0d0
+ coefficient(1)= 9.3439401238895310d-2
+ coefficient(2)=-6.2683780821546887d-3
+ coefficient(3)=-8.2007484513808621d-3
+ coefficient(4)= 1.0119593363084506d-2
+ coefficient(5)=-9.2481085050239271d-3
+ coefficient(6)= 7.8754354146912774d-3
+ coefficient(7)=-6.5855139302751235d-3
+ coefficient(8)= 5.5035102596088475d-3
+ else if (radius < 2.66d0) then
+ ! Expand around 2.
+ radiusPower(1)= 1.0d0
+ radiusPower(2)= radius-2.0d0
+ coefficient(1)= 8.4126434467263518d-2
+ coefficient(2)=-9.8388986218866523d-3
+ coefficient(3)= 6.1288152708705594d-4
+ coefficient(4)= 4.3464937545102683d-4
+ coefficient(5)=-3.4479664620159904d-4
+ coefficient(6)= 1.8815165134120623d-4
+ coefficient(7)=-9.2066324234421410d-5
+ coefficient(8)= 4.3068151103206337d-5
+ else
+ ! Expand around infinity.
+ radiusPower(1)= 1.0d0
+ radiusPower(2)= 1.0d0/radius
+ logRadius = log(radius)
+ coefficient(1)= 0.0d0
+ coefficient(2)=(- 3.0d0+ 4.0d0*logRadius)/ 16.0d0
+ coefficient(3)=( 69.0d0+ 20.0d0*logRadius)/ 200.0d0
+ coefficient(4)=(- 97.0d0- 60.0d0*logRadius)/ 1200.0d0
+ coefficient(5)=( 71.0d0+ 105.0d0*logRadius)/ 3675.0d0
+ coefficient(6)=(- 1.0d0- 56.0d0*logRadius)/ 3136.0d0
+ coefficient(7)=(-1271.0d0+2520.0d0*logRadius)/211680.0d0
+ coefficient(8)=( 341.0d0- 360.0d0*logRadius)/ 43200.0d0
+ end if
+ do i=3,maximumExpansionOrder+1
+ radiusPower(i)=radiusPower(i-1)*radiusPower(2)
+ end do
+ velocityDispersionSquare=sum(coefficient*radiusPower)
+ end if
+ else
+ if (radius == 1.0d0) then
+ velocityDispersionSquare=nfwNormalizationFactorUnitRadius
+ else if (radius >= maximumRadiusForExactSolution) then
+ logRadius = log(radius)
+ velocityDispersionSquare=+(- 3.0d0+ 4.0d0*logRadius)/( 16.0d0*radius ) &
+ & +( 69.0d0+ 20.0d0*logRadius)/( 200.0d0*radius**2) &
+ & +(- 97.0d0- 60.0d0*logRadius)/( 1200.0d0*radius**3) &
+ & +( 71.0d0+ 105.0d0*logRadius)/( 3675.0d0*radius**4) &
+ & +(- 1.0d0- 56.0d0*logradius)/( 3136.0d0*radius**5) &
+ & +(-1271.0d0+2520.0d0*logRadius)/(211680.0d0*radius**6)
+ else if (radius >= minimumRadiusForExactSolution) then
+ onePlusRadius = 1.0d0+radius
+ logRadius = log( radius)
+ logOnePlusRadius = log(onePlusRadius)
+ velocityDispersionSquare=+0.5d0 &
+ & * radius &
+ & *onePlusRadius**2 &
+ & *( &
+ & +Pi**2 &
+ & -logRadius &
+ & -1.0d0/ radius &
+ & -1.0d0/onePlusRadius**2 &
+ & -6.0d0/onePlusRadius &
+ & +( &
+ & +1.0d0+ 1.0d0/radius**2 &
+ & - 4.0d0/radius &
+ & -2.0d0/onePlusRadius &
+ & ) &
+ & *logOnePlusRadius &
+ & +3.0d0 &
+ & *logOnePlusRadius**2 &
+ & +6.0d0 &
+ & *Dilogarithm(-radius) &
+ & )
+ else if (radius > 0.0d0) then
+ logRadius = log(radius)
+ velocityDispersionSquare=+ 1.0d0/ 4.0d0*(-23.0d0 + 2.0d0*Pi**2- 2.0d0*logRadius)*radius &
+ & + (-59.0d0/6.0d0 + Pi**2- logRadius)*radius**2 &
+ & + 1.0d0/ 24.0d0*(-101.0d0 +12.0d0*Pi**2-12.0d0*logRadius)*radius**3 &
+ & +11.0d0/ 60.0d0 *radius**4 &
+ & -13.0d0/ 240.0d0 *radius**5 &
+ & +37.0d0/1400.0d0 *radius**6
+ else
+ velocityDispersionSquare=0.0d0
+ end if
+ end if
+ velocityDispersion=+sqrt( &
+ & +4.0d0 &
+ & *Pi &
+ & *velocityDispersionSquare &
+ & *gravitationalConstantGalacticus &
+ & *massDistributionEmbedding%densityNormalization &
+ & ) &
+ & * massDistributionEmbedding%scaleLength
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting an NFW mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our NFW distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function nfwVelocityDispersion1D
diff --git a/source/kinematic_distributions.SIDM.isothermal.F90 b/source/kinematic_distributions.SIDM.isothermal.F90
new file mode 100644
index 0000000000..39bbd3b5c0
--- /dev/null
+++ b/source/kinematic_distributions.SIDM.isothermal.F90
@@ -0,0 +1,125 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Provides a kinematic distribution class implementing the ``isothermal'' approximation to the effects of SIDM based on the model
+ of \cite{jiang_semi-analytic_2023}.
+ !!}
+
+ !![
+
+
+ A kinematic distribution class implementing the ``isothermal'' approximation to the effects of SIDM based on the model of
+ \cite{jiang_semi-analytic_2023}.
+
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionSIDMIsothermal
+ !!{
+ A kinematic distribution class implementing the ``isothermal'' approximation to the effects of SIDM based on the model
+ of \cite{jiang_semi-analytic_2023}.
+ !!}
+ contains
+ procedure :: isCollisional => sidmIsothermalIsCollisional
+ procedure :: velocityDispersion1D => sidmIsothermalVelocityDispersion1D
+ end type kinematicsDistributionSIDMIsothermal
+
+ interface kinematicsDistributionSIDMIsothermal
+ !!{
+ Constructors for the {\normalfont \ttfamily sidmIsothermal} kinematic distribution class.
+ !!}
+ module procedure sidmIsothermalConstructorParameters
+ module procedure sidmIsothermalConstructorInternal
+ end interface kinematicsDistributionSIDMIsothermal
+
+contains
+
+ function sidmIsothermalConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sidmIsothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type(kinematicsDistributionSIDMIsothermal) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionSIDMIsothermal()
+ !![
+
+ !!]
+ return
+ end function sidmIsothermalConstructorParameters
+
+ function sidmIsothermalConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily sidmIsothermal} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionSIDMIsothermal) :: self
+
+ return
+ end function sidmIsothermalConstructorInternal
+
+ logical function sidmIsothermalIsCollisional(self)
+ !!{
+ Return true indicating that the sidmIsothermal kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionSIDMIsothermal), intent(inout) :: self
+
+ sidmIsothermalIsCollisional=.false.
+ return
+ end function sidmIsothermalIsCollisional
+
+ double precision function sidmIsothermalVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an SIDMIsothermal kinematic distribution.
+ !!}
+ use :: ISO_Varying_String, only : char
+ implicit none
+ class(kinematicsDistributionSIDMIsothermal), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating SIDM isothermal distribution we have a constant velocity dispersion in the core region.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalSIDMIsothermal )
+ if (coordinates%rSpherical() > massDistributionEmbedding%radiusInteraction()) then
+ velocityDispersion=self %velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ else
+ velocityDispersion=massDistributionEmbedding%velocityDispersionCentral
+ end if
+ class is (massDistributionSphericalSIDMIsothermalBaryons)
+ if (coordinates%rSpherical() > massDistributionEmbedding%radiusInteraction()) then
+ velocityDispersion=self %velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ else
+ velocityDispersion=massDistributionEmbedding%velocityDispersionCentral
+ end if
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting an SIDMIsothermal mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our SIDM isothermal distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function sidmIsothermalVelocityDispersion1D
diff --git a/source/kinematic_distributions.Shi2016.F90 b/source/kinematic_distributions.Shi2016.F90
new file mode 100644
index 0000000000..ecbed71109
--- /dev/null
+++ b/source/kinematic_distributions.Shi2016.F90
@@ -0,0 +1,103 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for the \cite{shi_outer_2016} model of halo accretion flows.
+ !!}
+
+ !![
+
+ A kinematic distribution class for the \cite{shi_outer_2016} model of halo accretion flows.
+
+ !!]
+ type, public, extends(kinematicsDistributionCollisionless) :: kinematicsDistributionShi2016
+ !!{
+ A kinematics distribution for the \cite{shi_outer_2016} model of halo accretion flows.
+ !!}
+ contains
+ procedure :: velocityRadial => shi2016KinematicsVelocityRadial
+ end type kinematicsDistributionShi2016
+
+ interface kinematicsDistributionShi2016
+ !!{
+ Constructors for the {\normalfont \ttfamily shi2016} kinematic distribution class.
+ !!}
+ module procedure shi2016KinematicsConstructorParameters
+ module procedure shi2016KinematicsConstructorInternal
+ end interface kinematicsDistributionShi2016
+
+contains
+
+ function shi2016KinematicsConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily shi2016} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type(kinematicsDistributionShi2016) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionShi2016()
+ !![
+
+ !!]
+ return
+ end function shi2016KinematicsConstructorParameters
+
+ function shi2016KinematicsConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily shi2016} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionShi2016) :: self
+
+ return
+ end function shi2016KinematicsConstructorInternal
+
+ double precision function shi2016KinematicsVelocityRadial(self,coordinates,massDistributionEmbedding) result(velocityRadial)
+ !!{
+ Return the radial velocity at the specified {\normalfont \ttfamily coordinates} in the \cite{shi_outer_2016} model for the accretion flow around a halo.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class(kinematicsDistributionShi2016), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ select type (massDistributionEmbedding)
+ class is (massDistributionShi2016)
+ if (coordinates%rSpherical() > massDistributionEmbedding%radiusMaximumPhysical) then
+ ! Beyond the maximum radius for the flow just return the mean matter velocity.
+ velocityRadial=+massDistributionEmbedding%cosmologyFunctions_ %hubbleParameterEpochal(massDistributionEmbedding%time ) &
+ & *coordinates %rSpherical ( )
+ else if (coordinates%rSpherical() < massDistributionEmbedding%radiusMinimumPhysical) then
+ velocityRadial=+0.0d0
+ call Error_Report('radius is less than minimum tabulated for accretion flow'//{introspection:location})
+ else
+ velocityRadial=+massDistributionEmbedding%interpolatorVelocityPhysical%interpolate (coordinates %rSpherical())
+ end if
+ velocityRadial=+velocityRadial &
+ & *massDistributionEmbedding%scaleFactorVelocity
+ class default
+ velocityRadial=0.0d0
+ call Error_Report('expecting a Shi2016 mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ return
+ end function shi2016KinematicsVelocityRadial
diff --git a/source/kinematic_distributions.Zhao1996.F90 b/source/kinematic_distributions.Zhao1996.F90
new file mode 100644
index 0000000000..a19d6b0945
--- /dev/null
+++ b/source/kinematic_distributions.Zhao1996.F90
@@ -0,0 +1,262 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+!+ Contributions to this file made by: Andrew Benson, Xiaolong Du.
+
+ !!{
+ Implementation of a kinematic distribution class for \cite{zhao_analytical_1996} mass distributions.
+ !!}
+
+ !![
+
+ A kinematic distribution class for \cite{zhao_analytical_1996} mass distributions.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionZhao1996
+ !!{
+ A kinematics distribution for \cite{zhao_analytical_1996} distributions.
+ !!}
+ contains
+ procedure :: isCollisional => zhao1996IsCollisional
+ procedure :: velocityDispersion1D => zhao1996VelocityDispersion1D
+ end type kinematicsDistributionZhao1996
+
+ interface kinematicsDistributionZhao1996
+ !!{
+ Constructors for the {\normalfont \ttfamily zhao1996} kinematic distribution class.
+ !!}
+ module procedure zhao1996ConstructorParameters
+ module procedure zhao1996ConstructorInternal
+ end interface kinematicsDistributionZhao1996
+
+contains
+
+ function zhao1996ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily zhao1996} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type(kinematicsDistributionZhao1996) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionZhao1996()
+ !![
+
+ !!]
+ return
+ end function zhao1996ConstructorParameters
+
+ function zhao1996ConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily zhao1996} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionZhao1996) :: self
+
+ return
+ end function zhao1996ConstructorInternal
+
+ logical function zhao1996IsCollisional(self)
+ !!{
+ Return true indicating that the zhao1996 kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionZhao1996), intent(inout) :: self
+
+ zhao1996IsCollisional=.false.
+ return
+ end function zhao1996IsCollisional
+
+ double precision function zhao1996VelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an Zhao1996 kinematic distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Dilogarithms , only : Dilogarithm
+ implicit none
+ class (kinematicsDistributionZhao1996), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision , parameter :: radiusTiny =1.0d-3, radiusLarge=1.0d2
+ double precision :: radius
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating Zhao1996 distribution we have an analytic solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionZhao1996)
+ radius =+coordinates %rSpherical () &
+ & /massDistributionEmbedding%scaleLength
+ select case (massDistributionEmbedding%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ ! No analytic solution is available - fall back to the numerical solution.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ return
+ case (specialCaseNFW %ID)
+ if (radius < radiusTiny ) then
+ ! Use series solution for small radii.
+ velocityDispersion=+11.0d0*Pi/15.0d0*radius**4 &
+ & + Pi/ 6.0d0*radius**3*(-101.0d0+12.0d0*Pi**2-12.0d0*log(radius)) &
+ & + 2.0d0*Pi/ 3.0d0*radius**2*(- 59.0d0+ 6.0d0*Pi**2- 6.0d0*log(radius)) &
+ & + Pi *radius *(- 23.0d0+ 2.0d0*Pi**2- 2.0d0*log(radius))
+ else if (radius > radiusLarge) then
+ ! Use series solution for large radii.
+ velocityDispersion=+Pi*(- 3.0d0+ 4.0d0*log(radius))/( 4.0d0*radius ) &
+ & +Pi*(+ 69.0d0+ 20.0d0*log(radius))/( 50.0d0*radius**2) &
+ & +Pi*(- 97.0d0- 60.0d0*log(radius))/( 300.0d0*radius**3) &
+ & +Pi*(+284.0d0+420.0d0*log(radius))/(3675.0d0*radius**4)
+ else
+ velocityDispersion=+2.0d0 &
+ & *Pi &
+ & *( &
+ & +radius &
+ & *( &
+ & -1.0d0 &
+ & +radius &
+ & *( &
+ & -9.0d0 &
+ & -7.0d0 * radius &
+ & +Pi **2*(+1.0d0+radius)**2 &
+ & ) &
+ & ) &
+ & +radius**4*log(+1.0d0+1.0d0/radius) &
+ & +log(+1.0d0+radius)+radius*(-(radius*(+1.0d0+2.0d0*radius)*log(radius)) &
+ & +log(+1.0d0+radius)*(-2.0d0-4.0d0*radius*(2.0d0+radius)+3.0d0*radius*(+1.0d0+radius)**2*log(+1.0d0+radius))) &
+ & +6.0d0*radius**2*(+1.0d0+radius)**2*Dilogarithm(-radius) &
+ & ) &
+ & /radius
+ end if
+ case (specialCaseCoredNFW %ID)
+ if (radius < radiusTiny ) then
+ ! Use series solution for small radii.
+ velocityDispersion=+(119.0d0*Pi-12.0d0*Pi**3)/ 6.0d0 &
+ & +(119.0d0*Pi-12.0d0*Pi**3)/ 2.0d0*radius &
+ & +(353.0d0*Pi-36.0d0*Pi**3)/ 6.0d0*radius**2 &
+ & +(121.0d0*Pi-12.0d0*Pi**3)/ 6.0d0*radius**3 &
+ & - 9.0d0*Pi**4 /20.0d0*radius**4
+ else if (radius > radiusLarge) then
+ ! Use series solution for large radii.
+ velocityDispersion=+Pi*(- 5.0d0+ 4.0d0*log(radius))/( 4.0d0*radius ) &
+ & +Pi*(+ 177.0d0+ 60.0d0*log(radius))/( 100.0d0*radius**2) &
+ & +Pi*(- 157.0d0- 60.0d0*log(radius))/( 300.0d0*radius**3) &
+ & +Pi*(+5857.0d0+1260.0d0*log(radius))/(14700.0d0*radius**4)
+ else
+ ! Use full solution.
+ velocityDispersion=+(+1.0d0+radius)**3 &
+ & *( &
+ & +Pi &
+ & *( &
+ & +95.0d0 &
+ & -12.0d0*Pi**2*(+1.0d0+radius)**4 &
+ & + 2.0d0 * radius *(130.0d0+9.0d0*radius*(13.0d0+4.0d0*radius)) &
+ & ) &
+ & /6.0d0 &
+ & /(+1.0d0+radius)**4 &
+ & +(2.0d0*Pi*(2.0d0+9.0d0*radius+6.0d0*radius**2)*log(+1.0d0+radius))/(radius*(+1.0d0+radius)**2) &
+ & - 6.0d0*Pi*log (+1.0d0+radius)**2 &
+ & -12.0d0*Pi*Dilogarithm( -radius) &
+ & )
+ end if
+ case (specialCaseGamma0_5NFW%ID)
+ if (radius < radiusTiny ) then
+ ! Use series solution for small radii.
+ velocityDispersion=+16.0d0*Pi/ 3.0d0*sqrt(radius )*(- 11.0d0+ 16.0d0*log(2.0d0)) &
+ & +16.0d0*Pi/ 15.0d0* radius**1.5d0*(-139.0d0+ 200.0d0*log(2.0d0)) &
+ & + 2.0d0*Pi/ 7.0d0* radius**2.5d0*(-387.0d0+ 560.0d0*log(2.0d0)) &
+ & + 4.0d0*Pi/189.0d0* radius**3.5d0*(-887.0d0+1260.0d0*log(2.0d0))
+ else if (radius > radiusLarge) then
+ ! Use series solution for large radii.
+ velocityDispersion=+Pi*(- 29.0d0+ 24.0d0*log(2.0d0)+ 12.0d0*log(radius))/( 12.0d0*radius ) &
+ & +Pi*(+107.0d0+120.0d0*log(2.0d0)+ 60.0d0*log(radius))/( 120.0d0*radius**2) &
+ & +Pi*(- 11.0d0- 40.0d0*log(2.0d0)- 20.0d0*log(radius))/( 96.0d0*radius**3) &
+ & +Pi*(+ 57.0d0+280.0d0*log(2.0d0)+140.0d0*log(radius))/(1344.0d0*radius**4)
+ else
+ ! Use full solution.
+ velocityDispersion=+8.0d0 &
+ & /9.0d0 &
+ & *Pi &
+ & *( &
+ & - 6.0d0* sqrt(radius *(+1.0d0+radius)) &
+ & -38.0d0* sqrt(radius**3*(+1.0d0+radius)) &
+ & -57.0d0* sqrt(radius**5*(+1.0d0+radius)) &
+ & -24.0d0* sqrt(radius**7*(+1.0d0+radius)) &
+ & +24.0d0*( &
+ & + sqrt(radius**3*(+1.0d0+radius)) &
+ & +3.0d0*sqrt(radius**5*(+1.0d0+radius)) &
+ & +3.0d0*sqrt(radius**7*(+1.0d0+radius)) &
+ & + sqrt(radius**9*(+1.0d0+radius)) &
+ & ) &
+ & * (log(radius) + log(+1.0d0+radius)) &
+ & - 6.0d0*(+1.0d0+radius)**2 *(+1.0d0+2.0d0*radius) *(-1.0d0+8.0d0*radius*(+1.0d0+radius))*asinh(sqrt(radius)) &
+ & -24.0d0* radius **1.5d0*(+1.0d0 +radius)**3.5d0*(log(+radius)-log(+16.0d0*(+1.0d0+radius))) &
+ & ) &
+ & / radius &
+ & /(+1.0d0+radius)
+ end if
+ case (specialCaseGamma1_5NFW%ID)
+ if (radius < radiusTiny ) then
+ ! Use series solution for small radii.
+ velocityDispersion=+8.0d0*Pi/ 3.0d0*sqrt(radius ) &
+ & - Pi/3150.0d0* radius**3.5d0 *(-19861.0d0+60480.0d0*log(2.0d0)-7560.0d0*log(radius)) &
+ & - Pi/ 175.0d0* radius**2.5d0 *(- 8683.0d0+13440.0d0*log(2.0d0)-1680.0d0*log(radius)) &
+ & -4.0d0*Pi/ 75.0d0* radius**1.5d0 *(- 817.0d0+ 960.0d0*log(2.0d0)- 120.0d0*log(radius))
+ else if (radius > radiusLarge) then
+ ! Use series solution for large radii.
+ velocityDispersion=+Pi*(- 7.0d0+ 8.0d0*log(2.0d0)+ 4.0d0*log(radius))/( 4.0d0*radius ) &
+ & +Pi*(+ 147.0d0+ 120.0d0*log(2.0d0)+ 60.0d0*log(radius))/( 200.0d0*radius**2) &
+ & +Pi*(- 79.0d0- 840.0d0*log(2.0d0)- 420.0d0*log(radius))/( 2400.0d0*radius**3) &
+ & +Pi*(-3589.0d0+7560.0d0*log(2.0d0)+3780.0d0*log(radius))/(33600.0d0*radius**4)
+ else
+ ! Use full solution.
+ velocityDispersion=+8.0d0 &
+ & /5.0d0 &
+ & *Pi &
+ & * radius **1.5d0 &
+ & *(+1.0d0+radius)**1.5d0 &
+ & *( &
+ & +2.0d0*(+1.0d0+2*radius*(-1.0d0+4.0d0*radius+8.0d0*radius**2))*asinh(sqrt(radius))/sqrt(radius**5*(+1.0d0+radius)) &
+ & +( &
+ & -2.0d0+radius *(5.0d0+12.0d0*radius-32.0d0*radius*(+1.0d0+radius)* log(2.0d0 ) ) &
+ & +4.0d0*radius**2* (+1.0d0+radius)*(log(radius)-5.0d0*log(+1.0d0+radius)) &
+ & ) &
+ & /radius**2 &
+ & /(+1.0d0+radius) &
+ & )
+ end if
+ case default
+ velocityDispersion=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ velocityDispersion=+sqrt( &
+ & +velocityDispersion &
+ & *gravitationalConstantGalacticus &
+ & *massDistributionEmbedding%densityNormalization &
+ & ) &
+ & * massDistributionEmbedding%scaleLength
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting a Zhao1996 mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our Zhao1996 distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function zhao1996VelocityDispersion1D
diff --git a/source/kinematic_distributions.collisionless.F90 b/source/kinematic_distributions.collisionless.F90
new file mode 100644
index 0000000000..97fcdb66d4
--- /dev/null
+++ b/source/kinematic_distributions.collisionless.F90
@@ -0,0 +1,130 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for collisionless mass distributions.
+ !!}
+
+ !![
+
+ A kinematic distribution class for collisionless mass distributions.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionCollisionless
+ !!{
+ A kinematics distribution for collisionless distributions.
+ !!}
+ contains
+ procedure :: isCollisional => collisionlessIsCollisional
+ procedure :: velocityDispersion1D => collisionlessVelocityDispersion1D
+ end type kinematicsDistributionCollisionless
+
+ interface kinematicsDistributionCollisionless
+ !!{
+ Constructors for the {\normalfont \ttfamily collisionless} kinematic distribution class.
+ !!}
+ module procedure collisionlessConstructorParameters
+ module procedure collisionlessConstructorInternal
+ module procedure collisionlessConstructorDecorated
+ end interface kinematicsDistributionCollisionless
+
+contains
+
+ function collisionlessConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily collisionless} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionCollisionless) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
+
+ !![
+
+ toleranceRelativeVelocityDispersion
+ 1.0d-6
+
+ The relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
+
+
+ toleranceRelativeVelocityDispersionMaximum
+ 1.0d-3
+
+ The maximum relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
+
+ !!]
+ self=kinematicsDistributionCollisionless(toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum)
+ !![
+
+ !!]
+ return
+ end function collisionlessConstructorParameters
+
+ function collisionlessConstructorInternal(toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily collisionless} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionCollisionless) :: self
+ double precision , intent(in ), optional :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
+ !![
+
+ !!]
+
+ return
+ end function collisionlessConstructorInternal
+
+ function collisionlessConstructorDecorated(kinematicsDistribution_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily collisionless} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionCollisionless) :: self
+ class(kinematicsDistributionClass ), intent(in ) :: kinematicsDistribution_
+
+ self%toleranceRelativeVelocityDispersion =kinematicsDistribution_%toleranceRelativeVelocityDispersion
+ self%toleranceRelativeVelocityDispersionMaximum=kinematicsDistribution_%toleranceRelativeVelocityDispersionMaximum
+ return
+ end function collisionlessConstructorDecorated
+
+ logical function collisionlessIsCollisional(self)
+ !!{
+ Return false indicating that the collisionless kinematic distribution represents collisionless particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionCollisionless), intent(inout) :: self
+
+ collisionlessIsCollisional=.false.
+ return
+ end function collisionlessIsCollisional
+
+ double precision function collisionlessVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an collisionless kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionCollisionless), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ return
+ end function collisionlessVelocityDispersion1D
diff --git a/source/kinematic_distributions.finite-resolution.NFW.F90 b/source/kinematic_distributions.finite-resolution.NFW.F90
new file mode 100644
index 0000000000..aa23a03003
--- /dev/null
+++ b/source/kinematic_distributions.finite-resolution.NFW.F90
@@ -0,0 +1,399 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for finite-resolution NFW mass distributions.
+ !!}
+
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+ A kinematic distribution class for finite-resolution NFW mass distributions.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionFiniteResolutionNFW
+ !!{
+ A kinematics distribution for finite-resolution NFW distributions.
+ !!}
+ double precision :: velocityDispersion1DRadiusPrevious , velocityDispersion1DPrevious
+ ! Velocity dispersion tabulation.
+ logical :: velocityDispersion1DTableInitialized
+ integer :: velocityDispersion1DTableLengthResolutionCount , velocityDispersion1DTableRadiusCount
+ double precision , allocatable, dimension(: ) :: velocityDispersion1DTableLengthResolution , velocityDispersion1DTableRadius
+ double precision , allocatable, dimension(:,:) :: velocityDispersion1DTable
+ type (interpolator), allocatable :: velocityDispersion1DTableLengthResolutionInterpolator, velocityDispersion1DTableRadiusInterpolator
+ double precision :: velocityDispersion1DRadiusMinimum , velocityDispersion1DRadiusMaximum , &
+ & velocityDispersion1DLengthResolutionMinimum , velocityDispersion1DLengthResolutionMaximum
+ contains
+ !![
+
+
+
+
+
+ !!]
+ procedure :: isCollisional => finiteResolutionNFWIsCollisional
+ procedure :: velocityDispersion1D => finiteResolutionNFWVelocityDispersion1D
+ procedure :: velocityDispersion1DTabulate => finiteResolutionNFWVelocityDispersionRadialTabulate
+ procedure :: storeVelocityDispersionTable => finiteResolutionNFWStoreVelocityDispersionTable
+ procedure :: restoreVelocityDispersionTable => finiteResolutionNFWRestoreVelocityDispersionTable
+ end type kinematicsDistributionFiniteResolutionNFW
+
+ interface kinematicsDistributionFiniteResolutionNFW
+ !!{
+ Constructors for the {\normalfont \ttfamily nfw} kinematic distribution class.
+ !!}
+ module procedure finiteResolutionNFWConstructorParameters
+ module procedure finiteResolutionNFWConstructorInternal
+ end interface kinematicsDistributionFiniteResolutionNFW
+
+ ! Tabulation resolution parameters.
+ integer, parameter :: velocityDispersion1DTableRadiusPointsPerDecade =100
+ integer, parameter :: velocityDispersion1DTableLengthResolutionPointsPerDecade=100
+
+ class(kinematicsDistributionFiniteResolutionNFW ), pointer :: self_
+ class(massDistributionSphericalFiniteResolutionNFW), pointer :: massDistributionEmbedding_
+ integer :: iLengthResolution_
+ !$omp threadprivate(iLengthResolution_,self_,massDistributionEmbedding_)
+
+contains
+
+ function finiteResolutionNFWConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily finiteResolutionNFW} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type(kinematicsDistributionFiniteResolutionNFW) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionFiniteResolutionNFW()
+ !![
+
+ !!]
+ return
+ end function finiteResolutionNFWConstructorParameters
+
+ function finiteResolutionNFWConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily finiteResolutionNFW} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionFiniteResolutionNFW) :: self
+
+ self%velocityDispersion1DRadiusMinimum =+huge(0.0d0)
+ self%velocityDispersion1DRadiusMaximum =-huge(0.0d0)
+ self%velocityDispersion1DLengthResolutionMinimum=+huge(0.0d0)
+ self%velocityDispersion1DLengthResolutionMaximum=-huge(0.0d0)
+ self%velocityDispersion1DRadiusPrevious =-huge(0.0d0)
+ self%velocityDispersion1DPrevious =-huge(0.0d0)
+ return
+ end function finiteResolutionNFWConstructorInternal
+
+ logical function finiteResolutionNFWIsCollisional(self)
+ !!{
+ Return true indicating that the finiteResolutionNFW kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionFiniteResolutionNFW), intent(inout) :: self
+
+ finiteResolutionNFWIsCollisional=.false.
+ return
+ end function finiteResolutionNFWIsCollisional
+
+ double precision function finiteResolutionNFWVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an finite-resolution NFW kinematic distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (kinematicsDistributionFiniteResolutionNFW), intent(inout) , target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision , parameter :: lengthResolutionScaleFreeSmall=1.0d-3
+ integer (c_size_t ), dimension(0:1) :: jLengthResolution
+ double precision , dimension(0:1) :: hLengthResolution
+ integer :: iLengthResolution
+ double precision :: radiusScaleFree , radiusScaleFreeEffective
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating finite-resolution NFW distribution we have a tabulated solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalFiniteResolutionNFW)
+ if (coordinates%rSpherical() /= self%velocityDispersion1DRadiusPrevious) then
+ self%velocityDispersion1DRadiusPrevious=coordinates%rSpherical()
+ ! Compute the effective radius. In the core of the profile the velocity dispersion must become constant. Therefore, we
+ ! limit the smallest radius we consider to a small fraction of the core radius. Below this radius a constant velocity
+ ! dispersion is assumed.
+ radiusScaleFree =coordinates%rSpherical()/massDistributionEmbedding%radiusScale
+ radiusScaleFreeEffective=max(radiusScaleFree,lengthResolutionScaleFreeSmall*massDistributionEmbedding%lengthResolutionScaleFree)
+ ! Ensure table is sufficiently extensive.
+ call self%velocityDispersion1DTabulate(massDistributionEmbedding,radiusScaleFreeEffective,massDistributionEmbedding%lengthResolutionScaleFree)
+ ! Interpolate to get the velocity dispersion.
+ call self%velocityDispersion1DTableLengthResolutionInterpolator%linearFactors(massDistributionEmbedding%lengthResolutionScaleFree,jLengthResolution(0),hLengthResolution)
+ jLengthResolution(1)=jLengthResolution(0)+1
+ self%velocityDispersion1DPrevious=0.0d0
+ do iLengthResolution=0,1
+ self%velocityDispersion1DPrevious=+self%velocityDispersion1DPrevious &
+ & +self%velocityDispersion1DTableRadiusInterpolator%interpolate(radiusScaleFreeEffective,self%velocityDispersion1DTable(:,jLengthResolution(iLengthResolution))) &
+ & * hLengthResolution(iLengthResolution)
+ end do
+ self%velocityDispersion1DPrevious=+self%velocityDispersion1DPrevious &
+ & *sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *massDistributionEmbedding%densityNormalization &
+ & *massDistributionEmbedding%radiusScale **2 &
+ & )
+ end if
+ velocityDispersion=self%velocityDispersion1DPrevious
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting a finite-resolution NFW mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our finite resolution NFW distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function finiteResolutionNFWVelocityDispersion1D
+
+ subroutine finiteResolutionNFWVelocityDispersionRadialTabulate(self,massDistributionEmbedding,radius,radiusCore)
+ !!{
+ Tabulates the mass enclosed within a given radius for finite resolution NFW density profiles.
+ !!}
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ use :: Numerical_Integration, only : integrator
+ implicit none
+ class (kinematicsDistributionFiniteResolutionNFW ), intent(inout), target :: self
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: massDistributionEmbedding
+ double precision , intent(in ) :: radius , radiusCore
+ double precision , parameter :: radiusTiny =1.0d-2
+ type (integrator ), save :: integrator_
+ logical , save :: initialized =.false.
+ !$omp threadprivate(integrator_,initialized)
+ logical :: retabulate
+ integer :: iLengthResolution , iRadius , &
+ & i
+ double precision :: jeansIntegral , jeansIntegralPrevious, &
+ & radiusLower , radiusUpper , &
+ & radiusOuter , density
+
+ do i=1,2
+ retabulate=.false.
+ if (.not.self%velocityDispersion1DTableInitialized) then
+ retabulate=.true.
+ else if ( &
+ & radius < self%velocityDispersion1DRadiusMinimum &
+ & .or. &
+ & radius > self%velocityDispersion1DRadiusMaximum &
+ & .or. &
+ & radiusCore < self%velocityDispersion1DLengthResolutionMinimum &
+ & .or. &
+ & radiusCore > self%velocityDispersion1DLengthResolutionMaximum &
+ & ) then
+ retabulate=.true.
+ end if
+ if (retabulate .and.i==1) call self%restoreVelocityDispersionTable()
+ if (.not.retabulate ) exit
+ end do
+ if (retabulate) then
+ ! Decide how many points to tabulate and allocate table arrays.
+ self%velocityDispersion1DRadiusMinimum =min(0.5d0*radius ,self%velocityDispersion1DRadiusMinimum )
+ self%velocityDispersion1DRadiusMaximum =max(2.0d0*radius ,self%velocityDispersion1DRadiusMaximum )
+ self%velocityDispersion1DLengthResolutionMinimum =min(0.5d0*radiusCore,self%velocityDispersion1DLengthResolutionMinimum)
+ self%velocityDispersion1DLengthResolutionMaximum =max(2.0d0*radiusCore,self%velocityDispersion1DLengthResolutionMaximum)
+ self%velocityDispersion1DTableRadiusCount =int(log10(self%velocityDispersion1DRadiusMaximum /self%velocityDispersion1DRadiusMinimum )*dble(velocityDispersion1DTableRadiusPointsPerDecade ))+1
+ self%velocityDispersion1DTableLengthResolutionCount=int(log10(self%velocityDispersion1DLengthResolutionMaximum/self%velocityDispersion1DLengthResolutionMinimum)*dble(velocityDispersion1DTableLengthResolutionPointsPerDecade))+1
+ if (allocated(self%velocityDispersion1DTableRadius)) then
+ deallocate(self%velocityDispersion1DTableLengthResolution)
+ deallocate(self%velocityDispersion1DTableRadius )
+ deallocate(self%velocityDispersion1DTable )
+ end if
+ allocate(self%velocityDispersion1DTableLengthResolution( self%velocityDispersion1DTableLengthResolutionCount))
+ allocate(self%velocityDispersion1DTableRadius (self%velocityDispersion1DTableRadiusCount ))
+ allocate(self%velocityDispersion1DTable (self%velocityDispersion1DTableRadiusCount,self%velocityDispersion1DTableLengthResolutionCount))
+ ! Create a range of radii and core radii.
+ self%velocityDispersion1DTableRadius =Make_Range(self%velocityDispersion1DRadiusMinimum ,self%velocityDispersion1DRadiusMaximum ,self%velocityDispersion1DTableRadiusCount ,rangeType=rangeTypeLogarithmic)
+ self%velocityDispersion1DTableLengthResolution=Make_Range(self%velocityDispersion1DLengthResolutionMinimum,self%velocityDispersion1DLengthResolutionMaximum,self%velocityDispersion1DTableLengthResolutionCount,rangeType=rangeTypeLogarithmic)
+ ! Initialize integrator if necessary.
+ if (.not.initialized) then
+ integrator_=integrator(jeansEquationIntegrand,toleranceRelative=1.0d-2)
+ initialized=.true.
+ end if
+ ! Loop over radii and α and populate tables.
+ self_ => self
+ massDistributionEmbedding_ => massDistributionEmbedding
+ radiusOuter = max(10.0d0*self%velocityDispersion1DRadiusMaximum,1000.0d0)
+ do iLengthResolution=1,self%velocityDispersion1DTableLengthResolutionCount
+ iLengthResolution_ =iLengthResolution
+ jeansIntegralPrevious=0.0d0
+ do iRadius=self%velocityDispersion1DTableRadiusCount,1,-1
+ ! For radii that are tiny compared to the core radius the velocity dispersion become almost constant. Simply assume this to avoid floating point errors.
+ if ( &
+ & self%velocityDispersion1DTableRadius(iRadius) < radiusTiny &
+ & .and. &
+ & self%velocityDispersion1DTableRadius(iRadius) < radiusTiny*self%velocityDispersion1DTableLengthResolution(iLengthResolution) &
+ & .and. &
+ & iRadius < self%velocityDispersion1DTableRadiusCount &
+ & ) then
+ self%velocityDispersion1DTable(iRadius,iLengthResolution)=self%velocityDispersion1DTable(iRadius+1,iLengthResolution)
+ else
+ ! Find the limits for the integral.
+ if (iRadius == self%velocityDispersion1DTableRadiusCount) then
+ radiusUpper=radiusOuter
+ else
+ radiusUpper=self%velocityDispersion1DTableRadius(iRadius+1)
+ end if
+ radiusLower =self %velocityDispersion1DTableRadius( iRadius )
+ density =massDistributionEmbedding%densityScaleFree (radiusLower,self%velocityDispersion1DTableLengthResolution(iLengthResolution))
+ jeansIntegral =integrator_ %integrate (radiusLower, radiusUpper )
+ self%velocityDispersion1DTable(iRadius,iLengthResolution)=+sqrt( &
+ & +( &
+ & +jeansIntegral &
+ & +jeansIntegralPrevious &
+ & ) &
+ & /density &
+ & )
+ jeansIntegralPrevious =+jeansIntegralPrevious &
+ & +jeansIntegral
+ end if
+ end do
+ end do
+ ! Build interpolators.
+ if (allocated(self%velocityDispersion1DTableLengthResolutionInterpolator)) deallocate(self%velocityDispersion1DTableLengthResolutionInterpolator)
+ if (allocated(self%velocityDispersion1DTableRadiusInterpolator )) deallocate(self%velocityDispersion1DTableRadiusInterpolator )
+ allocate(self%velocityDispersion1DTableLengthResolutionInterpolator)
+ allocate(self%velocityDispersion1DTableRadiusInterpolator )
+ self%velocityDispersion1DTableLengthResolutionInterpolator=interpolator(self%velocityDispersion1DTableLengthResolution)
+ self%velocityDispersion1DTableRadiusInterpolator =interpolator(self%velocityDispersion1DTableRadius )
+ ! Specify that tabulation has been made.
+ self%velocityDispersion1DTableInitialized=.true.
+ call self%storeVelocityDispersionTable()
+ end if
+ return
+ end subroutine finiteResolutionNFWVelocityDispersionRadialTabulate
+
+ double precision function jeansEquationIntegrand(radius)
+ !!{
+ Integrand for dark matter profile Jeans equation.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ jeansEquationIntegrand=+massDistributionEmbedding_%massEnclosedScaleFree(radius,self_%velocityDispersion1DTableLengthResolution(iLengthResolution_)) &
+ & *massDistributionEmbedding_%densityScaleFree (radius,self_%velocityDispersion1DTableLengthResolution(iLengthResolution_)) &
+ & / radius **2
+ else
+ jeansEquationIntegrand=0.0d0
+ end if
+ return
+ end function jeansEquationIntegrand
+
+ subroutine finiteResolutionNFWStoreVelocityDispersionTable(self)
+ !!{
+ Store the tabulated velocity dispersion data to file.
+ !!}
+ use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
+ & File_Path
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//) , char
+ implicit none
+ class(kinematicsDistributionFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'VelocityDispersion_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ call Directory_Make(char(File_Path(char(fileName))))
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
+ call file%writeDataset(self%velocityDispersion1DTableLengthResolution,'radiusCore' )
+ call file%writeDataset(self%velocityDispersion1DTableRadius ,'radius' )
+ call file%writeDataset(self%velocityDispersion1DTable ,'velocityDispersion')
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ return
+ end subroutine finiteResolutionNFWStoreVelocityDispersionTable
+
+ subroutine finiteResolutionNFWRestoreVelocityDispersionTable(self)
+ !!{
+ Restore the tabulated velocity dispersion data from file.
+ !!}
+ use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//)
+ implicit none
+ class(kinematicsDistributionFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'VelocityDispersion_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ if (File_Exists(fileName)) then
+ if (allocated(self%velocityDispersion1DTableRadius)) then
+ deallocate(self%velocityDispersion1DTableLengthResolution)
+ deallocate(self%velocityDispersion1DTableRadius )
+ deallocate(self%velocityDispersion1DTable )
+ end if
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName))
+ call file%readDataset('radiusCore' ,self%velocityDispersion1DTableLengthResolution)
+ call file%readDataset('radius' ,self%velocityDispersion1DTableRadius )
+ call file%readDataset('velocityDispersion',self%velocityDispersion1DTable )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ self%velocityDispersion1DTableRadiusCount =size(self%velocityDispersion1DTableRadius )
+ self%velocityDispersion1DTableLengthResolutionCount=size(self%velocityDispersion1DTableLengthResolution)
+ self%velocityDispersion1DRadiusMinimum =self%velocityDispersion1DTableRadius ( 1)
+ self%velocityDispersion1DRadiusMaximum =self%velocityDispersion1DTableRadius (self%velocityDispersion1DTableRadiusCount )
+ self%velocityDispersion1DLengthResolutionMinimum =self%velocityDispersion1DTableLengthResolution( 1)
+ self%velocityDispersion1DLengthResolutionMaximum =self%velocityDispersion1DTableLengthResolution(self%velocityDispersion1DTableLengthResolutionCount)
+ if (allocated(self%velocityDispersion1DTableLengthResolutionInterpolator)) deallocate(self%velocityDispersion1DTableLengthResolutionInterpolator)
+ if (allocated(self%velocityDispersion1DTableRadiusInterpolator )) deallocate(self%velocityDispersion1DTableRadiusInterpolator )
+ allocate(self%velocityDispersion1DTableLengthResolutionInterpolator)
+ allocate(self%velocityDispersion1DTableRadiusInterpolator )
+ self%velocityDispersion1DTableLengthResolutionInterpolator=interpolator(self%velocityDispersion1DTableLengthResolution)
+ self%velocityDispersion1DTableRadiusInterpolator =interpolator(self%velocityDispersion1DTableRadius )
+ self%velocityDispersion1DTableInitialized =.true.
+ end if
+ return
+ end subroutine finiteResolutionNFWRestoreVelocityDispersionTable
diff --git a/source/kinematic_distributions.heated.F90 b/source/kinematic_distributions.heated.F90
new file mode 100644
index 0000000000..39e755e8c4
--- /dev/null
+++ b/source/kinematic_distributions.heated.F90
@@ -0,0 +1,261 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for heated mass distributions.
+ !!}
+
+ !![
+
+ An heated kinematic distribution class masses.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionHeated
+ !!{
+ A heated kinematic distribution.
+ !!}
+ logical :: velocityDispersionApproximate
+ type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
+ contains
+ procedure :: isCollisional => heatedIsCollisional
+ procedure :: velocityDispersion1D => heatedVelocityDispersion1D
+ procedure :: jeansEquationIntegrand => heatedJeansEquationIntegrand
+ procedure :: jeansEquationRadius => heatedJeansEquationRadius
+ end type kinematicsDistributionHeated
+
+ interface kinematicsDistributionHeated
+ !!{
+ Constructors for the {\normalfont \ttfamily heated} kinematic distribution class.
+ !!}
+ module procedure heatedConstructorParameters
+ module procedure heatedConstructorInternal
+ end interface kinematicsDistributionHeated
+
+ ! State used to indicate whether we are solving for a self-gravitating heated system or not.
+ logical :: isSelfGravitating=.true.
+ !$omp threadprivate(isSelfGravitating)
+
+contains
+
+ function heatedConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionHeated) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ logical :: velocityDispersionApproximate
+ type (varying_string ) :: nonAnalyticSolver
+ double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
+
+ !![
+
+ velocityDispersionApproximate
+ .true.
+
+
+ If {\normalfont \ttfamily true}, radial velocity dispersion is computed using an approximate method in which we assume
+ that $\sigma_\mathrm{r}^2(r) \rightarrow \sigma_\mathrm{r}^2(r) - (2/3) \epsilon(r)$, where $\epsilon(r)$ is the specific
+ heating energy. If {\normalfont \ttfamily false} then radial velocity dispersion is computed by numerically solving the
+ Jeans equation.
+
+
+
+ toleranceRelativeVelocityDispersion
+ 1.0d-6
+
+ The relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
+
+
+ toleranceRelativeVelocityDispersionMaximum
+ 1.0d-3
+
+ The maximum relative tolerance to use in numerical solutions for the velocity dispersion in dark-matter-only density profiles.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily
+ fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then
+ numerical solvers are used to find solutions.
+
+
+ !!]
+ self=kinematicsDistributionHeated(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),velocityDispersionApproximate,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum)
+ !![
+
+ !!]
+ return
+ end function heatedConstructorParameters
+
+ function heatedConstructorInternal(nonAnalyticSolver,velocityDispersionApproximate,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily heated} kinematics distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionHeated ) :: self
+ logical , intent(in ) :: velocityDispersionApproximate
+ double precision , intent(in ) :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
+ type (enumerationNonAnalyticSolversType), intent(in ) :: nonAnalyticSolver
+ !![
+
+ !!]
+
+ return
+ end function heatedConstructorInternal
+
+ logical function heatedIsCollisional(self)
+ !!{
+ Return false indicating that the heated kinematic distribution represents collisionless particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionHeated), intent(inout) :: self
+
+ heatedIsCollisional=.false.
+ return
+ end function heatedIsCollisional
+
+ double precision function heatedVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an heated kinematic distribution.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ class (kinematicsDistributionHeated), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision :: radiusInitial , energySpecific, &
+ & velocityDispersionSquare
+ type (coordinateSpherical ) :: coordinatesInitial
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating heated distribution we have an optimized numerical solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalHeated)
+ if (massDistributionEmbedding%massDistributionHeating_%specificEnergyIsEverywhereZero() .or. self%nonAnalyticSolver == nonAnalyticSolversFallThrough) then
+ ! Use the original, unheated profile velocity dispersion.
+ velocityDispersion=massDistributionEmbedding%massDistribution_%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding%massDistribution_)
+ else if (self%velocityDispersionApproximate) then
+ ! Use the approximate solution for velocity dispersion.
+ radiusInitial =+massDistributionEmbedding%radiusInitial (coordinates%rSpherical () )
+ coordinatesInitial = [radiusInitial,0.0d0,0.0d0]
+ energySpecific =+massDistributionEmbedding%massDistributionHeating_ %specificEnergy ( radiusInitial ,massDistributionEmbedding%massDistribution_)
+ velocityDispersionSquare =+massDistributionEmbedding%massDistribution_ %kinematicsDistribution_%velocityDispersion1D ( coordinatesInitial ,massDistributionEmbedding%massDistribution_)**2 &
+ & -2.0d0/3.0d0*energySpecific
+ velocityDispersion =+sqrt(max(0.0d0,velocityDispersionSquare))
+ else
+ ! Use a numerical solution.
+ velocityDispersion =+self %velocityDispersion1DNumerical(coordinates ,massDistributionEmbedding )
+ end if
+ class default
+ velocityDispersion =+0.0d0
+ call Error_Report('mass distribution must be of the `massDistributionSphericalHeated` class but found `'//char(massDistributionEmbedding%objectType())//'`'//{introspection:location})
+ end select
+ else
+ ! Our heated distribution is embedded in another distribution. We must compute the velocity dispersion numerically. We set
+ ! state to indicate that we must solve for a non-self-gravitating system.
+ isSelfGravitating =.false.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ isSelfGravitating =.true.
+ end if
+ return
+ end function heatedVelocityDispersion1D
+
+ double precision function heatedJeansEquationIntegrand(self,radius,massDistributionEmbedding)
+ !!{
+ Integrand for the Jeans equation in a heated mass distribution. Here we do the integration with respect to the initial radius
+ $r_i$.
+ \begin{eqnarray}
+ \sigma_r(r) &=& \frac{1}{\rho(r)}\int_r^{r^{\mathrm{max}}} \rho(r) \frac{\mathrm{G} M(r)}{r^2} \mathrm{d} r \nonumber \\
+ &=& \frac{1}{\rho(r)}\int_{r_i}^{r_{i}^{\mathrm{max}}} \rho_i(r_i) \frac{\mathrm{G} M(r_i)}{r_i^2}\left(\frac{r_i}{r}\right)^4 \mathrm{d} r_i.
+ \end{eqnarray}
+ Here $r$ can be written as a function of $r_i$
+ \begin{equation}
+ r=\frac{1}{1/r_i-2\epsilon(r_i)/(\mathrm{G}M(r_i))}.
+ \end{equation}
+ !!}
+ use :: Error , only : Error_Report
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (kinematicsDistributionHeated), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision :: radiusFinal , energySpecific, &
+ & massEnclosed
+ type (coordinateSpherical ) :: coordinates
+
+ if (isSelfGravitating) then
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalHeated)
+ massEnclosed =+massDistributionEmbedding %massDistribution_%massEnclosedBySphere(radius )
+ energySpecific=+massDistributionEmbedding%massDistributionHeating_ %specificEnergy (radius,massDistributionEmbedding%massDistribution_)
+ radiusFinal =+1.0d0 &
+ & /( &
+ & +1.0d0/radius &
+ & -2.0d0*energySpecific/gravitationalConstantGalacticus/massEnclosed &
+ & )
+ if (radiusFinal > 0.0d0) then
+ coordinates = [radius,0.0d0,0.0d0]
+ heatedJeansEquationIntegrand=+gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & *massDistributionEmbedding%massDistribution_%density(coordinates) &
+ & / radius **2 &
+ & *(radius/radiusFinal)**4
+ else
+ heatedJeansEquationIntegrand=+0.0d0
+ end if
+ class default
+ heatedJeansEquationIntegrand =+0.0d0
+ call Error_Report('mass distribution must be of the `massDistributionSphericalHeated` class'//{introspection:location})
+ end select
+ else
+ heatedJeansEquationIntegrand=self%kinematicsDistributionClass%jeansEquationIntegrand(radius,massDistributionEmbedding)
+ end if
+ return
+ end function heatedJeansEquationIntegrand
+
+ double precision function heatedJeansEquationRadius(self,radius,massDistributionEmbedding)
+ !!{
+ Return the radius variable used in solving the Jeans equation that corresponds to a given physical radius.
+ Here we do the integration with respect to the initial radius, so return the initial radius.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (kinematicsDistributionHeated), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (isSelfGravitating) then
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalHeated)
+ heatedJeansEquationRadius=massDistributionEmbedding%radiusInitial(radius)
+ class default
+ heatedJeansEquationRadius=0.0d0
+ call Error_Report('mass distribution must be of the `massDistributionSphericalHeated` class'//{introspection:location})
+ end select
+ else
+ heatedJeansEquationRadius=self%kinematicsDistributionClass%jeansEquationRadius(radius,massDistributionEmbedding)
+ end if
+ return
+ end function heatedJeansEquationRadius
diff --git a/source/kinematic_distributions.isothermal.F90 b/source/kinematic_distributions.isothermal.F90
new file mode 100644
index 0000000000..ebd8e0fd75
--- /dev/null
+++ b/source/kinematic_distributions.isothermal.F90
@@ -0,0 +1,190 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of an isothermal kinematic distribution class.
+ !!}
+
+ !![
+
+ An isothermal kinematic distribution class masses.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionIsothermal
+ !!{
+ An isothermal kinematic distribution.
+ !!}
+ double precision :: temperature_ , massAtomicMean, &
+ & velocityDispersion_
+ contains
+ procedure :: isCollisional => isothermalIsCollisional
+ procedure :: temperature => isothermalTemperature
+ procedure :: temperatureGradientLogarithmic => isothermalTemperatureGradientLogarithmic
+ procedure :: velocityDispersion1D => isothermalVelocityDispersion1D
+ end type kinematicsDistributionIsothermal
+
+ interface kinematicsDistributionIsothermal
+ !!{
+ Constructors for the {\normalfont \ttfamily isothermal} kinematic distribution class.
+ !!}
+ module procedure isothermalConstructorParameters
+ module procedure isothermalConstructorInternal
+ end interface kinematicsDistributionIsothermal
+
+contains
+
+ function isothermalConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionIsothermal) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: temperature , massAtomicMean, &
+ & velocityDispersion
+
+ if (parameters%isPresent('temperature')) then
+ !![
+
+ temperature
+ The temperature of the distribution.
+
+
+
+ massAtomicMean
+ The mean atomic mass (in atomic mass units) of the distribution.
+
+
+ !!]
+ self=kinematicsDistributionIsothermal(temperature_=temperature,massAtomicMean=massAtomicMean)
+ else
+ !![
+
+ velocityDispersion
+ The velocity dispersion of the distribution.
+
+
+ !!]
+ self=kinematicsDistributionIsothermal(velocityDispersion_=velocityDispersion)
+ end if
+ !![
+
+ !!]
+ return
+ end function isothermalConstructorParameters
+
+ function isothermalConstructorInternal(temperature_,massAtomicMean,velocityDispersion_) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily isothermal} kinematics distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Atomic , only : atomicMassUnit
+ use :: Numerical_Constants_Physical, only : boltzmannsConstant
+ use :: Numerical_Constants_Prefixes, only : kilo
+ implicit none
+ type (kinematicsDistributionIsothermal) :: self
+ double precision , intent(in ), optional :: temperature_ , massAtomicMean, &
+ & velocityDispersion_
+ !![
+
+ !!]
+
+ if (present(velocityDispersion_) .and. present(temperature_)) then
+ call Error_Report('can not provide both [temperature] and [velocityDispersion]'//{introspection:location})
+ else if (present(temperature_ )) then
+ if (.not.present(massAtomicMean)) call Error_Report('[massAtomicMean] must be provided' //{introspection:location})
+ self%velocityDispersion_=+sqrt( &
+ & + boltzmannsConstant &
+ & *self%temperature_ &
+ & /self%massAtomicMean &
+ & / atomicMassUnit &
+ & ) &
+ & /kilo
+ else if (present(velocityDispersion_)) then
+ if ( present(massAtomicMean)) call Error_Report('[massAtomicMean] must not be provided'//{introspection:location})
+ self%massAtomicMean =+1.0d0
+ self%temperature_ =+( &
+ & +velocityDispersion_ &
+ & *kilo &
+ & )**2 &
+ & *atomicMassUnit &
+ & /boltzmannsConstant
+ else
+ call Error_Report('either [temperature] or [velocityDispersion] must be supplied'//{introspection:location})
+ end if
+ return
+ end function isothermalConstructorInternal
+
+ logical function isothermalIsCollisional(self)
+ !!{
+ Return true indicating that the isothermal kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionIsothermal), intent(inout) :: self
+
+ isothermalIsCollisional=.true.
+ return
+ end function isothermalIsCollisional
+
+ double precision function isothermalTemperature(self,coordinates)
+ !!{
+ Return the temperature at the specified {\normalfont \ttfamily coordinates} in an isothermal kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionIsothermal), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ !$GLC attributes unused :: coordinates
+
+ isothermalTemperature=self%temperature_
+ return
+ end function isothermalTemperature
+
+ double precision function isothermalTemperatureGradientLogarithmic(self,coordinates)
+ !!{
+ Return the logarithmic gradient of temperature at the specified {\normalfont \ttfamily coordinates} in an isothermal kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionIsothermal), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ !$GLC attributes unused :: coordinates, self
+
+ isothermalTemperatureGradientLogarithmic=0.0d0
+ return
+ end function isothermalTemperatureGradientLogarithmic
+
+ double precision function isothermalVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an isothermal kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionIsothermal), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating isothermal distribution we have an analytic solution for the velocity dispersion.
+ velocityDispersion=self%velocityDispersion_
+ else
+ ! Our isothermal distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function isothermalVelocityDispersion1D
diff --git a/source/kinematic_distributions.local.F90 b/source/kinematic_distributions.local.F90
new file mode 100644
index 0000000000..b24a013c52
--- /dev/null
+++ b/source/kinematic_distributions.local.F90
@@ -0,0 +1,119 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a local kinematic distribution class.
+ !!}
+
+ !![
+
+
+ A local kinematic distribution class in which the 1D velocity dispersion is given by
+ \begin{equation}
+ \sigma_\mathrm{1D}(\mathbf{r}) = \alpha V_\mathrm{c}(r),
+ \end{equation}
+ where $r = |\mathbf{r}|$ and $V_\mathrm{c}(r)$ is the rotation curve. Here, $\alpha=${\normalfont \ttfamily []} is a
+ parameter.
+
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionLocal
+ !!{
+ A local kinematic distribution.
+ !!}
+ double precision :: alpha
+ contains
+ procedure :: isCollisional => localIsCollisional
+ procedure :: velocityDispersion1D => localVelocityDispersion1D
+ end type kinematicsDistributionLocal
+
+ interface kinematicsDistributionLocal
+ !!{
+ Constructors for the {\normalfont \ttfamily local} kinematic distribution class.
+ !!}
+ module procedure localConstructorParameters
+ module procedure localConstructorInternal
+ end interface kinematicsDistributionLocal
+
+contains
+
+ function localConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionLocal) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: alpha
+
+ !![
+
+ alpha
+ The parameter $\alpha$ in the relation $\sigma_\mathrm{1D}(\mathbf{r}) = \alpha V_\mathrm{c}(r)$.
+ 1.0d0/sqrt(2.0d0)
+
+
+ !!]
+ self=kinematicsDistributionLocal(alpha)
+ !![
+
+ !!]
+ return
+ end function localConstructorParameters
+
+ function localConstructorInternal(alpha) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily local} kinematics distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionLocal) :: self
+ double precision , intent(in ) :: alpha
+ !![
+
+ !!]
+
+ return
+ end function localConstructorInternal
+
+ logical function localIsCollisional(self)
+ !!{
+ Return false indicating that the local kinematic distribution represents collisionless particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionLocal), intent(inout) :: self
+
+ localIsCollisional=.false.
+ return
+ end function localIsCollisional
+
+ double precision function localVelocityDispersion1D(self,coordinates,massDistributionEmbedding)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an local kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionLocal), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ localVelocityDispersion1D=+self %alpha &
+ & *massDistributionEmbedding%rotationCurve(coordinates%rSpherical())
+ return
+ end function localVelocityDispersion1D
diff --git a/source/kinematic_distributions.spherical.scaler.F90 b/source/kinematic_distributions.spherical.scaler.F90
new file mode 100644
index 0000000000..c20343b410
--- /dev/null
+++ b/source/kinematic_distributions.spherical.scaler.F90
@@ -0,0 +1,150 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a spherical scaler kinematic distribution class.
+ !!}
+
+ !![
+
+ A spherical scaler kinematic distribution class masses.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionSphericalScaler
+ !!{
+ An spherical scaler kinematic distribution.
+ !!}
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_ => null()
+ double precision :: factorScalingLength , factorScalingMass
+ contains
+ final :: kinematicsSphericalScalerDestructor
+ procedure :: isCollisional => kinematicsSphericalScalerIsCollisional
+ procedure :: velocityDispersion1D => kinematicsSphericalScalerVelocityDispersion1D
+ end type kinematicsDistributionSphericalScaler
+
+ interface kinematicsDistributionSphericalScaler
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalScaler} kinematic distribution class.
+ !!}
+ module procedure kinematicsSphericalScalerConstructorParameters
+ module procedure kinematicsSphericalScalerConstructorInternal
+ end interface kinematicsDistributionSphericalScaler
+
+contains
+
+ function kinematicsSphericalScalerConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalScaler} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionSphericalScaler) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ double precision :: factorScalingLength , factorScalingMass
+
+ !![
+
+ factorScalingLength
+ The factor by which to scale lengths.
+
+
+
+ factorScalingMass
+ The factor by which to scale the mass.
+
+
+
+ !!]
+ self=kinematicsDistributionSphericalScaler(factorScalingLength,factorScalingMass,kinematicsDistribution_)
+ !![
+
+
+ !!]
+ return
+ end function kinematicsSphericalScalerConstructorParameters
+
+ function kinematicsSphericalScalerConstructorInternal(factorScalingLength,factorScalingMass,kinematicsDistribution_) result(self)
+ !!{
+ Constructor for ``sphericalScaler'' convergence class.
+ !!}
+ implicit none
+ type (kinematicsDistributionSphericalScaler) :: self
+ class (kinematicsDistributionClass ), intent(in ), target :: kinematicsDistribution_
+ double precision , intent(in ) :: factorScalingLength , factorScalingMass
+ !![
+
+ !!]
+
+ return
+ end function kinematicsSphericalScalerConstructorInternal
+
+ subroutine kinematicsSphericalScalerDestructor(self)
+ !!{
+ Destructor for the ``sphericalScaler'' mass distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionSphericalScaler), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine kinematicsSphericalScalerDestructor
+
+ logical function kinematicsSphericalScalerIsCollisional(self)
+ !!{
+ Return true indicating that the spherical scaler kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionSphericalScaler), intent(inout) :: self
+
+ kinematicsSphericalScalerIsCollisional=.false.
+ return
+ end function kinematicsSphericalScalerIsCollisional
+
+ double precision function kinematicsSphericalScalerVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in a spherical scaler kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionSphericalScaler), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating scaled distribution we have an analytic solution for the velocity dispersion.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalScaler)
+ velocityDispersion=+sqrt( &
+ & +self%factorScalingMass &
+ & /self%factorScalingLength &
+ & ) &
+ & *self%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding%massDistribution_)
+ class default
+ velocityDispersion=0.0d0
+ call Error_Report('expecting a spherical scaler mass distribution, but received '//char(massDistributionEmbedding%objectType())//{introspection:location})
+ end select
+ else
+ ! Our scaled distribution is embedded in another distribution. We must compute the velocity dispersion numerically.
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function kinematicsSphericalScalerVelocityDispersion1D
diff --git a/source/kinematic_distributions.truncated.F90 b/source/kinematic_distributions.truncated.F90
new file mode 100644
index 0000000000..ed95814c83
--- /dev/null
+++ b/source/kinematic_distributions.truncated.F90
@@ -0,0 +1,165 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for truncated mass distributions.
+ !!}
+
+ !![
+
+ An truncated kinematic distribution class masses.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionTruncated
+ !!{
+ A kinematic distribution for truncated mass distributions.
+ !!}
+ double precision :: velocityDispersionDecoratedTruncateMinimum, velocityDispersionTruncateMinimum, &
+ & densityTruncateMinimum
+ logical :: velocityDispersionTruncateMinimumComputed
+ contains
+ procedure :: isCollisional => truncatedIsCollisional
+ procedure :: velocityDispersion1D => truncatedVelocityDispersion1D
+ end type kinematicsDistributionTruncated
+
+ interface kinematicsDistributionTruncated
+ !!{
+ Constructors for the {\normalfont \ttfamily truncated} kinematic distribution class.
+ !!}
+ module procedure truncatedConstructorParameters
+ module procedure truncatedConstructorInternal
+ end interface kinematicsDistributionTruncated
+
+contains
+
+ function truncatedConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type(kinematicsDistributionTruncated) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=kinematicsDistributionTruncated()
+ !![
+
+ !!]
+ return
+ end function truncatedConstructorParameters
+
+ function truncatedConstructorInternal() result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily isothermal} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionTruncated) :: self
+
+ self%velocityDispersionTruncateMinimumComputed=.false.
+ return
+ end function truncatedConstructorInternal
+
+ logical function truncatedIsCollisional(self)
+ !!{
+ Return false indicating that the truncated kinematic distribution represents collisionless particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionTruncated), intent(inout) :: self
+
+ truncatedIsCollisional=.false.
+ return
+ end function truncatedIsCollisional
+
+ double precision function truncatedVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an truncated kinematic distribution.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ class (kinematicsDistributionTruncated), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinatesTruncateMinimum
+ logical :: analytic
+ double precision :: density , velocityDispersionDecorated
+
+ analytic=.false.
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating truncated distribution we have a piecewise solution for the velocity dispersion in some cases.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalTruncated)
+ if (coordinates%rSpherical() < massDistributionEmbedding%radiusTruncateMinimum) then
+ ! Use the decorated mass distribution solution, adjusted for the outer truncation shell.
+ analytic = .true.
+ kinematicsDistribution_ => massDistributionEmbedding%massDistribution_%kinematicsDistribution ( )
+ density = massDistributionEmbedding %density (coordinates )
+ velocityDispersionDecorated = kinematicsDistribution_ %velocityDispersion1D (coordinates ,massDistributionEmbedding%massDistribution_)
+ if (.not.self%velocityDispersionTruncateMinimumComputed) then
+ coordinatesTruncateMinimum = [massDistributionEmbedding %radiusTruncateMinimum,0.0d0,0.0d0]
+ self%densityTruncateMinimum = massDistributionEmbedding %density (coordinatesTruncateMinimum )
+ self%velocityDispersionDecoratedTruncateMinimum = kinematicsDistribution_ %velocityDispersion1D (coordinatesTruncateMinimum,massDistributionEmbedding%massDistribution_)
+ self%velocityDispersionTruncateMinimum = self %velocityDispersion1DNumerical(coordinatesTruncateMinimum,massDistributionEmbedding )
+ self%velocityDispersionTruncateMinimumComputed = .true.
+ end if
+ !![
+
+ !!]
+ end if
+ class is (massDistributionSphericalTruncatedExponential)
+ if (coordinates%rSpherical() < massDistributionEmbedding%radiusTruncateMinimum) then
+ ! Use the decorated mass distribution solution, adjusted for the outer truncation shell.
+ analytic = .true.
+ kinematicsDistribution_ => massDistributionEmbedding%massDistribution_%kinematicsDistribution ( )
+ density = massDistributionEmbedding %density (coordinates )
+ velocityDispersionDecorated = kinematicsDistribution_ %velocityDispersion1D (coordinates ,massDistributionEmbedding%massDistribution_)
+ if (.not.self%velocityDispersionTruncateMinimumComputed) then
+ coordinatesTruncateMinimum = [massDistributionEmbedding %radiusTruncateMinimum,0.0d0,0.0d0]
+ self%densityTruncateMinimum = massDistributionEmbedding %density (coordinatesTruncateMinimum )
+ self%velocityDispersionDecoratedTruncateMinimum = kinematicsDistribution_ %velocityDispersion1D (coordinatesTruncateMinimum,massDistributionEmbedding%massDistribution_)
+ self%velocityDispersionTruncateMinimum = self %velocityDispersion1DNumerical(coordinatesTruncateMinimum,massDistributionEmbedding )
+ self%velocityDispersionTruncateMinimumComputed = .true.
+ end if
+ !![
+
+ !!]
+ end if
+ class default
+ velocityDispersion=+0.0d0
+ call Error_Report('mass distribution must be of the `massDistributionSphericalTruncated` or `massDistributionSphericalTruncatedExponential` class'//{introspection:location})
+ end select
+ end if
+ ! Use a numerical solution if no analytic solution was available.
+ if (analytic) then
+ velocityDispersion=+sqrt( &
+ & + velocityDispersionDecorated **2 &
+ & +self%densityTruncateMinimum &
+ & / density &
+ & *( &
+ & +self%velocityDispersionTruncateMinimum **2 &
+ & -self%velocityDispersionDecoratedTruncateMinimum**2 &
+ & ) &
+ & )
+ else
+ velocityDispersion=+self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function truncatedVelocityDispersion1D
+
diff --git a/source/kinematic_distributions.undecorator.F90 b/source/kinematic_distributions.undecorator.F90
new file mode 100644
index 0000000000..4e54273cea
--- /dev/null
+++ b/source/kinematic_distributions.undecorator.F90
@@ -0,0 +1,132 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a kinematic distribution class for decorated mass distributions, that uses the undecorated mass distribution.
+ !!}
+
+ !![
+
+ A kinematic distribution class for decorated mass distributions, that uses the undecorated mass distribution.
+
+ !!]
+ type, public, extends(kinematicsDistributionClass) :: kinematicsDistributionUndecorator
+ !!{
+ A kinematics distribution for decorated mass distributions, that uses the undecorated mass distribution.
+ !!}
+ class(kinematicsDistributionClass), pointer :: kinematicsDistribution_ => null()
+ contains
+ final :: undecoratorDestructor
+ procedure :: isCollisional => undecoratorIsCollisional
+ procedure :: velocityDispersion1D => undecoratorVelocityDispersion1D
+ end type kinematicsDistributionUndecorator
+
+ interface kinematicsDistributionUndecorator
+ !!{
+ Constructors for the {\normalfont \ttfamily undecorator} kinematic distribution class.
+ !!}
+ module procedure undecoratorConstructorParameters
+ module procedure undecoratorConstructorInternal
+ end interface kinematicsDistributionUndecorator
+
+contains
+
+ function undecoratorConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily undecorator} kinematic distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (kinematicsDistributionUndecorator) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+
+ !![
+
+ !!]
+ self=kinematicsDistributionUndecorator(kinematicsDistribution_)
+ !![
+
+ !!]
+ return
+ end function undecoratorConstructorParameters
+
+ function undecoratorConstructorInternal(kinematicsDistribution_) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily undecorator} kinematic distribution class.
+ !!}
+ implicit none
+ type (kinematicsDistributionUndecorator) :: self
+ class(kinematicsDistributionClass ), intent(in ), target :: kinematicsDistribution_
+ !![
+
+ !!]
+
+ return
+ end function undecoratorConstructorInternal
+
+ subroutine undecoratorDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily undecorator} kinematic distribution class.
+ !!}
+ implicit none
+ type(kinematicsDistributionUndecorator), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine undecoratorDestructor
+
+ logical function undecoratorIsCollisional(self)
+ !!{
+ Return whether undecorator kinematic distribution represents collisional particles.
+ !!}
+ implicit none
+ class(kinematicsDistributionUndecorator), intent(inout) :: self
+
+ undecoratorIsCollisional=self%kinematicsDistribution_%isCollisional()
+ return
+ end function undecoratorIsCollisional
+
+ double precision function undecoratorVelocityDispersion1D(self,coordinates,massDistributionEmbedding) result(velocityDispersion)
+ !!{
+ Return the 1D velocity dispersion at the specified {\normalfont \ttfamily coordinates} in an undecorator kinematic distribution.
+ !!}
+ implicit none
+ class(kinematicsDistributionUndecorator), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (associated(massDistributionEmbedding%kinematicsDistribution_,self)) then
+ ! For the case of a self-gravitating distribution we can use the undecorated kinematic distribution in its own mass distribution.
+ select type (massDistributionEmbedding)
+ class is (massDistributionSphericalDecorator)
+ velocityDispersion=self%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding%massDistribution_)
+ class default
+ velocityDispersion=+0.0d0
+ call Error_Report('mass distribution must be of the `massDistributionSphericalDecorator` class but found `'//char(massDistributionEmbedding%objectType())//'`'//{introspection:location})
+ end select
+ else
+ ! Our distribution is embedded in another distribution. We must compute the velocity dispersion numerically
+ velocityDispersion=self%velocityDispersion1DNumerical(coordinates,massDistributionEmbedding)
+ end if
+ return
+ end function undecoratorVelocityDispersion1D
diff --git a/source/libraryClasses.xml b/source/libraryClasses.xml
index b6c1cf88de..88514a0f92 100644
--- a/source/libraryClasses.xml
+++ b/source/libraryClasses.xml
@@ -29,13 +29,8 @@
-
-
-
-
-
-
+
+
diff --git a/source/mass_distributions.F90 b/source/mass_distributions.F90
index e23715ab28..41abb962d9 100644
--- a/source/mass_distributions.F90
+++ b/source/mass_distributions.F90
@@ -26,18 +26,69 @@ module Mass_Distributions
Implements a class that provides mass distributions.
!!}
use :: Coordinates , only : coordinate
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, massTypeAll , massTypeDark , &
- & massTypeBaryonic , massTypeGalactic , massTypeGaseous , massTypeStellar, &
- & massTypeBlackHole , componentTypeAll , componentTypeUnknown, massTypeUnknown
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeType , enumerationMassTypeType, massTypeAll , massTypeDark , &
+ & massTypeBaryonic , massTypeGalactic , massTypeGaseous , massTypeStellar , &
+ & massTypeBlackHole , componentTypeAll , componentTypeUnknown , massTypeUnknown , &
+ & componentTypeDisk , componentTypeSpheroid , componentTypeBlackHole, enumerationStructureErrorCodeType
use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
use :: Tensors , only : tensorRank2Dimension3Symmetric
+ use :: Numerical_Interpolation , only : interpolator
private
-
+ public :: massDistributionMatches_
+
!![
massDistribution
Mass Distributions
Class providing mass distributions.
+
+
+ call kinematicsDistributionDestructor(self)
+
+
+
+ Set the kinematics distribution for this mass distribution.
+ void
+ yes
+ class(kinematicsDistributionClass), intent(in ) :: kinematicsDistribution_
+
+ call kinematicsDistributionAcquire(self,kinematicsDistribution_)
+
+
+
+ Get a pointer to the kinematics distribution for this mass distribution.
+ class(kinematicsDistributionClass)
+ yes
+
+ massDistributionKinematicsDistribution => self%kinematicsDistribution_
+ call kinematicsDistributionIncrement(self)
+
+
+
+ Set the component and mass types of the mass distribution.
+ void
+ yes
+ type(enumerationComponentTypeType), intent(in ), optional :: componentType
+ type(enumerationMassTypeType ), intent(in ), optional :: massType
+
+ if (present(componentType)) self%componentType=componentType
+ if (present( massType)) self% massType= massType
+
+
+
+ Return the subset of the mass distribution matching the given {\normalfont componentType} and {\normalfont \ttfamily massType}.
+ class(massDistributionClass)
+ yes
+ type(enumerationComponentTypeType), intent(in ), optional :: componentType
+ type(enumerationMassTypeType ), intent(in ), optional :: massType
+
+ if (self%matches(componentType,massType)) then
+ call selfAcquire(self,massDistributionSubset)
+ else
+ massDistributionSubset => null()
+ end if
+
+
Return true if this mass distribution matches the specified component and mass type.
logical
@@ -45,39 +96,7 @@ module Mass_Distributions
type(enumerationComponentTypeType), intent(in ), optional :: componentType
type(enumerationMassTypeType ), intent(in ), optional :: massType
- type(enumerationComponentTypeType) :: componentType_
- type(enumerationMassTypeType ) :: massType_
- if (present(componentType)) then
- componentType_=componentType
- else
- componentType_=componentTypeAll
- end if
- if (present(massType )) then
- massType_ =massType
- else
- massType_ =massTypeAll
- end if
- massDistributionMatches= ( &
- & massType_ == massTypeAll &
- & .or. &
- & massType_ == massTypeDark .and. self%massType == massTypeDark &
- & .or. &
- & massType_ == massTypeBaryonic .and. (self%massType == massTypeGaseous .or. self%massType == massTypeStellar ) &
- & .or. &
- & massType_ == massTypeGalactic .and. (self%massType == massTypeGaseous .or. self%massType == massTypeStellar .or. self%massType == massTypeBlackHole) &
- & .or. &
- & massType_ == massTypeGaseous .and. self%massType == massTypeGaseous &
- & .or. &
- & massType_ == massTypeStellar .and. self%massType == massTypeStellar &
- & .or. &
- & massType_ == massTypeBlackHole .and. self%massType == massTypeBlackHole &
- & ) &
- & .and. &
- & ( &
- & componentType_ == componentTypeAll &
- & .or. &
- & componentType_ == self%componentType &
- & )
+ massDistributionMatches=massDistributionMatches_(self%componentType,self%massType,componentType,massType)
@@ -89,6 +108,22 @@ module Mass_Distributions
massDistributionSymmetry=massDistributionSymmetryNone
+
+ Return true if the distribution is spherically symmetric.
+ logical
+ yes
+
+ massDistributionIsSphericallySymmetric=.false.
+
+
+
+ Return true if the distribution can be assumed to have a monotonically decreasing surface density.
+ logical
+ yes
+
+ massDistributionAssumeMonotonicDecreasingSurfaceDensity=.false.
+
+
Return true if the distribution is dimensionless.
logical
@@ -101,68 +136,382 @@ module Mass_Distributions
Return the total mass of the distribution.
double precision
yes
- type(enumerationComponentTypeType), intent(in ), optional :: componentType
- type(enumerationMassTypeType ), intent(in ), optional :: massType
Return the gravitational acceleration due to the distribution at the given coordinates.
double precision, dimension(3)
yes
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(coordinate), intent(in ) :: coordinates
Return the gravitational tidal tensor due to the distribution at the given coordinates.
type(tensorRank2Dimension3Symmetric)
yes
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(coordinate), intent(in ) :: coordinates
Return the density of the distribution at the given coordinates.
double precision
yes
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(coordinate), intent(in ) :: coordinates
+
+
+ Return the average density on a spherical shell of the gievn radius.
+ double precision
+ yes
+ double precision, intent(in ) :: radius
Return the radial gradient of density of the distribution at the given coordinates.
double precision
yes
- class (coordinate ), intent(in ) :: coordinates
- logical , intent(in ), optional :: logarithmic
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ yes
+ class (coordinate), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
Return the gravitational potential of the distribution at the given coordinates.
double precision
yes
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ yes
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+
+
+ Return true if the gravitational potential for this distribution has an analytic form.
+ logical
+ yes
+
+ massDistributionPotentialIsAnalytic=.false.
+
+
+
+ Return the difference in that gravitational potential of the distribution between the given coordinates.
+ double precision
+ yes
+ yes
+ class(coordinate ), intent(in ) :: coordinates1, coordinates2
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ Galactic_Structure_Options
+
+ double precision :: potential1, potential2
+
+ massDistributionPotentialDifference=0.0d0
+ if (self%potentialIsAnalytic()) then
+ potential1=self%potential(coordinates1,status)
+ if (present(status)) then
+ if (status /= structureErrorCodeSuccess) return
+ end if
+ potential2=self%potential(coordinates2,status)
+ if (present(status)) then
+ if (status /= structureErrorCodeSuccess) return
+ end if
+ massDistributionPotentialDifference=potential1-potential2
+ else
+ massDistributionPotentialDifference=self%potentialDifferenceNumerical(coordinates1,coordinates2,status)
+ end if
+
+
+
+ Return the difference in that gravitational potential of the distribution between the given coordinates using a numerical calculation.
+ double precision
+ yes
+ yes
+ class(coordinate ), intent(in ) :: coordinates1, coordinates2
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ Galactic_Structure_Options
+
+ massDistributionPotentialDifferenceNumerical=massDistributionPotentialDifferenceNumerical_(self,coordinates1,coordinates2,status)
+
Return the mass enclosed in the distribution by a sphere of given radius.
double precision
yes
yes
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision, intent(in ) :: radius
+
+
+ Return the radius enclosing a specified mass.
+ double precision
+ yes
+ yes
+ double precision, intent(in ), optional :: mass, massFractional
+
+ massDistributionRadiusEnclosingMass=self%radiusEnclosingMassNumerical(mass,massFractional)
+
+
+
+ Return the radius enclosing a specified mass using a numerical calculation.
+ double precision
+ yes
+ yes
+ double precision, intent(in ), optional :: mass, massFractional
+ Root_Finder
+
+ type (rootFinder), save :: finder
+ logical , save :: finderConstructed=.false.
+ !$omp threadprivate(finder,finderConstructed)
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-6
+
+ if (present(mass )) then
+ massTarget= mass
+ else if (present(massFractional)) then
+ massTarget=self%massTotal()*massFractional
+ else
+ call Error_Report('either "mass" or "massFractional" must be provided'//{introspection:location})
+ end if
+ if (massTarget <= 0.0d0) then
+ massDistributionRadiusEnclosingMassNumerical=0.0d0
+ return
+ end if
+ if (.not.finderConstructed) then
+ finder =rootFinder( &
+ & rootFunction =massEnclosedRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & solverType =GSL_Root_fSolver_Brent , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
+ & )
+ finderConstructed=.true.
+ end if
+ self_ => self
+ massDistributionRadiusEnclosingMassNumerical = finder%find(rootGuess=1.0d0)
+
+
+
+ Return the radius enclosing a specified density.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: density
+ double precision, intent(in ), optional :: radiusGuess
+
+ massDistributionRadiusEnclosingDensity=self%radiusEnclosingDensityNumerical(density,radiusGuess)
+
+
+
+ Return the radius enclosing a specified density using a numerical calculation.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: density
+ double precision, intent(in ), optional :: radiusGuess
+ Root_Finder
+
+ type (rootFinder), save :: finder
+ logical , save :: finderConstructed=.false.
+ !$omp threadprivate(finder,finderConstructed)
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-3
+ double precision :: radiusGuess_
+
+ if (.not.finderConstructed) then
+ finder =rootFinder( &
+ & rootFunction =densityEnclosedRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & solverType =GSL_Root_fSolver_Brent , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative &
+ & )
+ finderConstructed=.true.
+ end if
+ self_ => self
+ densityTarget = density
+ radiusGuess_ = self%radiusEnclosingDensityPrevious__
+ if (present(radiusGuess)) radiusGuess_=radiusGuess
+ massDistributionRadiusEnclosingDensityNumerical = finder%find(rootGuess=radiusGuess_)
+ self%radiusEnclosingDensityPrevious__ = massDistributionRadiusEnclosingDensityNumerical
+
+
+
+ Return the radius enclosing a specified surface density.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: densitySurface
+ double precision, intent(in ), optional :: radiusGuess
+
+ massDistributionRadiusEnclosingSurfaceDensity=self%radiusEnclosingSurfaceDensityNumerical(densitySurface,radiusGuess)
+
+
+
+ Return the radius enclosing a specified surface density using a numerical calculation.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: densitySurface
+ double precision, intent(in ), optional :: radiusGuess
+ Root_Finder
+
+ type (rootFinder), save :: finder
+ logical , save :: finderConstructed=.false.
+ !$omp threadprivate(finder,finderConstructed)
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-3
+ double precision :: radiusGuess_
+
+ if (.not.finderConstructed) then
+ finder =rootFinder( &
+ & rootFunction =densitySurfaceEnclosedRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & solverType =GSL_Root_fSolver_Brent , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative &
+ & )
+ finderConstructed=.true.
+ end if
+ self_ => self
+ densitySurfaceTarget = densitySurface
+ radiusGuess_ = self%radiusEnclosingDensitySurfacePrevious__
+ if (present(radiusGuess)) radiusGuess_=radiusGuess
+ massDistributionRadiusEnclosingSurfaceDensityNumerical = finder%find(rootGuess=radiusGuess_)
+ self%radiusEnclosingDensitySurfacePrevious__ = massDistributionRadiusEnclosingSurfaceDensityNumerical
+
+
+
+ Return the radius corresponding to a given specific angular momentum.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: angularMomentumSpecific
+
+ massDistributionRadiusFromSpecificAngularMomentum=self%radiusFromSpecificAngularMomentumNumerical(angularMomentumSpecific)
+
+
+
+ Return the radius corresponding to a given specific angular momentum using a numerical calculation.
+ double precision
+ yes
+ yes
+ double precision, intent(in ) :: angularMomentumSpecific
+ Root_Finder
+
+ type (rootFinder), save :: finder
+ logical , save :: finderConstructed=.false.
+ !$omp threadprivate(finder,finderConstructed)
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-6
+
+ if (angularMomentumSpecific <= 0.0d0) then
+ massDistributionRadiusFromSpecificAngularMomentumNumerical=+0.0d0
+ else
+ if (.not.finderConstructed) then
+ finder =rootFinder( &
+ & rootFunction =specificAngularMomentumRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & solverType =GSL_Root_fSolver_Brent , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
+ & )
+ finderConstructed=.true.
+ end if
+ self_ => self
+ angularMomentumSpecificTarget = angularMomentumSpecific
+ massDistributionRadiusFromSpecificAngularMomentumNumerical = finder%find(rootGuess=1.0d0)
+ end if
+
+
+
+ Return the rotation curve at the given radius.
+ double precision
+ yes
+ double precision, intent(in ) :: radius
+
+
+ Return the rotation curve gradient, $\mathrm{d}V^2/\mathrm{d}r$, at the given radius.
+ double precision
+ yes
+ double precision, intent(in ) :: radius
+
+
+ Return the maximum velocity in the rotation curve.
+ double precision
+ yes
+
+ massDistributionVelocityRotationCurveMaximum=self%rotationCurve(self%radiusRotationCurveMaximum())
+
+
+
+ Return the radius of the maximum velocity in the rotation curve.
+ double precision
+ yes
+ yes
+ Root_Finder
+
+ massDistributionRadiusRotationCurveMaximum=self%radiusRotationCurveMaximumNumerical()
+
+
+
+ Return the radius of the maximum velocity in the rotation curve.
+ double precision
+ yes
+ yes
+ Root_Finder Error
+
+ type (rootFinder), save :: finder
+ logical , save :: finderConstructed=.false.
+ !$omp threadprivate(finder,finderConstructed)
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-06, &
+ & radiusTiny =1.0d-9 , radiusHuge =1.0d+30
+ integer :: status
+
+ if (.not.finderConstructed) then
+ finder =rootFinder( &
+ & rootFunction =rotationCurveMaximumRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & solverType =GSL_Root_fSolver_Brent , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative , &
+ & rangeDownwardLimit =radiusTiny , &
+ & rangeUpwardLimit =radiusHuge &
+ & )
+ finderConstructed=.true.
+ end if
+ self_ => self
+ massDistributionRadiusRotationCurveMaximumNumerical = finder%find(rootGuess=1.0d0,status=status)
+ if (status /= errorStatusSuccess .and. .not.self%tolerateVelocityMaximumFailure) &
+ & call Error_Report('failed to find radius of maximum circular velocity'//{introspection:location})
+
+
+
+ Return the surface density at the given coordinates.
+ double precision
+ yes
+ class(coordinate), intent(in ) :: coordinates
+
+
+ Return the surface density at the given coordinates.
+ double precision
+ yes
+ double precision, intent(in ) :: moment
+ double precision, intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
Return the radial moment of the distribution.
double precision
yes
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
- logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision, intent(in ) :: moment
+ double precision, intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
Return the integral over the square of the density of the distribution.
@@ -171,17 +520,264 @@ module Mass_Distributions
double precision, intent(in ), optional :: radiusMinimum, radiusMaximum
logical , intent( out), optional :: isInfinite
+
+ Return the Chandresekhar integral of the distribution.
+ double precision, dimension(3)
+ yes
+ class (massDistributionClass), intent(inout) :: massDistributionEmbedding, massDistributionPerturber
+ double precision , intent(in ) :: massPerturber
+ class (coordinate ), intent(in ) :: coordinates , velocity
+
+
+ Return the radius at which the freefall time to the center equals the given {\normalfont \ttfamily time}.
+ double precision
+ yes
+ double precision, intent(in ) :: time
+
+
+ Return the rate of increase of the freefall radius corresponding to the given {\normalfont \ttfamily time}.
+ double precision
+ yes
+ double precision, intent(in ) :: time
+
+
+ Return the spherically-symmetrized Fourier transform of the density profile at the given wavenumber.
+ double precision
+ yes
+ double precision, intent(in ) :: radiusOuter , wavenumber
+
+
+ Return the total energy of the distribution within the given radius.
+ double precision
+ yes
+ yes
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass), intent(inout), target :: massDistributionEmbedding
+
Return a position sampled from the distribution.
double precision, dimension(3)
yes
- class(randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
+
+
+ Set a sub-module scope pointers on a stack to allow recursive calls to functions.
+ void
+ yes
+ yes
+ double precision, intent(in ), dimension(3) :: position1, position2, vectorUnit
+ double precision, intent(in ) :: separation
+
+ integer :: i
+ type (massSolver), allocatable, dimension(:) :: solvers_
+ if (allocated(massSolvers)) then
+ if (massSolversCount == size(massSolvers)) then
+ call move_alloc(massSolvers,solvers_)
+ allocate(massSolvers(size(solvers_)+massSolversIncrement))
+ massSolvers(1:size(solvers_))=solvers_
+ do i=1,size(solvers_)
+ nullify(solvers_(i)%self)
+ end do
+ deallocate(solvers_)
+ end if
+ else
+ allocate(massSolvers(massSolversIncrement))
+ end if
+ massSolversCount=massSolversCount+1
+ massSolvers(massSolversCount)%self => self
+ massSolvers(massSolversCount)%separation = separation
+ massSolvers(massSolversCount)%position1 = position1
+ massSolvers(massSolversCount)%position2 = position2
+ massSolvers(massSolversCount)%vectorUnit = vectorUnit
+
+
+
+ Unset a sub-module scope pointers on the stack.
+ void
+ yes
+
+ !$GLC attributes unused :: self
+ massSolvers(massSolversCount)%self => null()
+ massSolversCount=massSolversCount-1
+
+
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_ => null()
+ logical :: dimensionless
+ logical :: tolerateVelocityMaximumFailure = .false.
+ type (enumerationComponentTypeType) :: componentType = componentTypeUnknown
+ type (enumerationMassTypeType ) :: massType = massTypeUnknown
+ double precision :: radiusEnclosingDensityPrevious__ = 1.0d0
+ double precision :: radiusEnclosingDensitySurfacePrevious__ = 1.0d0
+
+ !!]
+
+ !![
+
+ kinematicsDistribution
+ Kinematics Distributions
+ Class providing kinematics distributions.
+
+ Return true if the kinematics is collisional.
+ logical
+ yes
+
+
+ Return the temperature of the distribution at the given coordinates.
+ double precision
+ yes
+ class(coordinate), intent(in ) :: coordinates
+
+ !$GLC attributes unused :: self, coordinates
+ kinematicsDistributionTemperature=0.0d0
+
+
+
+ Return the logarithmic gradient of the temperature of the distribution at the given coordinates.
+ double precision
+ yes
+ class(coordinate), intent(in ) :: coordinates
+
+ !$GLC attributes unused :: self, coordinates
+ kinematicsDistributionTemperatureGradientLogarithmic=0.0d0
+
+
+
+ Return the mean radial velocity at the given coordinate.
+ double precision
+ yes
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass), intent(inout) :: massDistributionEmbedding
+
+ !$GLC attributes unused :: self, coordinates, massDistributionEmbedding
+ kinematicsDistributionVelocityRadial=0.0d0
+
+
+
+ Return the 1D velocity dispersion at the given coordinate.
+ double precision
+ yes
+ yes
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass), intent(inout) :: massDistributionEmbedding
+
+ !$GLC attributes unused :: self, coordinates, massDistributionEmbedding
+ kinematicsDistributionVelocityDispersion1D=0.0d0
+
+
+
+ Return the 1D velocity dispersion at the given coordinate by numerically solving the Jeans equation.
+ double precision
+ yes
+ class(coordinate ), intent(in ) :: coordinates
+ class(massDistributionClass), intent(inout) :: massDistributionEmbedding
+
+ call jeansEquationSolver(self,coordinates%rSpherical(),massDistributionEmbedding)
+ kinematicsDistributionVelocityDispersion1DNumerical=self%velocityDispersion1D__%interpolate(log(coordinates%rSpherical()))
+
+
+
+ Return the radius variable used in solving the Jeans equation that corresponds to a given physical radius.
+ double precision
+ yes
+ double precision , intent(in ) :: radius
+ class (massDistributionClass), intent(inout) :: massDistributionEmbedding
+
+ !$GLC attributes unused :: massDistributionEmbedding
+ kinematicsDistributionJeansEquationRadius=radius
+
+
+
+ Integrand for Jeans equation.
+ double precision
+ yes
+ double precision , intent(in ) :: radius
+ class (massDistributionClass), intent(inout) :: massDistributionEmbedding
+ Numerical_Constants_Astronomical Coordinates
+
+ type(coordinateSpherical) :: coordinates
+ if (radius > 0.0d0) then
+ coordinates = [radius,0.0d0,0.0d0]
+ kinematicsDistributionJeansEquationIntegrand=+gravitationalConstantGalacticus &
+ & *massDistributionEmbedding%massEnclosedBySphere(radius ) &
+ & *massDistributionEmbedding%density (coordinates) &
+ & / radius **2
+ else
+ kinematicsDistributionJeansEquationIntegrand=+0.0d0
+ end if
+
+
+
+ Set a sub-module scope pointers on a stack to allow recursive calls to functions.
+ void
+ yes
+ yes
+ class (massDistributionClass), intent(in ), target :: massDistributionEmbedding
+
+ integer :: i
+ type (kinematicsSolver), allocatable, dimension(:) :: solvers_
+ if (allocated(solvers)) then
+ if (solversCount == size(solvers)) then
+ call move_alloc(solvers,solvers_)
+ allocate(solvers(size(solvers_)+solversIncrement))
+ solvers(1:size(solvers_))=solvers_
+ do i=1,size(solvers_)
+ nullify(solvers_(i)%self )
+ nullify(solvers_(i)%massDistributionEmbedding)
+ end do
+ deallocate(solvers_)
+ end if
+ else
+ allocate(solvers(solversIncrement))
+ end if
+ solversCount=solversCount+1
+ solvers(solversCount)%self => self
+ solvers(solversCount)%massDistributionEmbedding => massDistributionEmbedding
+
+
+
+ Unset a sub-module scope pointers on the stack.
+ void
+ yes
+
+ !$GLC attributes unused :: self
+ solvers(solversCount)%self => null()
+ solvers(solversCount)%massDistributionEmbedding => null()
+ solversCount=solversCount-1
+
+
+ type (interpolator), allocatable :: velocityDispersion1D__
+ double precision , allocatable, dimension(:) :: velocityDispersionRadialVelocity__ , velocityDispersionRadialRadius__
+ double precision :: velocityDispersionRadialRadiusMinimum__ =+huge(0.0d0), velocityDispersionRadialRadiusMaximum__=-huge(0.0d0)
+ double precision :: velocityDispersionRadialRadiusOuter__
+ double precision :: toleranceRelativeVelocityDispersion =1.0d-6
+ double precision :: toleranceRelativeVelocityDispersionMaximum=1.0d-3
+
+ !!]
+
+ !![
+
+ massDistributionHeating
+ Heating of Mass Distributions
+ Class providing heating models for mass distributions.
+
+ Return the specific energy at the given radius.
+ double precision
+ yes
+ double precision , intent(in ) :: radius
+ class (massDistributionClass), intent(inout) :: massDistribution_
+
+
+ Return the radial gradient of the specific energy at the given radius.
+ double precision
+ yes
+ double precision , intent(in ) :: radius
+ class (massDistributionClass), intent(inout) :: massDistribution_
+
+
+ Return true if the specific energy is zero everywhere (i.e. no heating).
+ logical
+ yes
- logical :: dimensionless
- type (enumerationComponentTypeType) :: componentType=componentTypeUnknown
- type (enumerationMassTypeType ) :: massType =massTypeUnknown
!!]
@@ -197,4 +793,490 @@ module Mass_Distributions
!!]
+ ! Enumeration of non-analytic solver options.
+ !![
+
+ nonAnalyticSolvers
+ Used to specify the type of solution to use when no analytic solution is available.
+ yes
+ public
+ yes
+
+
+
+ !!]
+
+ ! Module-scope variables used in root finding.
+ class (massDistributionClass), pointer :: self_
+ double precision :: massTarget , densityTarget , &
+ & angularMomentumSpecificTarget, densitySurfaceTarget
+ !$omp threadprivate(self_,massTarget,densityTarget,angularMomentumSpecificTarget,densitySurfaceTarget)
+
+ ! Module-scope pointers used in integrand functions and root finding.
+ type :: kinematicsSolver
+ class(kinematicsDistributionClass), pointer :: self => null()
+ class(massDistributionClass ), pointer :: massDistributionEmbedding => null()
+ end type kinematicsSolver
+ type (kinematicsSolver), allocatable, dimension(:) :: solvers
+ integer , parameter :: solversIncrement=10
+ integer :: solversCount = 0
+ !$omp threadprivate(solvers,solversCount)
+
+ ! Module-scope pointers used in integrand functions.
+ type :: massSolver
+ class (massDistributionClass), pointer :: self => null()
+ double precision , dimension(3) :: position1 , position2, &
+ & vectorUnit
+ double precision :: separation
+ end type massSolver
+ type (massSolver), allocatable, dimension(:) :: massSolvers
+ integer , parameter :: massSolversIncrement=10
+ integer :: massSolversCount = 0
+ !$omp threadprivate(massSolvers,massSolversCount)
+
+contains
+
+ subroutine kinematicsDistributionDestructor(self)
+ !!{
+ Destroy a kinematics distribution.
+ !!}
+ implicit none
+ type(massDistributionClass ), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine kinematicsDistributionDestructor
+
+ subroutine selfAcquire(self,self_)
+ !!{
+ Acquire a reference to a mass distribution.
+ !!}
+ implicit none
+ class(massDistributionClass), intent(inout), target :: self
+ class(massDistributionClass), intent( out), pointer :: self_
+
+ !![
+
+ !!]
+ return
+ end subroutine selfAcquire
+
+ subroutine kinematicsDistributionAcquire(self,kinematicsDistribution_)
+ !!{
+ Acquire a reference to a kinematics distribution.
+ !!}
+ implicit none
+ class(massDistributionClass ), intent(inout) :: self
+ class(kinematicsDistributionClass), intent(in ), target :: kinematicsDistribution_
+
+ !![
+
+
+ !!]
+ return
+ end subroutine kinematicsDistributionAcquire
+
+ subroutine kinematicsDistributionIncrement(self)
+ !!{
+ Increment the reference count to a kinematics distribution.
+ !!}
+ implicit none
+ class(massDistributionClass), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine kinematicsDistributionIncrement
+
+ logical function massDistributionMatches_(componentTypeTarget,massTypeTarget,componentType,massType)
+ !!{
+ Determine if the requested mass and component types match that of the target mass distribution.
+ !!}
+ implicit none
+ type(enumerationComponentTypeType), intent(in ) :: componentTypeTarget
+ type(enumerationMassTypeType ), intent(in ) :: massTypeTarget
+ type(enumerationComponentTypeType), intent(in ), optional :: componentType
+ type(enumerationMassTypeType ), intent(in ), optional :: massType
+
+ if (present(componentType)) then
+ massDistributionMatches_ = componentType == componentTypeAll &
+ & .or. &
+ & componentType == componentTypeTarget
+ if (massDistributionMatches_.and.present(massType)) then
+ massDistributionMatches_= (massType == massTypeAll ) &
+ & .or. &
+ & (massType == massTypeDark .and. massTypeTarget == massTypeDark ) &
+ & .or. &
+ & (massType == massTypeGaseous .and. massTypeTarget == massTypeGaseous ) &
+ & .or. &
+ & (massType == massTypeStellar .and. massTypeTarget == massTypeStellar ) &
+ & .or. &
+ & (massType == massTypeBlackHole .and. massTypeTarget == massTypeBlackHole ) &
+ & .or. &
+ & (massType == massTypeBaryonic .and. (massTypeTarget == massTypeGaseous .or. massTypeTarget == massTypeStellar )) &
+ & .or. &
+ & (massType == massTypeGalactic .and. (massTypeTarget == massTypeGaseous .or. massTypeTarget == massTypeStellar .or. massTypeTarget == massTypeBlackHole ) &
+ & .and. (componentTypeTarget == componentTypeDisk .or. componentTypeTarget == componentTypeSpheroid .or. componentTypeTarget == componentTypeBlackHole))
+ end if
+ else
+ if (present(massType)) then
+ ! Match on mass distribution only.
+ massDistributionMatches_= (massType == massTypeAll ) &
+ & .or. &
+ & (massType == massTypeDark .and. massTypeTarget == massTypeDark ) &
+ & .or. &
+ & (massType == massTypeGaseous .and. massTypeTarget == massTypeGaseous ) &
+ & .or. &
+ & (massType == massTypeStellar .and. massTypeTarget == massTypeStellar ) &
+ & .or. &
+ & (massType == massTypeBlackHole .and. massTypeTarget == massTypeBlackHole ) &
+ & .or. &
+ & (massType == massTypeBaryonic .and. (massTypeTarget == massTypeGaseous .or. massTypeTarget == massTypeStellar )) &
+ & .or. &
+ & (massType == massTypeGalactic .and. (massTypeTarget == massTypeGaseous .or. massTypeTarget == massTypeStellar .or. massTypeTarget == massTypeBlackHole ) &
+ & .and. (componentTypeTarget == componentTypeDisk .or. componentTypeTarget == componentTypeSpheroid .or. componentTypeTarget == componentTypeBlackHole))
+ else
+ ! No selection was requested, so we always match.
+ massDistributionMatches_=.true.
+ end if
+ end if
+ return
+ end function massDistributionMatches_
+
+ double precision function massEnclosedRoot(radius)
+ !!{
+ Root function used in finding radii enclosing a target mass.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ massEnclosedRoot=+self_%massEnclosedBySphere(radius) &
+ & - massTarget
+ return
+ end function massEnclosedRoot
+
+ double precision function densityEnclosedRoot(radius)
+ !!{
+ Root function used in finding radii enclosing a target density.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+
+ densityEnclosedRoot=+3.0d0 &
+ & /4.0d0 &
+ & /Pi &
+ & *self_%massEnclosedBySphere(radius) &
+ & / radius **3 &
+ & - densityTarget
+ return
+ end function densityEnclosedRoot
+
+ double precision function densitySurfaceEnclosedRoot(radius)
+ !!{
+ Root function used in finding radii enclosing a target surface density.
+ !!}
+ use :: Coordinates, only : coordinateCylindrical, assignment(=)
+ implicit none
+ double precision , intent(in ) :: radius
+ type (coordinateCylindrical) :: coordinates
+
+ coordinates =[radius,0.0d0,0.0d0]
+ densitySurfaceEnclosedRoot=+self_%surfaceDensity (coordinates) &
+ & - densitySurfaceTarget
+ return
+ end function densitySurfaceEnclosedRoot
+
+ double precision function specificAngularMomentumRoot(radius)
+ !!{
+ Root function used in finding radii corresponding to a target specific angular momentum.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ specificAngularMomentumRoot=+self_%rotationCurve (radius) &
+ & * radius &
+ & - angularMomentumSpecificTarget
+ return
+ end function specificAngularMomentumRoot
+
+ double precision function rotationCurveMaximumRoot(radius)
+ !!{
+ Root function used in finding the radius corresponding to the peak of the rotation curve.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ rotationCurveMaximumRoot=self_%rotationCurveGradient(radius)
+ return
+ end function rotationCurveMaximumRoot
+
+ double precision function massDistributionPotentialDifferenceNumerical_(self,coordinates1,coordinates2,status) result(potentialDifference)
+ !!{
+ Numerically calculate the potential difference between the provided coordinates.
+ !!}
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
+ use :: Vectors , only : Vector_Magnitude
+ use :: Numerical_Integration , only : integrator
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess, structureErrorCodeIntegration
+ use :: Error , only : Error_Report , errorStatusSuccess
+ implicit none
+ class (massDistributionClass ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates1 , coordinates2
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateCartesian ) :: coordinates1_ , coordinates2_
+ double precision , dimension(3) :: position1 , position2 , &
+ & vectorUnit
+ double precision :: separation
+ type (integrator ), save :: integrator_
+ logical , save :: initialized =.false.
+ integer :: status_
+
+ if (present(status)) status=structureErrorCodeSuccess
+ coordinates1_=coordinates1
+ coordinates2_=coordinates2
+ position1 =coordinates1_
+ position2 =coordinates2_
+ separation =Vector_Magnitude(position1-position2)
+ if (separation == 0.0d0) then
+ potentialDifference=0.0d0
+ else
+ vectorUnit =+( &
+ & +position1 &
+ & -position2 &
+ & ) &
+ & / separation
+ ! Initialize integrator if necessary.
+ if (.not.initialized) then
+ integrator_=integrator(potentialDifferenceIntegrand,toleranceRelative=1.0d-3)
+ initialized=.true.
+ end if
+ call self%solverSet (position1,position2,vectorUnit,separation)
+ potentialDifference=integrator_%integrate(0.0d0,separation,status=status_)
+ call self%solverUnset( )
+ if (status_ /= errorStatusSuccess) then
+ if (present(status)) then
+ status=structureErrorCodeIntegration
+ else
+ call Error_Report("integration of potential difference failed"//{introspection:location})
+ end if
+ end if
+ end if
+ return
+ end function massDistributionPotentialDifferenceNumerical_
+
+ double precision function potentialDifferenceIntegrand(distance)
+ !!{
+ Integrand used in computing potential differences.
+ !!}
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
+ implicit none
+ double precision , intent(in ) :: distance
+ double precision , dimension(3) :: position , acceleration
+ type (coordinateCartesian) :: coordinates
+
+ position =+massSolvers(massSolversCount) %position2 &
+ & +massSolvers(massSolversCount) %vectorUnit &
+ & * distance
+ coordinates = position
+ acceleration =massSolvers(massSolversCount)%self%acceleration(coordinates)
+ potentialDifferenceIntegrand=-Dot_Product(acceleration,massSolvers(massSolversCount)%vectorUnit) &
+ & *Mpc_per_km_per_s_To_Gyr
+ return
+ end function potentialDifferenceIntegrand
+
+ subroutine jeansEquationSolver(self,radius,massDistributionEmbedding)
+ !!{
+ Solve the Jeans equation numerically to find the 1D velocity dispersion.
+ !!}
+ use, intrinsic :: ISO_C_Binding , only : c_size_t
+ use :: Error , only : Error_Report , errorStatusSuccess
+ use :: Numerical_Integration , only : integrator
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Table_Labels , only : extrapolationTypeFix
+ use :: Numerical_Interpolation, only : gsl_interp_linear
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ implicit none
+ class (kinematicsDistributionClass), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ double precision , parameter :: radiusTinyFactor =1.0d-9 , factorDensityLarge =1.0d+5
+ double precision , parameter :: countPointsPerOctave =2.0d0
+ double precision , parameter :: toleranceFactor =2.0d0
+ double precision , dimension(:) , allocatable :: velocityDispersions , radii
+ double precision :: radiusMinimum , radiusMaximum , &
+ & toleranceRelative , density , &
+ & jeansIntegral , radiusOuter_ , &
+ & radiusLower , radiusUpper , &
+ & radiusLowerJeansEquation , radiusUpperJeansEquation , &
+ & densityMaximum , densityOuter_ , &
+ & jeansIntegralPrevious
+ integer (c_size_t ) :: countRadii , iMinimum , &
+ & iMaximum , i
+ integer :: status
+ type (coordinateSpherical ) :: coordinates
+ type (integrator ), save :: integrator_
+ logical , save :: initialized =.false.
+ logical :: remakeTable
+ !$omp threadprivate(integrator_,initialized)
+
+ ! Determine if the table must be rebuilt.
+ remakeTable=.false.
+ if (.not.allocated(self%velocityDispersionRadialVelocity__)) then
+ remakeTable=.true.
+ else
+ remakeTable= radius < self%velocityDispersionRadialRadiusMinimum__ &
+ & .or. &
+ & radius > self%velocityDispersionRadialRadiusMaximum__
+ end if
+ if (remakeTable) then
+ ! Initialize integrator if necessary.
+ if (.not.initialized) then
+ integrator_=integrator(jeansEquationIntegrand_,toleranceRelative=self%toleranceRelativeVelocityDispersion)
+ initialized=.true.
+ end if
+ ! Find the range of radii at which to compute the velocity dispersion, and construct the arrays.
+ call self%solverSet(massDistributionEmbedding)
+ !! Set an initial range of radii that brackets the requested radius.
+ radiusMinimum=0.5d0*radius
+ radiusMaximum=2.0d0*radius
+ !! Round to the nearest factor of 2.
+ radiusMinimum=2.0d0**floor (log(radiusMinimum)/log(2.0d0))
+ radiusMaximum=2.0d0**ceiling(log(radiusMaximum)/log(2.0d0))
+ !! Expand to encompass any pre-existing range.
+ if (allocated(self%velocityDispersionRadialRadius__)) then
+ radiusMinimum=min(radiusMinimum,self%velocityDispersionRadialRadiusMinimum__)
+ radiusMaximum=max(radiusMaximum,self%velocityDispersionRadialRadiusMaximum__)
+ end if
+ !! Set a suitable outer radius for integration. We require that the density at the outer radius be much smaller than that
+ !! at the maximum radius. This should ensure that any constribution to the Jeans integral from beyond this outer radius is
+ !! negligible.
+ !!! Start at the maximum radius and gradually increase the outer radius until the density is sufficiently small.
+ coordinates =[radiusMaximum,0.0d0,0.0d0]
+ densityMaximum=massDistributionEmbedding%density(coordinates)
+ radiusOuter_ =radiusMaximum
+ densityOuter_ =densityMaximum
+ do while (densityOuter_ > densityMaximum/factorDensityLarge)
+ radiusOuter_ =radiusOuter_*2.0d0
+ coordinates =[radiusOuter_,0.0d0,0.0d0]
+ densityOuter_=massDistributionEmbedding%density(coordinates)
+ end do
+ !! Construct arrays.
+ countRadii=nint(log(radiusMaximum/radiusMinimum)/log(2.0d0)*countPointsPerOctave+1.0d0)
+ allocate(radii (countRadii))
+ allocate(velocityDispersions(countRadii))
+ radii=Make_Range(radiusMinimum,radiusMaximum,int(countRadii),rangeTypeLogarithmic)
+ ! Copy in any usable results from any previous solution.
+ !! Assume by default that no previous solutions are usable.
+ iMinimum=+huge(0_c_size_t)
+ iMaximum=-huge(0_c_size_t)
+ !! Check that a pre-existing solution exists.
+ if (allocated(self%velocityDispersionRadialRadius__)) then
+ !! Check that the outer radius for integration has not changed - if it has we need to recompute the full solution for
+ !! consistency.
+ if (radiusOuter_ == self%velocityDispersionRadialRadiusOuter__) then
+ iMinimum=nint(log(self%velocityDispersionRadialRadiusMinimum__/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
+ iMaximum=nint(log(self%velocityDispersionRadialRadiusMaximum__/radiusMinimum)/log(2.0d0)*countPointsPerOctave)+1_c_size_t
+ velocityDispersions(iMinimum:iMaximum)=self%velocityDispersionRadialVelocity__
+ end if
+ end if
+ ! Solve for the velocity dispersion where old results were unavailable.
+ jeansIntegralPrevious=0.0d0
+ do i=countRadii,1,-1
+ ! Skip cases for which we have a pre-existing solution.
+ if (i >= iMinimum .and. i <= iMaximum) cycle
+ ! Find the limits for the integral.
+ if (i == countRadii) then
+ radiusUpper=radiusOuter_
+ else
+ radiusUpper=radii(i+1)
+ end if
+ radiusLower =radii(i )
+ ! Reset the accumulated Jeans integral if necessary.
+ if (i == iMinimum-1) then
+ coordinates = [radii(iMinimum),0.0d0,0.0d0]
+ jeansIntegralPrevious=+ velocityDispersions(iMinimum )**2 &
+ & *massDistributionEmbedding%density (coordinates)
+ end if
+ ! If the interval is wholly outside of the outer radius, the integral is zero.
+ if (radiusLower > radiusOuter_) then
+ jeansIntegral =0.0d0
+ velocityDispersions(i)=0.0d0
+ else
+ ! Evaluate the integral.
+ coordinates =[radiusLower,0.0d0,0.0d0]
+ density =massDistributionEmbedding%density (coordinates )
+ radiusLowerJeansEquation=self %jeansEquationRadius(radiusLower ,massDistributionEmbedding)
+ radiusUpperJeansEquation=self %jeansEquationRadius(radiusUpper ,massDistributionEmbedding)
+ jeansIntegral =integrator_ %integrate (radiusLowerJeansEquation,radiusUpperJeansEquation,status )
+ if (status /= errorStatusSuccess) then
+ ! Integration failed.
+ toleranceRelative=+ toleranceFactor &
+ & *self%toleranceRelativeVelocityDispersion
+ do while (toleranceRelative < self%toleranceRelativeVelocityDispersionMaximum)
+ call integrator_%toleranceSet(toleranceRelative=toleranceRelative)
+ jeansIntegral=integrator_%integrate(radiusLowerJeansEquation,radiusUpperJeansEquation,status)
+ if (status == errorStatusSuccess) then
+ exit
+ else
+ toleranceRelative=+toleranceFactor &
+ & *toleranceRelative
+ end if
+ end do
+ if (status /= errorStatusSuccess) call Error_Report('integration of Jeans equation failed'//{introspection:location})
+ call integrator_%toleranceSet(toleranceRelative=self%toleranceRelativeVelocityDispersion)
+ end if
+ if (density <= 0.0d0) then
+ ! Density is zero - the velocity dispersion is undefined. If the Jeans integral is also zero this is acceptable - we've
+ ! been asked for the velocity dispersion in a region of zero density, so we simply return zero dispersion as it should have
+ ! no consequence. If the Jeans integral is non-zero however, then something has gone wrong.
+ velocityDispersions(i)=0.0d0
+ if (jeansIntegral+jeansIntegralPrevious > 0.0d0) call Error_Report('undefined velocity dispersion'//{introspection:location})
+ else
+ velocityDispersions(i)=sqrt( &
+ & +( &
+ & +jeansIntegral &
+ & +jeansIntegralPrevious &
+ & ) &
+ & /density &
+ & )
+ end if
+ end if
+ jeansIntegralPrevious=+jeansIntegralPrevious &
+ & +jeansIntegral
+ end do
+ call self%solverUnset()
+ ! Build the interpolator.
+ if (allocated(self%velocityDispersion1D__)) deallocate(self%velocityDispersion1D__)
+ allocate(self%velocityDispersion1D__)
+ self%velocityDispersion1D__=interpolator(log(radii),velocityDispersions,interpolationType=gsl_interp_linear,extrapolationType=extrapolationTypeFix)
+ ! Store the current results for future re-use.
+ if (allocated(self%velocityDispersionRadialRadius__ )) deallocate(self%velocityDispersionRadialRadius__ )
+ if (allocated(self%velocityDispersionRadialVelocity__)) deallocate(self%velocityDispersionRadialVelocity__)
+ allocate(self%velocityDispersionRadialRadius__ (countRadii))
+ allocate(self%velocityDispersionRadialVelocity__(countRadii))
+ self%velocityDispersionRadialRadius__ =radii
+ self%velocityDispersionRadialVelocity__ =velocityDispersions
+ self%velocityDispersionRadialRadiusMinimum__=radiusMinimum
+ self%velocityDispersionRadialRadiusMaximum__=radiusMaximum
+ self%velocityDispersionRadialRadiusOuter__ =radiusOuter_
+ end if
+ return
+ end subroutine jeansEquationSolver
+
+ double precision function jeansEquationIntegrand_(radius)
+ !!{
+ Integrand for the Jeans equation.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ jeansEquationIntegrand_=solvers(solversCount)%self%jeansEquationIntegrand(radius,solvers(solversCount)%massDistributionEmbedding)
+ return
+ end function jeansEquationIntegrand_
+
end module Mass_Distributions
diff --git a/source/mass_distributions.Gaussian_ellipsoid.F90 b/source/mass_distributions.Gaussian_ellipsoid.F90
index 8e4955cad1..876104215f 100644
--- a/source/mass_distributions.Gaussian_ellipsoid.F90
+++ b/source/mass_distributions.Gaussian_ellipsoid.F90
@@ -266,26 +266,20 @@ subroutine gaussianEllipsoidInitialize(self,scaleLength,axes,rotation)
return
end subroutine gaussianEllipsoidInitialize
- double precision function gaussianEllipsoidDensity(self,coordinates,componentType,massType)
+ double precision function gaussianEllipsoidDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a Gaussian ellipsoid mass distribution.
!!}
use :: Coordinates , only : assignment(=), coordinateCartesian
use :: Linear_Algebra, only : assignment(=), operator(*) , vector
implicit none
- class (massDistributionGaussianEllipsoid), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCartesian ) :: position
- double precision , dimension(3) :: positionComponents
- double precision :: mSquared
- type (vector ) :: positionVectorUnrotated, positionVector
+ class (massDistributionGaussianEllipsoid), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCartesian ) :: position
+ double precision , dimension(3) :: positionComponents
+ double precision :: mSquared
+ type (vector ) :: positionVectorUnrotated, positionVector
- if (.not.self%matches(componentType,massType)) then
- gaussianEllipsoidDensity=0.0d0
- return
- end if
! Rotate the position to the frame where the ellipsoid is aligned with the principle Cartesian axes.
position = coordinates
positionComponents = position
@@ -319,7 +313,7 @@ double precision function gaussianEllipsoidDensityEllipsoidal(self,mSquared)
return
end function gaussianEllipsoidDensityEllipsoidal
- function gaussianEllipsoidAcceleration(self,coordinates,componentType,massType)
+ function gaussianEllipsoidAcceleration(self,coordinates)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for Gaussian ellipsoid mass distributions.
!!}
@@ -327,22 +321,16 @@ function gaussianEllipsoidAcceleration(self,coordinates,componentType,massType)
use :: Linear_Algebra , only : assignment(=) , operator(*) , vector
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- double precision , dimension(3 ) :: gaussianEllipsoidAcceleration
- class (massDistributionGaussianEllipsoid), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCartesian ) :: coordinatesCartesian
- double precision , dimension(3) :: positionCartesian , positionCartesianScaleFree , &
- & accelerationScaleFree
- integer :: i
- type (vector ) :: positionVector , positionVectorUnrotated , &
- & accelerationVector , accelerationVectorUnrotated
+ double precision , dimension(3) :: gaussianEllipsoidAcceleration
+ class (massDistributionGaussianEllipsoid), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCartesian ) :: coordinatesCartesian
+ double precision , dimension(3) :: positionCartesian , positionCartesianScaleFree , &
+ & accelerationScaleFree
+ integer :: i
+ type (vector ) :: positionVector , positionVectorUnrotated , &
+ & accelerationVector , accelerationVectorUnrotated
- if (.not.self%matches(componentType,massType)) then
- gaussianEllipsoidAcceleration=0.0d0
- return
- end if
! Ensure that acceleration is tabulated.
call self%accelerationTabulate()
! Construct the scale-free (and rotated) position.
diff --git a/source/mass_distributions.cloud_overdensities.F90 b/source/mass_distributions.cloud_overdensities.F90
index a180179099..688838c22b 100644
--- a/source/mass_distributions.cloud_overdensities.F90
+++ b/source/mass_distributions.cloud_overdensities.F90
@@ -210,25 +210,19 @@ subroutine cloudOverdensitiesDestructor(self)
return
end subroutine cloudOverdensitiesDestructor
- double precision function cloudOverdensitiesDensity(self,coordinates,componentType,massType)
+ double precision function cloudOverdensitiesDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a cloud overdensities mass distribution.
!!}
use :: Coordinates , only : assignment(=), coordinateCartesian
implicit none
- class (massDistributionCloudOverdensities), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCartesian ) :: position
- double precision , dimension(3) :: positionComponents
- double precision :: densityContrast
- integer :: neighborCount
+ class (massDistributionCloudOverdensities), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCartesian ) :: position
+ double precision , dimension(3) :: positionComponents
+ double precision :: densityContrast
+ integer :: neighborCount
- if (.not.self%matches(componentType,massType)) then
- cloudOverdensitiesDensity=0.0d0
- return
- end if
! Extract the position.
position =coordinates
positionComponents=position
diff --git a/source/mass_distributions.composite.F90 b/source/mass_distributions.composite.F90
index 8b9ae0362b..9967858d5c 100644
--- a/source/mass_distributions.composite.F90
+++ b/source/mass_distributions.composite.F90
@@ -38,27 +38,47 @@
!!{
A composite mass distribution class.
!!}
- type(massDistributionList ), pointer :: massDistributions => null()
- type(enumerationMassDistributionSymmetryType) :: symmetry_
+ type (massDistributionList ), pointer :: massDistributions => null()
+ type (enumerationMassDistributionSymmetryType) :: symmetry_
+ logical :: isSingleComponent , isCollisionless
contains
!![
-
+
+
+
!!]
- final :: compositeDestructor
- procedure :: initialize => compositeInitialize
- procedure :: symmetry => compositeSymmetry
- procedure :: isDimensionless => compositeIsDimensionless
- procedure :: massTotal => compositeMassTotal
- procedure :: acceleration => compositeAcceleration
- procedure :: tidalTensor => compositeTidalTensor
- procedure :: density => compositeDensity
- procedure :: densityGradientRadial => compositeDensityGradientRadial
- procedure :: densityRadialMoment => compositeDensityRadialMoment
- procedure :: potential => compositePotential
- procedure :: massEnclosedBySphere => compositeMassEnclosedBySphere
- procedure :: positionSample => compositePositionSample
+ final :: compositeDestructor
+ procedure :: initialize => compositeInitialize
+ procedure :: subset => compositeSubset
+ procedure :: describe => compositeDescribe
+ procedure :: matches => compositeMatches
+ procedure :: symmetry => compositeSymmetry
+ procedure :: assumeMonotonicDecreasingSurfaceDensity => compositeAssumeMonotonicDecreasingSurfaceDensity
+ procedure :: isSphericallySymmetric => compositeIsSphericallySymmetric
+ procedure :: isDimensionless => compositeIsDimensionless
+ procedure :: massTotal => compositeMassTotal
+ procedure :: acceleration => compositeAcceleration
+ procedure :: tidalTensor => compositeTidalTensor
+ procedure :: density => compositeDensity
+ procedure :: surfaceDensity => compositeSurfaceDensity
+ procedure :: densityGradientRadial => compositeDensityGradientRadial
+ procedure :: densityRadialMoment => compositeDensityRadialMoment
+ procedure :: densitySphericalAverage => compositeDensitySphericalAverage
+ procedure :: densitySquareIntegral => compositeDensitySquareIntegral
+ procedure :: potentialIsAnalytic => compositePotentialIsAnalytic
+ procedure :: potential => compositePotential
+ procedure :: potentialDifference => compositePotentialDifference
+ procedure :: energy => compositeEnergy
+ procedure :: massEnclosedBySphere => compositeMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => compositeRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => compositeRadiusEnclosingDensity
+ procedure :: radiusEnclosingSurfaceDensity => compositeRadiusEnclosingSurfaceDensity
+ procedure :: rotationCurve => compositeRotationCurve
+ procedure :: rotationCurveGradient => compositeRotationCurveGradient
+ procedure :: chandrasekharIntegral => compositeChandrasekharIntegral
+ procedure :: positionSample => compositePositionSample
end type massDistributionComposite
interface massDistributionComposite
@@ -107,9 +127,9 @@ function compositeConstructorInternal(massDistributions) result(self)
!!{
Internal constructor for ``composite'' mass distribution class.
!!}
- type(massDistributionComposite) :: self
- type(massDistributionList ), target, intent(in ) :: massDistributions
- type(massDistributionList ), pointer :: massDistribution_
+ type(massDistributionComposite) :: self
+ type(massDistributionList ), pointer, intent(in ) :: massDistributions
+ type(massDistributionList ), pointer :: massDistribution_
self %massDistributions => massDistributions
massDistribution_ => massDistributions
@@ -147,22 +167,33 @@ end subroutine compositeDestructor
subroutine compositeInitialize(self)
!!{
- Destructor for composite mass distributions.
+ Initialize a composite mass distribution.
!!}
+ use :: ISO_Varying_String, only : char
implicit none
- class(massDistributionComposite ), intent(inout) :: self
- type (massDistributionList ), pointer :: massDistribution_
- type (enumerationMassDistributionSymmetryType) :: symmetry_
-
+ class (massDistributionComposite ), intent(inout) :: self
+ type (massDistributionList ), pointer :: massDistribution_
+ type (enumerationMassDistributionSymmetryType) :: symmetry_
+ logical :: firstComponent , haveKinematics
+ double precision :: toleranceRelativeVelocityDispersion, toleranceRelativeVelocityDispersionMaximum
+
! Begin by assuming the highest degree of symmetry.
- self%symmetry_=massDistributionSymmetrySpherical
+ self%symmetry_ =massDistributionSymmetrySpherical
+ ! Begin by assuming a single component.
+ self%isSingleComponent=.true.
+ firstComponent =.true.
+ ! Begin by assuming a collisionless distribution.
+ self%isCollisionless =.true.
+ haveKinematics =.true.
! Examine each distribution.
if (associated(self%massDistributions)) then
- massDistribution_ => self%massDistributions
+ massDistribution_ => self%massDistributions
+ toleranceRelativeVelocityDispersion = 0.0d0
+ toleranceRelativeVelocityDispersionMaximum = 0.0d0
do while (associated(massDistribution_))
! Dimensionless mass distributions are not allowed.
- if (massDistribution_%massDistribution_%isDimensionless()) &
- & call Error_Report('dimensionless mass distributions can not be part of a composite distribution'//{introspection:location})
+ if (massDistribution_%massDistribution_%isDimensionless()) &
+ & call Error_Report('dimensionless mass distribution (type: '//char(massDistribution_%massDistribution_%objectType())//') can not be part of a composite distribution'//{introspection:location})
! Determine the symmetry of the composite distribution.
symmetry_=massDistribution_%massDistribution_%symmetry()
select case (symmetry_%ID)
@@ -179,9 +210,42 @@ subroutine compositeInitialize(self)
case default
call Error_Report('unknown symmetry'//{introspection:location})
end select
+ ! Record if we have multiple components.
+ if (firstComponent) then
+ firstComponent=.false.
+ else
+ self%isSingleComponent=.false.
+ end if
+ ! Check for collisional components.
+ if (associated(massDistribution_%massDistribution_%kinematicsDistribution_)) then
+ if (massDistribution_%massDistribution_%kinematicsDistribution_%isCollisional()) then
+ self%isCollisionless=.false.
+ else
+ toleranceRelativeVelocityDispersion =max(toleranceRelativeVelocityDispersion,massDistribution_%massDistribution_%kinematicsDistribution_%toleranceRelativeVelocityDispersion )
+ toleranceRelativeVelocityDispersionMaximum=max(toleranceRelativeVelocityDispersion,massDistribution_%massDistribution_%kinematicsDistribution_%toleranceRelativeVelocityDispersionMaximum)
+ end if
+ else
+ haveKinematics=.false.
+ end if
! Move to the next mass distribution.
massDistribution_ => massDistribution_%next
end do
+ ! Establish a kinematics distribution.
+ if (haveKinematics.and.self%isSingleComponent) then
+ ! For a single component, simply use the kinematics distribution from that component
+ call self%setKinematicsDistribution(self%massDistributions%massDistribution_%kinematicsDistribution_)
+ else
+ ! Construct a collisionless mass distribution. Note that we build a collisionless distribution here even if some
+ ! component is collisional. A better approach might be to find a self-consistent solution for collisional components
+ ! assuming hydrostatic equilibrium.
+ allocate(kinematicsDistributionCollisionless :: self%kinematicsDistribution_)
+ select type (kinematicsDistribution_ => self%kinematicsDistribution_)
+ type is (kinematicsDistributionCollisionless)
+ !![
+
+ !!]
+ end select
+ end if
end if
return
end subroutine compositeInitialize
@@ -198,6 +262,17 @@ function compositeSymmetry(self) result(symmetry)
return
end function compositeSymmetry
+ logical function compositeIsSphericallySymmetric(self) result(isSphericallySymmetric)
+ !!{
+ Return true if the distribution is spherically symmetric.
+ !!}
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+
+ isSphericallySymmetric=self%symmetry_ == massDistributionSymmetrySpherical
+ return
+ end function compositeIsSphericallySymmetric
+
logical function compositeIsDimensionless(self)
!!{
Return the dimensionless nature of a composite mass distribution.
@@ -210,7 +285,31 @@ logical function compositeIsDimensionless(self)
return
end function compositeIsDimensionless
- double precision function compositeMassTotal(self,componentType,massType)
+ logical function compositeAssumeMonotonicDecreasingSurfaceDensity(self) result(isSphericallySymmetric)
+ !!{
+ Return true if the surface density is monotonically decreasing.
+ !!}
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+ type (massDistributionList ), pointer :: massDistribution_
+
+ isSphericallySymmetric=.true.
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ ! If any of the components does not have a monotonically decreasing surface density, report that the total distribution
+ ! also does not. This is overly conservative.
+ if (.not.massDistribution_ %massDistribution_%assumeMonotonicDecreasingSurfaceDensity()) then
+ isSphericallySymmetric=.false.
+ exit
+ end if
+ massDistribution_ => massDistribution_%next
+ end do
+ end if
+ return
+ end function compositeAssumeMonotonicDecreasingSurfaceDensity
+
+ logical function compositeMatches(self,componentType,massType)
!!{
Return the total mass of a composite mass distribution.
!!}
@@ -219,98 +318,308 @@ double precision function compositeMassTotal(self,componentType,massType)
type (enumerationComponentTypeType), intent(in ), optional :: componentType
type (enumerationMassTypeType ), intent(in ), optional :: massType
type (massDistributionList ), pointer :: massDistribution_
+ !![
+
+
+ !!]
- compositeMassTotal=0.0d0
+ ! Assume no match by default.
+ compositeMatches=.false.
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeMassTotal = +compositeMassTotal &
- & +massDistribution_ %massDistribution_%massTotal()
- massDistribution_ => massDistribution_ %next
+ ! If any component matches, report that as a match.
+ if (massDistribution_ %massDistribution_%matches(componentType,massType)) then
+ compositeMatches=.true.
+ exit
+ end if
+ massDistribution_ => massDistribution_%next
end do
end if
return
- end function compositeMassTotal
+ end function compositeMatches
- double precision function compositeDensity(self,coordinates,componentType,massType)
+ subroutine compositeDescribe(self)
!!{
- Return the density at the specified {\normalfont \ttfamily coordinates} in a composite mass distribution.
+ Display a description of a composite mass distribution.
+ !!}
+ use :: Display, only : displayMessage, displayIndent, displayUnindent
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+ type (massDistributionList ), pointer :: massDistribution_
+
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ select type (massDistribution__ => massDistribution_ %massDistribution_)
+ class is (massDistributionComposite)
+ call displayIndent (massDistribution_%massDistribution_%objectType())
+ call massDistribution__%describe()
+ call displayUnindent("")
+ class default
+ call displayMessage(massDistribution_%massDistribution_%objectType())
+ end select
+ massDistribution_ => massDistribution_%next
+ end do
+ end if
+ return
+ end subroutine compositeDescribe
+
+ function compositeSubset(self,componentType,massType) result(subset)
+ !!{
+ Return the subset of the composite distribution that matches.
!!}
implicit none
+ class(massDistributionClass ), pointer :: subset
class(massDistributionComposite ), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
type (enumerationComponentTypeType), intent(in ), optional :: componentType
type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ class(massDistributionClass ), pointer :: massDistribution___
+ type (massDistributionList ), pointer :: subsetHead , subsetNext , &
+ & compositesHead , compositesNext, &
+ & massDistribution_
+ integer :: countComponents
+ !![
+
+
+ !!]
+
+ subset => null()
+ if (associated(self%massDistributions)) then
+ subsetHead => null()
+ subsetNext => null()
+ compositesHead => null()
+ massDistribution_ => self%massDistributions
+ countComponents = 0
+ do while (associated(massDistribution_))
+ select type (massDistribution__ => massDistribution_ %massDistribution_)
+ class is (massDistributionComposite)
+ massDistribution___ => massDistribution__%subset(componentType_,massType_)
+ if (associated(massDistribution___)) then
+ countComponents=countComponents+1
+ if (associated(subsetHead)) then
+ allocate(subsetNext%next)
+ subsetNext => subsetNext%next
+ else
+ allocate(subsetHead )
+ subsetNext => subsetHead
+ end if
+ subsetNext%massDistribution_ => massDistribution___
+ if (associated(compositesHead)) then
+ allocate(compositesNext%next)
+ compositesNext => compositesNext%next
+ else
+ allocate(compositesHead )
+ compositesNext => compositesHead
+ end if
+ compositesNext%massDistribution_ => massDistribution___
+ end if
+ class default
+ if (massDistribution__%matches(componentType,massType)) then
+ countComponents=countComponents+1
+ if (associated(subsetHead)) then
+ allocate(subsetNext%next)
+ subsetNext => subsetNext%next
+ else
+ allocate(subsetHead )
+ subsetNext => subsetHead
+ end if
+ subsetNext%massDistribution_ => massDistribution__
+ end if
+ end select
+ massDistribution_ => massDistribution_%next
+ end do
+ if (associated(subsetHead)) then
+ if (countComponents == 1) then
+ !![
+
+ !!]
+ deallocate(subsetHead)
+ else
+ allocate(massDistributionComposite :: subset)
+ select type(subset)
+ type is (massDistributionComposite)
+ !![
+
+ !!]
+ end select
+ end if
+ end if
+ do while (associated(compositesHead))
+ compositesNext => compositesHead%next
+ !![
+
+ !!]
+ deallocate(compositesHead)
+ compositesHead => compositesNext
+ end do
+ end if
+ return
+ end function compositeSubset
+
+ double precision function compositeMassTotal(self)
+ !!{
+ Return the total mass of a composite mass distribution.
+ !!}
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+ type (massDistributionList ), pointer :: massDistribution_
+
+ compositeMassTotal=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeMassTotal = +compositeMassTotal &
+ & +massDistribution_ %massDistribution_%massTotal()
+ massDistribution_ => massDistribution_ %next
+ end do
+ end if
+ return
+ end function compositeMassTotal
+
+ double precision function compositeDensity(self,coordinates)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a composite mass distribution.
+ !!}
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (massDistributionList ), pointer :: massDistribution_
compositeDensity=0.0d0
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeDensity = +compositeDensity &
- & +massDistribution_ %massDistribution_%density(coordinates)
- massDistribution_ => massDistribution_ %next
+ compositeDensity = +compositeDensity &
+ & +massDistribution_ %massDistribution_%density(coordinates)
+ massDistribution_ => massDistribution_ %next
end do
end if
return
end function compositeDensity
- double precision function compositeDensityGradientRadial(self,coordinates,logarithmic,componentType,massType)
+ double precision function compositeDensitySphericalAverage(self,radius)
+ !!{
+ Return the spherically-averaged density at the specified {\normalfont \ttfamily radius} in a composite mass distribution.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (massDistributionList ), pointer :: massDistribution_
+
+ compositeDensitySphericalAverage=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeDensitySphericalAverage = +compositeDensitySphericalAverage &
+ & +massDistribution_ %massDistribution_%densitySphericalAverage(radius)
+ massDistribution_ => massDistribution_ %next
+ end do
+ end if
+ return
+ end function compositeDensitySphericalAverage
+
+ double precision function compositeDensitySquareIntegral(self,radiusMinimum,radiusMaximum,isInfinite)
+ !!{
+ Return the integral of the square of the density within the given radial interval.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionComposite), intent(inout) :: self
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ if (self%isSingleComponent) then
+ compositeDensitySquareIntegral=self%massDistributions%massDistribution_%densitySquareIntegral(radiusMinimum,radiusMaximum)
+ else
+ compositeDensitySquareIntegral=0.0d0
+ call Error_Report('support for ∫ dr ρ²(r) of multiple components is not implemented'//{introspection:location})
+ end if
+ return
+ end function compositeDensitySquareIntegral
+
+ double precision function compositeSurfaceDensity(self,coordinates)
+ !!{
+ Return the surface density at the specified {\normalfont \ttfamily coordinates} in a composite mass distribution.
+ !!}
+ use :: Coordinates, only : coordinate
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (massDistributionList ), pointer :: massDistribution_
+
+
+ compositeSurfaceDensity=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeSurfaceDensity = +compositeSurfaceDensity &
+ & +massDistribution_ %massDistribution_%surfaceDensity(coordinates)
+ massDistribution_ => massDistribution_ %next
+ end do
+ end if
+ return
+ end function compositeSurfaceDensity
+
+ double precision function compositeDensityGradientRadial(self,coordinates,logarithmic)
!!{
Return the radial density gradient at the specified {\normalfont \ttfamily coordinates} in a composite mass distribution.
!!}
+ use :: Error, only : Error_Report
implicit none
- class (massDistributionComposite ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- logical , intent(in ), optional :: logarithmic
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ class (massDistributionComposite), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ type (massDistributionList ), pointer :: massDistribution_
+ double precision :: density
!![
!!]
- compositeDensityGradientRadial=0.0d0
- if (associated(self%massDistributions)) then
- massDistribution_ => self%massDistributions
- do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeDensityGradientRadial = +compositeDensityGradientRadial &
- & +massDistribution_ %massDistribution_%densityGradientRadial(coordinates,logarithmic=.false.)
- massDistribution_ => massDistribution_ %next
- end do
- if (logarithmic_) then
- compositeDensityGradientRadial=+compositeDensityGradientRadial &
- & *coordinates %rSpherical( ) &
- & /self %density (coordinates)
+ if (self%isSingleComponent) then
+ compositeDensityGradientRadial=self%massDistributions%massDistribution_%densityGradientRadial(coordinates,logarithmic)
+ else
+ compositeDensityGradientRadial=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeDensityGradientRadial = +compositeDensityGradientRadial &
+ & +massDistribution_ %massDistribution_%densityGradientRadial(coordinates,logarithmic=.false.)
+ massDistribution_ => massDistribution_ %next
+ end do
+ if (logarithmic_) then
+ density=self%density(coordinates)
+ if (density <= 0.0d0) then
+ if (compositeDensityGradientRadial /= 0.0d0) call Error_Report('non-zero gradient, but zero density'//{introspection:location})
+ else
+ compositeDensityGradientRadial=+compositeDensityGradientRadial &
+ & *coordinates %rSpherical() &
+ & / density
+ end if
+ end if
end if
end if
return
end function compositeDensityGradientRadial
- double precision function compositeDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function compositeDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Return the given radial density moment in a composite mass distribution.
!!}
implicit none
- class (massDistributionComposite ), intent(inout) :: self
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
- logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ class (massDistributionComposite), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ type (massDistributionList ), pointer :: massDistribution_
compositeDensityRadialMoment=0.0d0
if (present(isInfinite)) isInfinite=.false.
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeDensityRadialMoment = +compositeDensityRadialMoment &
- & +massDistribution_ %massDistribution_%densityRadialMoment(moment,radiusMinimum,radiusMaximum,isInfinite)
+ compositeDensityRadialMoment = +compositeDensityRadialMoment &
+ & +massDistribution_ %massDistribution_%densityRadialMoment(moment,radiusMinimum,radiusMaximum,isInfinite)
if (present(isInfinite)) then
if (isInfinite) return
end if
@@ -320,116 +629,281 @@ double precision function compositeDensityRadialMoment(self,moment,radiusMinimum
return
end function compositeDensityRadialMoment
- double precision function compositeMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function compositeMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere in a composite mass distribution.
!!}
implicit none
- class (massDistributionComposite ), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ) , pointer :: massDistribution_
+ class (massDistributionComposite), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ type (massDistributionList ) , pointer :: massDistribution_
compositeMassEnclosedBySphere=0.0d0
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeMassEnclosedBySphere = +compositeMassEnclosedBySphere &
- & +massDistribution_ %massDistribution_%massEnclosedBySphere(radius)
- massDistribution_ => massDistribution_ %next
+ compositeMassEnclosedBySphere = +compositeMassEnclosedBySphere &
+ & +massDistribution_ %massDistribution_%massEnclosedBySphere(radius)
+ massDistribution_ => massDistribution_ %next
end do
end if
return
end function compositeMassEnclosedBySphere
- function compositeAcceleration(self,coordinates,componentType,massType)
+ double precision function compositeRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for composite mass distributions.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass, massFractional
+
+ if (self%isSingleComponent) then
+ radius=self%massDistributions%massDistribution_%radiusEnclosingMass (mass,massFractional)
+ else
+ radius=self %radiusEnclosingMassNumerical(mass,massFractional)
+ end if
+ return
+ end function compositeRadiusEnclosingMass
+
+ double precision function compositeRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for composite mass distributions.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (self%isSingleComponent) then
+ radius=self%massDistributions%massDistribution_%radiusEnclosingDensity (density,radiusGuess)
+ else
+ radius=self %radiusEnclosingDensityNumerical(density,radiusGuess)
+ end if
+ return
+ end function compositeRadiusEnclosingDensity
+
+ double precision function compositeRadiusEnclosingSurfaceDensity(self,densitySurface,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given surface density for composite mass distributions.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout), target :: self
+ double precision , intent(in ) :: densitySurface
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (self%isSingleComponent) then
+ radius=self%massDistributions%massDistribution_%radiusEnclosingSurfaceDensity (densitySurface,radiusGuess)
+ else
+ radius=self %radiusEnclosingSurfaceDensityNumerical(densitySurface,radiusGuess)
+ end if
+ return
+ end function compositeRadiusEnclosingSurfaceDensity
+
+ double precision function compositeRotationCurve(self,radius)
+ !!{
+ Return the rotation curve for a composite mass distribution.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (massDistributionList ), pointer :: massDistribution_
+
+ compositeRotationCurve=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeRotationCurve = +compositeRotationCurve &
+ & +massDistribution_ %massDistribution_%rotationCurve(radius)**2
+ massDistribution_ => massDistribution_ %next
+ end do
+ compositeRotationCurve=sqrt(compositeRotationCurve)
+ end if
+ return
+ end function compositeRotationCurve
+
+ double precision function compositeRotationCurveGradient(self,radius)
+ !!{
+ Return the gradient of the rotation curve for a composite mass distribution.
+ !!}
+ implicit none
+ class (massDistributionComposite), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (massDistributionList ), pointer :: massDistribution_
+
+ compositeRotationCurveGradient=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeRotationCurveGradient = +compositeRotationCurveGradient &
+ & +massDistribution_ %massDistribution_%rotationCurveGradient(radius)
+ massDistribution_ => massDistribution_ %next
+ end do
+ end if
+ return
+ end function compositeRotationCurveGradient
+
+ function compositeAcceleration(self,coordinates)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for a composite mass distribution.
!!}
implicit none
- double precision , dimension(3 ) :: compositeAcceleration
- class (massDistributionComposite ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ double precision , dimension(3 ) :: compositeAcceleration
+ class (massDistributionComposite ), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (massDistributionList ), pointer :: massDistribution_
compositeAcceleration=[0.0d0,0.0d0,0.0d0]
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeAcceleration = +compositeAcceleration &
- & +massDistribution_ %massDistribution_%acceleration(coordinates)
- massDistribution_ => massDistribution_ %next
+ compositeAcceleration = +compositeAcceleration &
+ & +massDistribution_ %massDistribution_%acceleration(coordinates)
+ massDistribution_ => massDistribution_ %next
end do
end if
return
end function compositeAcceleration
- function compositeTidalTensor(self,coordinates,componentType,massType)
+ function compositeTidalTensor(self,coordinates)
!!{
Computes the gravitational tidal tensor at {\normalfont \ttfamily coordinates} for exponential disk mass distributions.
!!}
implicit none
- type (tensorRank2Dimension3Symmetric) :: compositeTidalTensor
- class(massDistributionComposite ), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ type (tensorRank2Dimension3Symmetric) :: compositeTidalTensor
+ class(massDistributionComposite ), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (massDistributionList ), pointer :: massDistribution_
compositeTidalTensor=tensorRank2Dimension3Symmetric()
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositeTidalTensor = +compositeTidalTensor &
- & +massDistribution_ %massDistribution_%tidalTensor(coordinates)
- massDistribution_ => massDistribution_ %next
+ compositeTidalTensor = +compositeTidalTensor &
+ & +massDistribution_ %massDistribution_%tidalTensor(coordinates)
+ massDistribution_ => massDistribution_ %next
end do
end if
return
end function compositeTidalTensor
- double precision function compositePotential(self,coordinates,componentType,massType)
+ logical function compositePotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Specify whether the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionComposite), intent(inout) :: self
+
+ isAnalytic=.false.
+ return
+ end function compositePotentialIsAnalytic
+
+ double precision function compositePotential(self,coordinates,status)
!!{
Return the gravitational potential for a composite mass distribution.
!!}
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
implicit none
- class(massDistributionComposite ), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
+ class(massDistributionComposite ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (massDistributionList ), pointer :: massDistribution_
+ if (present(status)) status=structureErrorCodeSuccess
compositePotential=0.0d0
if (associated(self%massDistributions)) then
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & compositePotential = +compositePotential &
- & +massDistribution_ %massDistribution_%potential(coordinates)
- massDistribution_ => massDistribution_ %next
+ compositePotential = +compositePotential &
+ & +massDistribution_ %massDistribution_%potential(coordinates,status)
+ if (present(status).and.status /= structureErrorCodeSuccess) return
+ massDistribution_ => massDistribution_ %next
end do
end if
return
end function compositePotential
+
+ double precision function compositePotentialDifference(self,coordinates1,coordinates2,status) result(potential)
+ !!{
+ Return the gravitational potential for a composite mass distribution.
+ !!}
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
+ implicit none
+ class(massDistributionComposite ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates1 , coordinates2
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (massDistributionList ), pointer :: massDistribution_
+
+ if (present(status)) status=structureErrorCodeSuccess
+ potential=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ potential=+potential &
+ & +massDistribution_%massDistribution_%potentialDifference(coordinates1,coordinates2,status)
+ if (present(status).and.status /= structureErrorCodeSuccess) return
+ massDistribution_ => massDistribution_%next
+ end do
+ end if
+ return
+ end function compositePotentialDifference
+
+ double precision function compositeEnergy(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the energy of the mass distribution.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionComposite), intent(inout), target :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout), target :: massDistributionEmbedding
+ class (massDistributionClass ) , pointer :: self_
+
+ self_ => self
+ if (self%isSingleComponent .and. associated(self_,massDistributionEmbedding)) then
+ energy=self%massDistributions%massDistribution_%energy(radiusOuter,self%massDistributions%massDistribution_)
+ else
+ energy=0.0d0
+ call Error_Report('support for energy of multiple components is not implemented'//{introspection:location})
+ end if
+ return
+ end function compositeEnergy
- function compositePositionSample(self,randomNumberGenerator_,componentType,massType)
+ function compositeChandrasekharIntegral(self,massDistributionEmbedding,massDistributionPerturber,massPerturber,coordinates,velocity)
+ !!{
+ Compute the Chandrasekhar integral at the specified {\normalfont \ttfamily coordinates} in a composite mass distribution.
+ !!}
+ implicit none
+ double precision , dimension(3) :: compositeChandrasekharIntegral
+ class (massDistributionComposite ), intent(inout) :: self
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding , massDistributionPerturber
+ double precision , intent(in ) :: massPerturber
+ class (coordinate ), intent(in ) :: coordinates , velocity
+ type (massDistributionList ), pointer :: massDistribution_
+ !$GLC attributes unused :: massDistributionEmbedding
+
+ compositeChandrasekharIntegral=0.0d0
+ if (associated(self%massDistributions)) then
+ massDistribution_ => self%massDistributions
+ do while (associated(massDistribution_))
+ compositeChandrasekharIntegral = +compositeChandrasekharIntegral &
+ & +massDistribution_%massDistribution_%chandrasekharIntegral(massDistribution_%massDistribution_,massDistributionPerturber,massPerturber,coordinates,velocity)
+ massDistribution_ => massDistribution_%next
+ end do
+ end if
+ return
+ end function compositeChandrasekharIntegral
+
+ function compositePositionSample(self,randomNumberGenerator_)
!!{
Sample a position from a composite distribution.
!!}
implicit none
- double precision , dimension(3) :: compositePositionSample
- class (massDistributionComposite ), intent(inout) :: self
- class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (massDistributionList ), pointer :: massDistribution_
- double precision :: massCumulative
+ double precision , dimension(3) :: compositePositionSample
+ class (massDistributionComposite ), intent(inout) :: self
+ class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
+ type (massDistributionList ), pointer :: massDistribution_
+ double precision :: massCumulative
compositePositionSample=[0.0d0,0.0d0,0.0d0]
if (associated(self%massDistributions)) then
@@ -437,14 +911,13 @@ function compositePositionSample(self,randomNumberGenerator_,componentType,massT
& *randomNumberGenerator_%uniformSample()
massDistribution_ => self%massDistributions
do while (associated(massDistribution_))
- if (massDistribution_ %massDistribution_%matches(componentType,massType)) &
- & massCumulative=+ massCumulative &
- & -massDistribution_%massDistribution_%massTotal ()
+ massCumulative=+ massCumulative &
+ & -massDistribution_%massDistribution_%massTotal ()
if (massCumulative <= 0.0d0) then
compositePositionSample=massDistribution_%massDistribution_%positionSample(randomNumberGenerator_)
return
end if
- massDistribution_ => massDistribution_ %next
+ massDistribution_ => massDistribution_%next
end do
end if
diff --git a/source/mass_distributions.cylindrical.F90 b/source/mass_distributions.cylindrical.F90
index 566d974454..ec496368b1 100644
--- a/source/mass_distributions.cylindrical.F90
+++ b/source/mass_distributions.cylindrical.F90
@@ -34,92 +34,25 @@
contains
!![
-
-
-
-
-
-
+
!!]
- procedure :: symmetry => cylindricalSymmetry
- procedure(cylindricalRadiusHalfMass ), deferred :: radiusHalfMass
- procedure(cylindricalSurfaceDensity ), deferred :: surfaceDensity
- procedure(cylindricalRotationCurve ), deferred :: rotationCurve
- procedure(cylindricalRotationCurveGradient ), deferred :: rotationCurveGradient
- procedure(cylindricalSurfaceDensityRadialMoment), deferred :: surfaceDensityRadialMoment
- procedure(cylindricalDensitySphericalAverage ), deferred :: densitySphericalAverage
+ procedure :: symmetry => cylindricalSymmetry
+ procedure :: chandrasekharIntegral => cylindricalChandrasekharIntegral
+ procedure :: densityRadialMoment => cylindricalDensityRadialMoment
+ procedure(cylindricalRadiusHalfMass), deferred :: radiusHalfMass
end type massDistributionCylindrical
abstract interface
- double precision function cylindricalRadiusHalfMass(self,componentType,massType)
+ double precision function cylindricalRadiusHalfMass(self)
!!{
Interface for cylindrically symmetric mass distribution half mass radii functions.
!!}
- import massDistributionCylindrical, enumerationComponentTypeType, enumerationMassTypeType
- class(massDistributionCylindrical ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ import massDistributionCylindrical
+ class(massDistributionCylindrical), intent(inout) :: self
end function cylindricalRadiusHalfMass
- double precision function cylindricalSurfaceDensity(self,coordinates,componentType,massType)
- !!{
- Interface for cylindrically symmetric mass distribution surface density functions.
- !!}
- import massDistributionCylindrical, coordinate, enumerationComponentTypeType, enumerationMassTypeType
- class(massDistributionCylindrical ), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- end function cylindricalSurfaceDensity
-
- double precision function cylindricalRotationCurve(self,radius,componentType,massType)
- !!{
- Interface for cylindrically symmetric mass distribution rotation curve functions.
- !!}
- import massDistributionCylindrical, enumerationComponentTypeType, enumerationMassTypeType
- class (massDistributionCylindrical ), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- end function cylindricalRotationCurve
-
- double precision function cylindricalRotationCurveGradient(self,radius,componentType,massType)
- !!{
- Interface for cylindrically symmetric mass distribution rotation curve gradient functions.
- !!}
- import massDistributionCylindrical, enumerationComponentTypeType, enumerationMassTypeType
- class (massDistributionCylindrical ), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- end function cylindricalRotationCurveGradient
-
- double precision function cylindricalSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
- !!{
- Interface for cylindrically symmetric mass distribution surface density radial moment functions.
- !!}
- import massDistributionCylindrical, enumerationComponentTypeType, enumerationMassTypeType
- class (massDistributionCylindrical ), intent(inout) :: self
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
- logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- end function cylindricalSurfaceDensityRadialMoment
-
- double precision function cylindricalDensitySphericalAverage(self,radius,componentType,massType)
- !!{
- Interface for cylindrically symmetric mass distribution spherically-averaged density functions.
- !!}
- import massDistributionCylindrical, enumerationComponentTypeType, enumerationMassTypeType
- class (massDistributionCylindrical ), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- end function cylindricalDensitySphericalAverage
-
end interface
contains
@@ -136,3 +69,144 @@ function cylindricalSymmetry(self)
cylindricalSymmetry=massDistributionSymmetryCylindrical
return
end function cylindricalSymmetry
+
+ function cylindricalChandrasekharIntegral(self,massDistributionEmbedding,massDistributionPerturber,massPerturber,coordinates,velocity)
+ !!{
+ Compute the Chandrasekhar integral at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution.
+ !!}
+ use :: Coordinates , only : coordinateCartesian , coordinateSpherical, coordinateCylindrical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll , enumerationComponentTypeType, enumerationMassTypeType
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Linear_Algebra , only : vector , matrix , assignment(=)
+ implicit none
+ double precision , dimension(3) :: cylindricalChandrasekharIntegral
+ class (massDistributionCylindrical ), intent(inout) :: self
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding , massDistributionPerturber
+ double precision , intent(in ) :: massPerturber
+ class (coordinate ), intent(in ) :: coordinates , velocity
+ double precision , dimension(3) :: velocityCartesian_
+ double precision , parameter :: toomreQRadiusHalfMass =1.50d0 ! The Toomre Q-parameter at the disk half-mass radius (Benson et al.,
+ ! 2004 , https://ui.adsabs.harvard.edu/abs/2004MNRAS.351.1215B, Appendix A).
+ double precision , parameter :: toomreQFactor =3.36d0 ! The factor appearing in the definition of the Toomre Q-parameter for
+ ! a stellar disk (Binney & Tremaine, eqn. 6.71).
+ double precision , dimension(3) :: velocityDisk , velocityRelative , &
+ & positionCartesianMidplane , &
+ & positionCylindricalHalfMass , positionCartesian
+ type (massDistributionGaussianEllipsoid), save :: velocityDistribution
+ logical , save :: velocityDistributionInitialized =.false.
+ !$omp threadprivate(velocityDistribution,velocityDistributionInitialized)
+ type (coordinateCartesian ) :: coordinates_ , coordinatesMidplane , &
+ & coordinatesMidplaneHalfMass , velocityCartesian
+ double precision :: velocityDispersionRadial , velocityDispersionAzimuthal , &
+ & velocityDispersionVertical , velocityCircular , &
+ & velocityCircularHalfMassRadius , velocityCircularSquaredGradient , &
+ & velocityCircularSquaredGradientHalfMassRadius , density , &
+ & densityMidPlane , densitySurface , &
+ & heightScale , radiusMidplane , &
+ & frequencyCircular , frequencyEpicyclic , &
+ & frequencyCircularHalfMassRadius , frequencyEpicyclicHalfMassRadius, &
+ & densitySurfaceRadiusHalfMass , velocityDispersionRadialHalfMass, &
+ & velocityDispersionMaximum , velocityRelativeMagnitude , &
+ & factorSuppressionExtendedMass
+ type (matrix ) :: rotation
+
+ coordinates_ = coordinates
+ positionCartesian = coordinates_
+ positionCartesianMidplane = [ positionCartesian(1),positionCartesian(2),0.0d0]
+ positionCylindricalHalfMass = [self%radiusHalfMass ( ),0.0d0 ,0.0d0]
+ coordinatesMidplane = positionCartesianMidplane
+ coordinatesMidplaneHalfMass = positionCylindricalHalfMass
+ radiusMidplane = coordinatesMidplane%rSpherical()
+ velocityCircular = massDistributionEmbedding%rotationCurve ( radiusMidplane )
+ velocityCircularSquaredGradient = massDistributionEmbedding%rotationCurveGradient( radiusMidplane )
+ velocityCircularHalfMassRadius = massDistributionEmbedding%rotationCurve (self%radiusHalfMass())
+ velocityCircularSquaredGradientHalfMassRadius = massDistributionEmbedding%rotationCurveGradient(self%radiusHalfMass())
+ velocityDisk = +[positionCartesianMidplane(2),-positionCartesianMidplane(1),0.0d0] &
+ & /radiusMidplane &
+ & *velocityCircular
+ ! Compute epicyclic frequency.
+ frequencyCircular =velocityCircular / radiusMidplane
+ frequencyCircularHalfMassRadius =velocityCircularHalfMassRadius/self%radiusHalfMass()
+ frequencyEpicyclic =sqrt(velocityCircularSquaredGradient / radiusMidplane +2.0d0*frequencyCircular **2)
+ frequencyEpicyclicHalfMassRadius=sqrt(velocityCircularSquaredGradientHalfMassRadius/self%radiusHalfMass()+2.0d0*frequencyCircularHalfMassRadius**2)
+ ! Get disk structural properties.
+ density =+self%density (coordinates )
+ densityMidPlane =+self%density (coordinatesMidplane )
+ densitySurface =+self%surfaceDensity(coordinatesMidplane )
+ densitySurfaceRadiusHalfMass=+self%surfaceDensity(coordinatesMidplaneHalfMass)
+ if (density <= 0.0d0) then
+ cylindricalChandrasekharIntegral=0.0d0
+ return
+ end if
+ heightScale =+0.5d0 &
+ & *densitySurface &
+ & /densityMidPlane
+ ! Compute normalization of the radial velocity dispersion.
+ velocityDispersionRadialHalfMass=+toomreQFactor &
+ & *gravitationalConstantGalacticus &
+ & *densitySurfaceRadiusHalfMass &
+ & *toomreQRadiusHalfMass &
+ & /frequencyEpicyclicHalfMassRadius
+ ! Find the velocity dispersion components of the disk.
+ velocityDispersionRadial =+velocityDispersionRadialHalfMass &
+ & *sqrt( &
+ & +densitySurface &
+ & /densitySurfaceRadiusHalfMass &
+ & )
+ velocityDispersionAzimuthal=+velocityDispersionRadial*frequencyEpicyclic/2.0d0/frequencyCircular
+ velocityDispersionVertical =+sqrt(Pi*gravitationalConstantGalacticus*densitySurface*heightScale)
+ velocityDispersionMaximum =+maxval([velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical])
+ velocityDispersionRadial =+velocityDispersionRadial /velocityDispersionMaximum
+ velocityDispersionAzimuthal=+velocityDispersionAzimuthal/velocityDispersionMaximum
+ velocityDispersionVertical =+velocityDispersionVertical /velocityDispersionMaximum
+ if (any([velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical] <= 0.0d0)) return
+ ! Find the relative velocity of the perturber and the disk.
+ velocityCartesian = velocity
+ velocityCartesian_ = velocityCartesian
+ velocityRelative =(velocityCartesian_-velocityDisk)/velocityDispersionMaximum
+ ! Handle limiting case of large relative velocity.
+ velocityRelativeMagnitude =sqrt(sum(velocityRelative**2))
+ ! Initialize the velocity distribution.
+ rotation=reshape( &
+ & [ &
+ & +positionCartesianMidplane(1),-positionCartesianMidplane(2),+0.0d0 , &
+ & +positionCartesianMidplane(2),+positionCartesianMidplane(1),+0.0d0 , &
+ & +0.0d0 ,+0.0d0 ,+radiusMidplane &
+ & ] &
+ & /radiusMidplane , &
+ & [3,3] &
+ & )
+ coordinates_=velocityRelative
+ if (.not.velocityDistributionInitialized) then
+ velocityDistribution =massDistributionGaussianEllipsoid(scaleLength=[1.0d0,1.0d0,1.0d0],rotation=rotation,mass=1.0d0,dimensionless=.true.)
+ velocityDistributionInitialized=.true.
+ end if
+ call velocityDistribution%initialize(scaleLength=[velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical],rotation=rotation)
+ ! Compute suppression factor due to satellite being an extended mass distribution. This is largely untested - it is meant to
+ ! simply avoid extremely large accelerations for subhalo close to the disk plane when that subhalo is much more extended than
+ ! the disk.
+ factorSuppressionExtendedMass=min(1.0d0,massDistributionPerturber%massEnclosedBySphere(heightScale)/massPerturber)
+ ! Evaluate the integral.
+ cylindricalChandrasekharIntegral=+density &
+ & *velocityDistribution %acceleration(coordinates_) &
+ & /velocityDispersionMaximum **2 &
+ & *factorSuppressionExtendedMass
+ return
+ end function cylindricalChandrasekharIntegral
+
+ double precision function cylindricalDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
+ !!{
+ Computes radial moments of the density in cylindrical mass distributions.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionCylindrical), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ cylindricalDensityRadialMoment=0.0d0
+ call Error_Report('radial density moments are not defined in cylindrical mass distributions'//{introspection:location})
+ return
+ end function cylindricalDensityRadialMoment
diff --git a/source/mass_distributions.cylindrical.Gaussian_slab.F90 b/source/mass_distributions.cylindrical.Gaussian_slab.F90
index c10596ec28..87163dd3bb 100644
--- a/source/mass_distributions.cylindrical.Gaussian_slab.F90
+++ b/source/mass_distributions.cylindrical.Gaussian_slab.F90
@@ -150,24 +150,18 @@ function gaussianSlabConstructorInternal(scaleHeight,densityCentral,dimensionles
return
end function gaussianSlabConstructorInternal
- double precision function gaussianSlabDensity(self,coordinates,componentType,massType)
+ double precision function gaussianSlabDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a Gaussian slab mass distribution.
!!}
use :: Coordinates, only : assignment(=), coordinateCylindrical
use :: Error , only : Error_Report
implicit none
- class (massDistributionGaussianSlab), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCylindrical ) :: position
- double precision :: z
+ class (massDistributionGaussianSlab), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCylindrical ) :: position
+ double precision :: z
- if (.not.self%matches(componentType,massType)) then
- gaussianSlabDensity=0.0d0
- return
- end if
! If disk is razor thin, density is undefined.
if (self%scaleHeight <= 0.0d0) call Error_Report('density undefined for razor-thin slab'//{introspection:location})
! Get position in cylindrical coordinate system.
@@ -178,76 +172,64 @@ double precision function gaussianSlabDensity(self,coordinates,componentType,mas
return
end function gaussianSlabDensity
- double precision function gaussianSlabDensitySphericalAverage(self,radius,componentType,massType)
+ double precision function gaussianSlabDensitySphericalAverage(self,radius)
!!{
Return the spherically-averaged density at the specified {\normalfont \ttfamily radius} in a Gaussian slab mass distribution.
!!}
implicit none
- class (massDistributionGaussianSlab), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- !$GLC attributes unused :: self, radius, componentType, massType
+ class (massDistributionGaussianSlab), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
gaussianSlabDensitySphericalAverage=0.0d0
call Error_Report('spherically-averaged density profile is not implemented'//{introspection:location})
return
end function gaussianSlabDensitySphericalAverage
- double precision function gaussianSlabRotationCurve(self,radius,componentType,massType)
+ double precision function gaussianSlabRotationCurve(self,radius)
!!{
Rotation curve for a infinite extent Gaussian slab.
!!}
implicit none
- class (massDistributionGaussianSlab), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- !$GLC attributes unused :: self, radius, componentType, massType
+ class (massDistributionGaussianSlab), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
gaussianSlabRotationCurve=0.0d0
return
end function gaussianSlabRotationCurve
- double precision function gaussianSlabRotationCurveGradient(self,radius,componentType,massType)
+ double precision function gaussianSlabRotationCurveGradient(self,radius)
!!{
Rotation curve gradient for a infinite extent Gaussian slab.
!!}
implicit none
- class (massDistributionGaussianSlab), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- !$GLC attributes unused :: self, radius, componentType, massType
+ class (massDistributionGaussianSlab), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
gaussianSlabRotationCurveGradient=0.0d0
return
end function gaussianSlabRotationCurveGradient
- double precision function gaussianSlabSurfaceDensity(self,coordinates,componentType,massType)
+ double precision function gaussianSlabSurfaceDensity(self,coordinates)
!!{
Return the surface density at the specified {\normalfont \ttfamily coordinates} in a Gaussian slab mass distribution.
!!}
use :: Coordinates , only : coordinate
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionGaussianSlab), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionGaussianSlab), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
!$GLC attributes unused :: coordinates
- if (.not.self%matches(componentType,massType)) then
- gaussianSlabSurfaceDensity=0.0d0
- return
- end if
gaussianSlabSurfaceDensity=+sqrt(2.0d0*Pi) &
& *self%scaleHeight &
& *self%densityCentral
return
end function gaussianSlabSurfaceDensity
- double precision function gaussianSlabSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function gaussianSlabSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Compute radial moments of the Gaussian slab mass distribution surface density profile.
!!}
@@ -257,14 +239,8 @@ double precision function gaussianSlabSurfaceDensityRadialMoment(self,moment,rad
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
!$GLC attributes unused :: self, moment, radiusMinimum, radiusMaximum
- if (.not.self%matches(componentType,massType)) then
- gaussianSlabSurfaceDensityRadialMoment=0.0d0
- return
- end if
! All moments are infinite.
gaussianSlabSurfaceDensityRadialMoment=huge(0.0d0)
if (present(isInfinite)) then
@@ -275,16 +251,14 @@ double precision function gaussianSlabSurfaceDensityRadialMoment(self,moment,rad
return
end function gaussianSlabSurfaceDensityRadialMoment
- double precision function gaussianSlabRadiusHalfMass(self,componentType,massType)
+ double precision function gaussianSlabRadiusHalfMass(self)
!!{
Return the half-mass radius for an infinite extent Gaussian slab mass distribution.
!!}
use :: Error, only : Error_Report
implicit none
- class(massDistributionGaussianSlab), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- !$GLC attributes unused :: self, componentType, massType
+ class(massDistributionGaussianSlab), intent(inout) :: self
+ !$GLC attributes unused :: self
gaussianSlabRadiusHalfMass=0.0d0
call Error_Report('half mass radius is undefined'//{introspection:location})
diff --git a/source/mass_distributions.cylindrical.Miyamoto_Nagai.F90 b/source/mass_distributions.cylindrical.Miyamoto_Nagai.F90
index ec0ad3a963..8127eebae2 100644
--- a/source/mass_distributions.cylindrical.Miyamoto_Nagai.F90
+++ b/source/mass_distributions.cylindrical.Miyamoto_Nagai.F90
@@ -52,6 +52,7 @@
procedure :: surfaceDensityRadialMoment => miyamotoNagaiSurfaceDensityRadialMoment
procedure :: massEnclosedBySphere => miyamotoNagaiMassEnclosedBySphere
procedure :: massEnclosedTabulate => miyamotoNagaiMassEnclosedTabulate
+ procedure :: potentialIsAnalytic => miyamotoNagaiPotentialIsAnalytic
procedure :: potential => miyamotoNagaiPotential
procedure :: rotationCurve => miyamotoNagaiRotationCurve
procedure :: rotationCurveGradient => miyamotoNagaiRotationCurveGradient
@@ -189,23 +190,17 @@ function miyamotoNagaiConstructorInternal(a,b,mass,dimensionless,componentType,m
return
end function miyamotoNagaiConstructorInternal
- double precision function miyamotoNagaiDensity(self,coordinates,componentType,massType)
+ double precision function miyamotoNagaiDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in an \citep{miyamoto_three-dimensional_1975} disk mass distribution.
!!}
use :: Coordinates, only : assignment(=), coordinateCylindrical
implicit none
- class (massDistributionMiyamotoNagai), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCylindrical ) :: position
- double precision :: r , z
+ class (massDistributionMiyamotoNagai), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCylindrical ) :: position
+ double precision :: r , z
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiDensity=0.0d0
- return
- end if
! Get position in cylindrical coordinate system.
position=coordinates
! Compute density.
@@ -251,35 +246,27 @@ double precision function miyamotoNagaiDensity(self,coordinates,componentType,ma
return
end function miyamotoNagaiDensity
- double precision function miyamotoNagaiDensitySphericalAverage(self,radius,componentType,massType)
+ double precision function miyamotoNagaiDensitySphericalAverage(self,radius)
!!{
Return the spherically-averaged density at the specified {\normalfont \ttfamily radius} in an \citep{miyamoto_three-dimensional_1975} disk mass distribution.
!!}
implicit none
- class (massDistributionMiyamotoNagai), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
+ class (massDistributionMiyamotoNagai), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
miyamotoNagaiDensitySphericalAverage=0.0d0
call Error_Report('spherically-averaged density profile is not implemented'//{introspection:location})
return
end function miyamotoNagaiDensitySphericalAverage
- double precision function miyamotoNagaiMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function miyamotoNagaiMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for \citep{miyamoto_three-dimensional_1975} disk mass distributions.
!!}
implicit none
- class (massDistributionMiyamotoNagai), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiMassEnclosedBySphere=0.0d0
- return
- end if
+ class (massDistributionMiyamotoNagai), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
! Ensure mass enclosed profile is tabulated.
call self%massEnclosedTabulate()
! Evaluate the mass enclosed.
@@ -287,19 +274,13 @@ double precision function miyamotoNagaiMassEnclosedBySphere(self,radius,componen
return
end function miyamotoNagaiMassEnclosedBySphere
- double precision function miyamotoNagaiRadiusHalfMass(self,componentType,massType)
+ double precision function miyamotoNagaiRadiusHalfMass(self)
!!{
Return the half-mass radius in a Miyamoto-Nagai mass distribution.
!!}
implicit none
- class(massDistributionMiyamotoNagai), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionMiyamotoNagai), intent(inout) :: self
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiRadiusHalfMass=0.0d0
- return
- end if
! Ensure mass enclosed profile is tabulated.
call self%massEnclosedTabulate()
! Return the half-mass radius.
@@ -462,22 +443,16 @@ end function integrandSurfaceDensity
end subroutine miyamotoNagaiSurfaceDensityTabulate
- double precision function miyamotoNagaiSurfaceDensity(self,coordinates,componentType,massType)
+ double precision function miyamotoNagaiSurfaceDensity(self,coordinates)
!!{
Return the surface density at the specified {\normalfont \ttfamily coordinates} in a Miyamoto-Nagai mass distribution.
!!}
use :: Coordinates, only : assignment(=), coordinateCylindrical
implicit none
- class(massDistributionMiyamotoNagai), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (coordinateCylindrical ) :: position
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiSurfaceDensity=0.0d0
- return
- end if
+ class(massDistributionMiyamotoNagai), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (coordinateCylindrical ) :: position
+
! Ensure surface density profile is tabulated.
call self%surfaceDensityTabulate()
! Evaluate the surface density.
@@ -486,7 +461,7 @@ double precision function miyamotoNagaiSurfaceDensity(self,coordinates,component
return
end function miyamotoNagaiSurfaceDensity
- double precision function miyamotoNagaiSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function miyamotoNagaiSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Compute radial moments of the Miyamoto-Nagai mass distribution surface density profile.
!!}
@@ -497,15 +472,9 @@ double precision function miyamotoNagaiSurfaceDensityRadialMoment(self,moment,ra
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
procedure (tablesIntegrationWeightFunction), pointer :: integrandWeightFunction
double precision :: radiusMinimumActual , radiusMaximumActual
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiSurfaceDensityRadialMoment=0.0d0
- return
- end if
! Set infinity status.
if (present(isInfinite)) isInfinite=.false.
! Ensure surface density profile is tabulated.
@@ -570,22 +539,16 @@ end function momentWeight
end function miyamotoNagaiSurfaceDensityRadialMoment
- double precision function miyamotoNagaiRotationCurve(self,radius,componentType,massType)
+ double precision function miyamotoNagaiRotationCurve(self,radius)
!!{
Return the mid-plane rotation curve for a Miyamoto-Nagai mass distribution.
!!}
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionMiyamotoNagai), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r
+ class (massDistributionMiyamotoNagai), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiRotationCurve=0.0d0
- return
- end if
! Get dimensionless radius.
r=radius/self%a
! Evaluate the rotation curve.
@@ -608,22 +571,16 @@ double precision function miyamotoNagaiRotationCurve(self,radius,componentType,m
return
end function miyamotoNagaiRotationCurve
- double precision function miyamotoNagaiRotationCurveGradient(self,radius,componentType,massType)
+ double precision function miyamotoNagaiRotationCurveGradient(self,radius)
!!{
Return the mid-plane rotation curve gradient for an \citep{miyamoto_three-dimensional_1975} disk.
!!}
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionMiyamotoNagai), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r
+ class (massDistributionMiyamotoNagai), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiRotationCurveGradient=0.0d0
- return
- end if
! Get dimensionless radius.
r=radius/self%a
! Evaluate the rotation curve.
@@ -656,24 +613,32 @@ double precision function miyamotoNagaiRotationCurveGradient(self,radius,compone
return
end function miyamotoNagaiRotationCurveGradient
- double precision function miyamotoNagaiPotential(self,coordinates,componentType,massType)
+ logical function miyamotoNagaiPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionMiyamotoNagai), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function miyamotoNagaiPotentialIsAnalytic
+
+ double precision function miyamotoNagaiPotential(self,coordinates,status)
!!{
Return the gravitational potential for an \citep{miyamoto_three-dimensional_1975} disk.
!!}
use :: Coordinates , only : assignment(=) , coordinateCylindrical
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionMiyamotoNagai), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCylindrical ) :: position
- double precision :: r , z
+ class (massDistributionMiyamotoNagai ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateCylindrical ) :: position
+ double precision :: r , z
- if (.not.self%matches(componentType,massType)) then
- miyamotoNagaiPotential=0.0d0
- return
- end if
+ if (present(status)) status=structureErrorCodeSuccess
! Get position in cylindrical coordinate system.
position=coordinates
! Compute density.
diff --git a/source/mass_distributions.cylindrical.exponential_disk.F90 b/source/mass_distributions.cylindrical.exponential_disk.F90
index 97ceb28f8d..7faa662bf1 100644
--- a/source/mass_distributions.cylindrical.exponential_disk.F90
+++ b/source/mass_distributions.cylindrical.exponential_disk.F90
@@ -22,7 +22,7 @@
!!}
!$ use :: OMP_Lib, only : omp_lock_kind
- use :: Tables , only : table1DLogarithmicLinear
+ use :: Tables , only : table1DLogarithmicLinear
!![
@@ -67,25 +67,30 @@
!!]
- final :: exponentialDiskDestructor
- procedure :: tabulate => exponentialDiskTabulate
- procedure :: besselFactorRotationCurve => exponentialDiskBesselFactorRotationCurve
- procedure :: besselFactorRotationCurveGradient => exponentialDiskBesselFactorRotationCurveGradient
- procedure :: besselFactorPotential => exponentialDiskBesselFactorPotential
- procedure :: density => exponentialDiskDensity
- procedure :: densitySphericalAverage => exponentialDiskDensitySphericalAverage
- procedure :: surfaceDensity => exponentialDiskSurfaceDensity
- procedure :: massEnclosedBySphere => exponentialDiskMassEnclosedBySphere
- procedure :: potential => exponentialDiskPotential
- procedure :: rotationCurve => exponentialDiskRotationCurve
- procedure :: rotationCurveGradient => exponentialDiskRotationCurveGradient
- procedure :: radiusHalfMass => exponentialDiskRadiusHalfMass
- procedure :: surfaceDensityRadialMoment => exponentialDiskSurfaceDensityRadialMoment
- procedure :: acceleration => exponentialDiskAcceleration
- procedure :: tidalTensor => exponentialDiskTidalTensor
- procedure :: accelerationTabulate => exponentialDiskAccelerationTabulate
- procedure :: accelerationInterpolate => exponentialDiskAccelerationInterpolate
- procedure :: positionSample => exponentialDiskPositionSample
+ final :: exponentialDiskDestructor
+ procedure :: tabulate => exponentialDiskTabulate
+ procedure :: besselFactorRotationCurve => exponentialDiskBesselFactorRotationCurve
+ procedure :: besselFactorRotationCurveGradient => exponentialDiskBesselFactorRotationCurveGradient
+ procedure :: besselFactorPotential => exponentialDiskBesselFactorPotential
+ procedure :: assumeMonotonicDecreasingSurfaceDensity => exponentialDiskAssumeMonotonicDecreasingSurfaceDensity
+ procedure :: massTotal => exponentialDiskMassTotal
+ procedure :: density => exponentialDiskDensity
+ procedure :: densityGradientRadial => exponentialDensityGradientRadial
+ procedure :: densitySphericalAverage => exponentialDiskDensitySphericalAverage
+ procedure :: surfaceDensity => exponentialDiskSurfaceDensity
+ procedure :: massEnclosedBySphere => exponentialDiskMassEnclosedBySphere
+ procedure :: radiusEnclosingSurfaceDensity => exponentialDiskRadiusEnclosingSurfaceDensity
+ procedure :: potentialIsAnalytic => exponentialDiskPotentialIsAnalytic
+ procedure :: potential => exponentialDiskPotential
+ procedure :: rotationCurve => exponentialDiskRotationCurve
+ procedure :: rotationCurveGradient => exponentialDiskRotationCurveGradient
+ procedure :: radiusHalfMass => exponentialDiskRadiusHalfMass
+ procedure :: surfaceDensityRadialMoment => exponentialDiskSurfaceDensityRadialMoment
+ procedure :: acceleration => exponentialDiskAcceleration
+ procedure :: tidalTensor => exponentialDiskTidalTensor
+ procedure :: accelerationTabulate => exponentialDiskAccelerationTabulate
+ procedure :: accelerationInterpolate => exponentialDiskAccelerationInterpolate
+ procedure :: positionSample => exponentialDiskPositionSample
end type massDistributionExponentialDisk
interface massDistributionExponentialDisk
@@ -299,45 +304,55 @@ subroutine exponentialDiskTabulate(self)
return
end subroutine exponentialDiskTabulate
- double precision function exponentialDiskRadiusHalfMass(self,componentType,massType)
+ logical function exponentialDiskAssumeMonotonicDecreasingSurfaceDensity(self) result(assumeMonotonicDecreasingSurfaceDensity)
+ !!{
+ Return true indicating that this distribution has a monotonically-decreasing surface density.
+ !!}
+ implicit none
+ class(massDistributionExponentialDisk), intent(inout) :: self
+
+ assumeMonotonicDecreasingSurfaceDensity=.true.
+ return
+ end function exponentialDiskAssumeMonotonicDecreasingSurfaceDensity
+
+ double precision function exponentialDiskMassTotal(self)
+ !!{
+ Return the total mass in an exponential disk distribution.
+ !!}
+ implicit none
+ class(massDistributionExponentialDisk), intent(inout) :: self
+
+ exponentialDiskMassTotal=self%mass
+ return
+ end function exponentialDiskMassTotal
+
+ double precision function exponentialDiskRadiusHalfMass(self)
!!{
Return the half-mass radius in an exponential disk mass distribution.
!!}
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , parameter :: radiusHalfMassToScaleRadius=1.678346990d0
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ double precision , parameter :: radiusHalfMassToScaleRadius=1.678346990d0
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskRadiusHalfMass=0.0d0
- return
- end if
exponentialDiskRadiusHalfMass=+radiusHalfMassToScaleRadius &
& *self%scaleRadius
return
end function exponentialDiskRadiusHalfMass
- double precision function exponentialDiskDensity(self,coordinates,componentType,massType)
+ double precision function exponentialDiskDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in an exponential disk mass distribution.
!!}
use :: Coordinates, only : assignment(=), coordinateCylindrical
use :: Error , only : Error_Report
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCylindrical ) :: position
- double precision , parameter :: coshArgumentMaximum=50.0d0
- double precision :: r , z, &
- & coshTerm
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateCylindrical ) :: position
+ double precision , parameter :: coshArgumentMaximum=50.0d0
+ double precision :: r , z, &
+ & coshTerm
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskDensity=0.0d0
- return
- end if
! If disk is razor thin, density is undefined.
if (self%scaleHeight <= 0.0d0) call Error_Report('density undefined for razor-thin disk'//{introspection:location})
! Get position in cylindrical coordinate system.
@@ -354,21 +369,53 @@ double precision function exponentialDiskDensity(self,coordinates,componentType,
return
end function exponentialDiskDensity
- double precision function exponentialDiskDensitySphericalAverage(self,radius,componentType,massType)
+ double precision function exponentialDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density gradient in the radial direction in a scaled spherical mass distribution.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateCylindrical
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionExponentialDisk), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision , parameter :: coshArgumentMaximum=50.0d0
+ type (coordinateCylindrical ) :: position
+ double precision :: r , z, &
+ & coshTerm
+ !![
+
+ !!]
+
+ ! If disk is razor thin, density is undefined.
+ if (self%scaleHeight <= 0.0d0) call Error_Report('density undefined for razor-thin disk'//{introspection:location})
+ ! Get position in cylindrical coordinate system.
+ position=coordinates
+ ! Compute density.
+ r= position%r() /self%scaleRadius
+ z=abs(position%z())/self%scaleHeight
+ if (z > coshArgumentMaximum) then
+ coshTerm=(2.0d0*exp(-z)/(1.0d0+exp(-2.0d0*z)))**2
+ else
+ coshTerm=1.0d0/cosh(z)**2
+ end if
+ exponentialDensityGradientRadial=-(r+2.0d0*z*tanh(z))
+ if (.not.logarithmic_) &
+ & exponentialDensityGradientRadial=+ exponentialDensityGradientRadial &
+ & *self %density (coordinates) &
+ & /position%rSpherical ( )
+ return
+ end function exponentialDensityGradientRadial
+
+ double precision function exponentialDiskDensitySphericalAverage(self,radius)
!!{
Return the spherically-averaged density at the specified {\normalfont \ttfamily coordinates} in an exponential disk mass
distribution. Note that this assumes the thin-disk approximation.
!!}
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ double precision , intent(in ) :: radius
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskDensitySphericalAverage=0.0d0
- return
- end if
exponentialDiskDensitySphericalAverage=+0.5d0 &
& * self%surfaceDensityNormalization &
& / radius &
@@ -379,23 +426,17 @@ double precision function exponentialDiskDensitySphericalAverage(self,radius,com
return
end function exponentialDiskDensitySphericalAverage
- double precision function exponentialDiskMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function exponentialDiskMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for exponential disk mass
distributions. Note that this assumes the thin-disk approximation.
!!}
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionExponentialDisk), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: fractionalRadius
+ class (massDistributionExponentialDisk), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: fractionalRadius
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskMassEnclosedBySphere=0.0d0
- return
- end if
fractionalRadius =+radius &
& /self%scaleRadius
exponentialDiskMassEnclosedBySphere=+2.0d0 &
@@ -413,22 +454,16 @@ double precision function exponentialDiskMassEnclosedBySphere(self,radius,compon
return
end function exponentialDiskMassEnclosedBySphere
- double precision function exponentialDiskSurfaceDensity(self,coordinates,componentType,massType)
+ double precision function exponentialDiskSurfaceDensity(self,coordinates)
!!{
Return the surface density at the specified {\normalfont \ttfamily coordinates} in an exponential disk mass distribution.
!!}
use :: Coordinates, only : coordinate
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskSurfaceDensity=0.0d0
- return
- end if
! Get the radial coordinate.
r=coordinates%rCylindrical()/self%scaleRadius
! Compute the density.
@@ -436,23 +471,34 @@ double precision function exponentialDiskSurfaceDensity(self,coordinates,compone
return
end function exponentialDiskSurfaceDensity
- double precision function exponentialDiskRotationCurve(self,radius,componentType,massType)
+ double precision function exponentialDiskRadiusEnclosingSurfaceDensity(self,densitySurface,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given surface density for exponential disk mass distributions.
+ !!}
+ implicit none
+ class (massDistributionExponentialDisk), intent(inout), target :: self
+ double precision , intent(in ) :: densitySurface
+ double precision , intent(in ), optional :: radiusGuess
+
+ radius=- self%scaleRadius &
+ & *log( &
+ & + densitySurface &
+ & /self%surfaceDensityNormalization &
+ & )
+ return
+ end function exponentialDiskRadiusEnclosingSurfaceDensity
+
+ double precision function exponentialDiskRotationCurve(self,radius)
!!{
Return the mid-plane rotation curve for an exponential disk.
!!}
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r , halfRadius, &
- & radiusFactor
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision :: r , halfRadius, &
+ & radiusFactor
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskRotationCurve=0.0d0
- return
- end if
! Get scale-free radius.
r=radius/self%scaleRadius
! Compute rotation curve.
@@ -486,23 +532,17 @@ double precision function exponentialDiskRotationCurve(self,radius,componentType
return
end function exponentialDiskRotationCurve
- double precision function exponentialDiskRotationCurveGradient(self,radius,componentType,massType)
+ double precision function exponentialDiskRotationCurveGradient(self,radius)
!!{
Return the mid-plane rotation curve gradient for an exponential disk.
!!}
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , parameter :: fractionalRadiusMaximum=30.0d0
- double precision :: besselArgument , besselFactor
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision , parameter :: fractionalRadiusMaximum=30.0d0
+ double precision :: besselArgument , besselFactor
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskRotationCurveGradient=0.0d0
- return
- end if
! Compute Bessel functions argument.
besselArgument=+radius &
& /2.0d0 &
@@ -520,30 +560,38 @@ double precision function exponentialDiskRotationCurveGradient(self,radius,compo
end if
! Make dimensionful if necessary.
if (.not.self%dimensionless) exponentialDiskRotationCurveGradient= &
- & +sqrt(gravitationalConstantGalacticus) &
+ & +gravitationalConstantGalacticus &
& *exponentialDiskRotationCurveGradient
return
end function exponentialDiskRotationCurveGradient
- double precision function exponentialDiskPotential(self,coordinates,componentType,massType)
+ logical function exponentialDiskPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionExponentialDisk), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function exponentialDiskPotentialIsAnalytic
+
+ double precision function exponentialDiskPotential(self,coordinates,status)
!!{
Return the gravitational potential for an exponential disk.
!!}
use :: Coordinates , only : assignment(=) , coordinateCylindrical
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateCylindrical ) :: position
- double precision :: correctionSmallRadius, halfRadius, &
- & radius
-
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskPotential=0.0d0
- return
- end if
+ class (massDistributionExponentialDisk ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateCylindrical ) :: position
+ double precision :: correctionSmallRadius, halfRadius, &
+ & radius
+
+ if (present(status)) status=structureErrorCodeSuccess
! Get position in cylindrical coordinate system.
position=coordinates
! Compute density.
@@ -613,8 +661,8 @@ Compute Bessel function factors appearing in the expression for an razor-thin ex
implicit none
class (massDistributionExponentialDisk), intent(inout) :: self
double precision , intent(in ) :: halfRadius
- double precision , parameter :: halfRadiusSmall =1.0d-3
- integer , parameter :: rotationCurvePointsPerDecade=10
+ double precision , parameter :: halfRadiusSmall =1.0d-3
+ integer , parameter :: rotationCurvePointsPerDecade=100
integer :: iPoint , rotationCurvePointsCount
double precision :: x
logical :: makeTable
@@ -676,7 +724,7 @@ Compute Bessel function factors appearing in the expression for a razor-thin exp
double precision , intent(in ) :: halfRadius
double precision , parameter :: halfRadiusSmall =1.0d-3
double precision , parameter :: halfRadiusLarge =1.0d+2
- integer , parameter :: rotationCurveGradientPointsPerDecade=10
+ integer , parameter :: rotationCurveGradientPointsPerDecade=100
integer :: iPoint , rotationCurveGradientPointsCount
double precision :: x
@@ -713,17 +761,17 @@ Compute Bessel function factors appearing in the expression for a razor-thin exp
x=self%rotationCurveGradientTable%x(iPoint)
call self%rotationCurveGradientTable%populate &
& ( &
- & 2.0d0 &
+ & +2.0d0 &
& *x &
& *( &
- & Bessel_Function_I0(x)*Bessel_Function_K0(x) &
- & -Bessel_Function_I1(x)*Bessel_Function_K1(x) &
+ & + Bessel_Function_I0(x) *Bessel_Function_K0(x) &
+ & - Bessel_Function_I1(x) *Bessel_Function_K1(x) &
& ) &
& +x**2 &
& *( &
- & Bessel_Function_I1(x) *Bessel_Function_K0(x) &
+ & + Bessel_Function_I1(x) *Bessel_Function_K0(x) &
& - Bessel_Function_K1(x) *Bessel_Function_I0(x) &
- & -( Bessel_Function_I0(x)-Bessel_Function_I1(x)/x)*Bessel_Function_K1(x) &
+ & -(+Bessel_Function_I0(x)-Bessel_Function_I1(x)/x)*Bessel_Function_K1(x) &
& -(-Bessel_Function_K0(x)-Bessel_Function_K1(x)/x)*Bessel_Function_I1(x) &
& ), &
& iPoint &
@@ -738,7 +786,7 @@ Compute Bessel function factors appearing in the expression for a razor-thin exp
return
end function exponentialDiskBesselFactorRotationCurveGradient
- double precision function exponentialDiskSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function exponentialDiskSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Compute radial moments of the exponential disk mass distribution surface density profile.
!!}
@@ -749,14 +797,8 @@ double precision function exponentialDiskSurfaceDensityRadialMoment(self,moment,
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
double precision :: integralLow , integralHigh
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskSurfaceDensityRadialMoment=0.0d0
- return
- end if
! All moments n>-1 are finite.
if (present(isInfinite)) isInfinite=(moment <= -1.0d0)
if (moment <= -1.0d0) then
@@ -779,7 +821,7 @@ double precision function exponentialDiskSurfaceDensityRadialMoment(self,moment,
return
end function exponentialDiskSurfaceDensityRadialMoment
- function exponentialDiskAcceleration(self,coordinates,componentType,massType)
+ function exponentialDiskAcceleration(self,coordinates)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for exponential disk mass distributions.
!!}
@@ -787,20 +829,14 @@ function exponentialDiskAcceleration(self,coordinates,componentType,massType)
use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
implicit none
- double precision , dimension(3 ) :: exponentialDiskAcceleration
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ) , optional :: componentType
- type (enumerationMassTypeType ), intent(in ) , optional :: massType
- double precision , dimension(3 ) :: positionCartesian
- type (coordinateCylindrical ) :: coordinatesCylindrical
- type (coordinateCartesian ) :: coordinatesCartesian
- double precision :: accelerationRadial , accelerationVertical
+ double precision , dimension(3) :: exponentialDiskAcceleration
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision , dimension(3) :: positionCartesian
+ type (coordinateCylindrical ) :: coordinatesCylindrical
+ type (coordinateCartesian ) :: coordinatesCartesian
+ double precision :: accelerationRadial , accelerationVertical
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskAcceleration=0.0d0
- return
- end if
! Get position in cylindrical and Cartesian coordinate systems.
coordinatesCylindrical=coordinates
coordinatesCartesian =coordinates
@@ -831,31 +867,24 @@ function exponentialDiskAcceleration(self,coordinates,componentType,massType)
return
end function exponentialDiskAcceleration
- function exponentialDiskTidalTensor(self,coordinates,componentType,massType)
+ function exponentialDiskTidalTensor(self,coordinates)
!!{
Computes the gravitational tidal tensor at {\normalfont \ttfamily coordinates} for exponential disk mass distributions.
!!}
use :: Coordinates , only : assignment(=) , coordinateCartesian, coordinateCylindrical
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorNullR2D3Sym
implicit none
- type (tensorRank2Dimension3Symmetric ) :: exponentialDiskTidalTensor
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , dimension(3 ) :: positionCartesian
- double precision , parameter :: radiusCylindricalSmall =1.0d-6
- type (coordinateCartesian ) :: coordinatesCartesian
- type (coordinateCylindrical ) :: coordinatesCylindrical
- double precision :: accelerationRadial , accelerationVertical , &
- & tidalTensorRadialRadial , tidalTensorVerticalVertical, &
- & tidalTensorCross , radiusCylindrical
-
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskTidalTensor=tensorNullR2D3Sym
- return
- end if
+ type (tensorRank2Dimension3Symmetric ) :: exponentialDiskTidalTensor
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision , dimension(3) :: positionCartesian
+ double precision , parameter :: radiusCylindricalSmall =1.0d-6
+ type (coordinateCartesian ) :: coordinatesCartesian
+ type (coordinateCylindrical ) :: coordinatesCylindrical
+ double precision :: accelerationRadial , accelerationVertical , &
+ & tidalTensorRadialRadial , tidalTensorVerticalVertical, &
+ & tidalTensorCross , radiusCylindrical
+
! Get position in cylindrical and Cartesian coordinate systems.
coordinatesCylindrical=coordinates
coordinatesCartesian =coordinatesCylindrical
@@ -1091,11 +1120,11 @@ subroutine exponentialDiskAccelerationTabulate(self)
! Compute the vertical inverse scale-height. Note that our definition of β differs slightly from that of Kuijken & Gilmore
! (1989). They assume a density profile in the vertical direction of the form:
!
- ! ρ(z) = sech^ξ(βz/ξ)
+ ! ρ(z) = sech²(βz/ξ)
!
! while we use:
!
- ! ρ(z) = sech^ξ(z/h)
+ ! ρ(z) = sech²(z/h)
!
! where h is the scale-height. Therefore:
!
@@ -1575,25 +1604,19 @@ end function d2Izdz2m
end subroutine exponentialDiskAccelerationTabulate
- function exponentialDiskPositionSample(self,randomNumberGenerator_,componentType,massType)
+ function exponentialDiskPositionSample(self,randomNumberGenerator_)
!!{
Sample a position from an exponential disk distribution.
!!}
use :: Lambert_Ws , only : Lambert_Wm1
use :: Numerical_Constants_Math, only : Pi
implicit none
- double precision , dimension(3) :: exponentialDiskPositionSample
- class (massDistributionExponentialDisk), intent(inout) :: self
- class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: radius , height, &
- & phi
+ double precision , dimension(3) :: exponentialDiskPositionSample
+ class (massDistributionExponentialDisk), intent(inout) :: self
+ class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
+ double precision :: radius , height, &
+ & phi
- if (.not.self%matches(componentType,massType)) then
- exponentialDiskPositionSample=0.0d0
- return
- end if
! Select a radial coordinate.
radius=(-1.0d0-Lambert_Wm1((-1.0d0+ randomNumberGenerator_%uniformSample())/exp(1.0d0)))*self%scaleRadius
! Select a vertical coordinate.
diff --git a/source/mass_distributions.cylindrical.scaler.F90 b/source/mass_distributions.cylindrical.scaler.F90
index fd55d08ec2..71f0e27c1c 100644
--- a/source/mass_distributions.cylindrical.scaler.F90
+++ b/source/mass_distributions.cylindrical.scaler.F90
@@ -27,7 +27,7 @@
A mass distribution class for scaling cylindrical mass distributions. Specifically, the density at position $\mathbf{x}$ is
given by
\begin{equation}
- \rho(\mathbf{x}) = f_\mathrm{M} \rho^\prime(\mathbf{x}/f_\mathrm{r}),
+ \rho(\mathbf{x}) = \frac{f_\mathrm{M}}{f_\mathrm{r}^3} \rho^\prime(\mathbf{x}/f_\mathrm{r}),
\end{equation}
where $\rho^\prime(\mathbf{x})$ is the original mass distribution, and $f_\mathrm{r}=${\normalfont \ttfamily
[factorScalingLength]}, and $f_\mathrm{M}=${\normalfont \ttfamily [factorScalingMass]}.
@@ -42,20 +42,33 @@
class (massDistributionCylindrical), pointer :: massDistribution_ => null()
double precision :: factorScalingLength , factorScalingMass
contains
- final :: cylindricalScalerDestructor
- procedure :: density => cylindricalScalerDensity
- procedure :: densitySphericalAverage => cylindricalScalerDensitySphericalAverage
- procedure :: surfaceDensity => cylindricalScalerSurfaceDensity
- procedure :: radiusHalfMass => cylindricalScalerRadiusHalfMass
- procedure :: massEnclosedBySphere => cylindricalScalerMassEnclosedBySphere
- procedure :: potential => cylindricalScalerPotential
- procedure :: rotationCurve => cylindricalScalerRotationCurve
- procedure :: rotationCurveGradient => cylindricalScalerRotationCurveGradient
- procedure :: surfaceDensityRadialMoment => cylindricalScalerSurfaceDensityRadialMoment
- procedure :: acceleration => cylindricalScalerAcceleration
- procedure :: tidalTensor => cylindricalScalerTidalTensor
- procedure :: positionSample => cylindricalScalerPositionSample
- procedure :: isDimensionless => cylindricalScalerIsDimensionless
+ !![
+
+
+
+ !!]
+ final :: cylindricalScalerDestructor
+ procedure :: unscaled => cylindricalScalerUnscaled
+ procedure :: assumeMonotonicDecreasingSurfaceDensity => cylindricalScalerAssumeMonotonicDecreasingSurfaceDensity
+ procedure :: massTotal => cylindricalScalerMassTotal
+ procedure :: density => cylindricalScalerDensity
+ procedure :: densitySphericalAverage => cylindricalScalerDensitySphericalAverage
+ procedure :: densityGradientRadial => cylindricalScalerDensityGradientRadial
+ procedure :: surfaceDensity => cylindricalScalerSurfaceDensity
+ procedure :: radiusHalfMass => cylindricalScalerRadiusHalfMass
+ procedure :: massEnclosedBySphere => cylindricalScalerMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => cylindricalScalerRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => cylindricalScalerRadiusEnclosingDensity
+ procedure :: radiusEnclosingSurfaceDensity => cylindricalScalerRadiusEnclosingSurfaceDensity
+ procedure :: potentialIsAnalytic => cylindricalScalerPotentialIsAnalytic
+ procedure :: potential => cylindricalScalerPotential
+ procedure :: rotationCurve => cylindricalScalerRotationCurve
+ procedure :: rotationCurveGradient => cylindricalScalerRotationCurveGradient
+ procedure :: surfaceDensityRadialMoment => cylindricalScalerSurfaceDensityRadialMoment
+ procedure :: acceleration => cylindricalScalerAcceleration
+ procedure :: tidalTensor => cylindricalScalerTidalTensor
+ procedure :: positionSample => cylindricalScalerPositionSample
+ procedure :: isDimensionless => cylindricalScalerIsDimensionless
end type massDistributionCylindricalScaler
interface massDistributionCylindricalScaler
@@ -119,8 +132,8 @@ function cylindricalScalerConstructorInternal(factorScalingLength,factorScalingM
!!]
- self%componentType=self%massDistribution_%componentType
- self% massType=self%massDistribution_% massType
+ self%componentType=massDistribution_%componentType
+ self% massType=massDistribution_% massType
return
end function cylindricalScalerConstructorInternal
@@ -137,6 +150,29 @@ subroutine cylindricalScalerDestructor(self)
return
end subroutine cylindricalScalerDestructor
+ function cylindricalScalerUnscaled(self) result(massDistribution_)
+ !!{
+ Return a pointer to the unscaled mass distribution.
+ !!}
+ implicit none
+ class(massDistributionClass ), pointer :: massDistribution_
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+
+ massDistribution_ => self%massDistribution_
+ return
+ end function cylindricalScalerUnscaled
+
+ logical function cylindricalScalerAssumeMonotonicDecreasingSurfaceDensity(self) result(assumeMonotonicDecreasingSurfaceDensity)
+ !!{
+ Return true indicating that this distribution has a monotonically-decreasing surface density.
+ !!}
+ implicit none
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+
+ assumeMonotonicDecreasingSurfaceDensity=self%massDistribution_%assumeMonotonicDecreasingSurfaceDensity()
+ return
+ end function cylindricalScalerAssumeMonotonicDecreasingSurfaceDensity
+
logical function cylindricalScalerIsDimensionless(self)
!!{
Return the dimensional status.
@@ -148,64 +184,90 @@ logical function cylindricalScalerIsDimensionless(self)
return
end function cylindricalScalerIsDimensionless
- double precision function cylindricalScalerDensity(self,coordinates,componentType,massType)
+ double precision function cylindricalScalerMassTotal(self)
+ !!{
+ Return the total mass in a scaled cylindrical distribution.
+ !!}
+ implicit none
+ class(massDistributionCylindricalScaler), intent(inout):: self
+
+ cylindricalScalerMassTotal=+self%massDistribution_%massTotal () &
+ & *self %factorScalingMass
+ return
+ end function cylindricalScalerMassTotal
+
+ double precision function cylindricalScalerDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled cylindrical distribution.
!!}
implicit none
- class(massDistributionCylindricalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerDensity=+self%massDistribution_%density ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(coordinate ), allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerDensity=+self%massDistribution_%density (coordinatesScaled) &
+ & *self %factorScalingMass &
& /self %factorScalingLength**3
return
end function cylindricalScalerDensity
- double precision function cylindricalScalerDensitySphericalAverage(self,radius,componentType,massType)
+ double precision function cylindricalScalerDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density gradient in the radial direction in a scaled cylindrical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionCylindricalScaler), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ class (coordinate ) , allocatable :: coordinatesScaled
+ !![
+
+ !!]
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerDensityGradientRadial=+self%massDistribution_%densityGradientRadial( &
+ & coordinatesScaled, &
+ & logarithmic &
+ & )
+ if (.not.logarithmic) &
+ & cylindricalScalerDensityGradientRadial=+cylindricalScalerDensityGradientRadial &
+ & *self%factorScalingMass &
+ & /self%factorScalingLength**4
+ return
+ end function cylindricalScalerDensityGradientRadial
+
+ double precision function cylindricalScalerDensitySphericalAverage(self,radius)
!!{
Return the spherically-averaged density at the specified {\normalfont \ttfamily coordinates} in a scaled cylindrical mass
distribution.
!!}
implicit none
- class (massDistributionCylindricalScaler), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionCylindricalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radius
cylindricalScalerDensitySphericalAverage=+self%massDistribution_%densitySphericalAverage( &
& + radius &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
+ & /self%factorScalingLength &
& ) &
& *self %factorScalingMass &
& /self %factorScalingLength**3
return
end function cylindricalScalerDensitySphericalAverage
- double precision function cylindricalScalerRadiusHalfMass(self,componentType,massType)
+ double precision function cylindricalScalerRadiusHalfMass(self)
!!{
Interface for cylindrically symmetric mass distribution half mass radii functions.
!!}
implicit none
- class(massDistributionCylindricalScaler), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionCylindricalScaler), intent(inout) :: self
- cylindricalScalerRadiusHalfMass=+self%massDistribution_%radiusHalfMass (componentType,massType) &
+ cylindricalScalerRadiusHalfMass=+self%massDistribution_%radiusHalfMass () &
& *self %factorScalingLength
return
end function cylindricalScalerRadiusHalfMass
- double precision function cylindricalScalerMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function cylindricalScalerMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a scaled cylindrical mass
distribution.
@@ -213,110 +275,189 @@ double precision function cylindricalScalerMassEnclosedBySphere(self,radius,comp
implicit none
class (massDistributionCylindricalScaler), intent(inout), target :: self
double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
cylindricalScalerMassEnclosedBySphere=+self%massDistribution_%massEnclosedBySphere( &
& radius &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
+ & /self%factorScalingLength &
& ) &
& *self %factorScalingMass
return
end function cylindricalScalerMassEnclosedBySphere
- double precision function cylindricalScalerSurfaceDensity(self,coordinates,componentType,massType)
+ double precision function cylindricalScalerRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for cylindrically-scaled mass distributions.
+ !!}
+ implicit none
+ class (massDistributionCylindricalScaler), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass, massFractional
+
+ if (present(mass)) then
+ radius=+self%massDistribution_%radiusEnclosingMass(mass/self%factorScalingMass,massFractional) &
+ & *self %factorScalingLength
+ else
+ radius=+self%massDistribution_%radiusEnclosingMass(mass ,massFractional) &
+ & *self %factorScalingLength
+ end if
+ return
+ end function cylindricalScalerRadiusEnclosingMass
+
+ double precision function cylindricalScalerRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for cylindrically-scaled mass distributions.
+ !!}
+ implicit none
+ class (massDistributionCylindricalScaler), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (present(radiusGuess)) then
+ radius=+self%massDistribution_%radiusEnclosingDensity( &
+ & + density &
+ & /self%factorScalingMass &
+ & *self%factorScalingLength**3, &
+ & + radiusGuess &
+ & /self%factorScalingLength &
+ & ) &
+ & *self %factorScalingLength
+ else
+ radius=+self%massDistribution_%radiusEnclosingDensity( &
+ & + density &
+ & /self%factorScalingMass &
+ & *self%factorScalingLength**3 &
+ & ) &
+ & *self %factorScalingLength
+ end if
+ return
+ end function cylindricalScalerRadiusEnclosingDensity
+
+ double precision function cylindricalScalerRadiusEnclosingSurfaceDensity(self,densitySurface,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given surface density for cylindrically-scaled mass distributions.
+ !!}
+ implicit none
+ class (massDistributionCylindricalScaler), intent(inout), target :: self
+ double precision , intent(in ) :: densitySurface
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (present(radiusGuess)) then
+ radius=+self%massDistribution_%radiusEnclosingSurfaceDensity( &
+ & + densitySurface &
+ & /self%factorScalingMass &
+ & *self%factorScalingLength**2, &
+ & + radiusGuess &
+ & /self%factorScalingLength &
+ & ) &
+ & *self %factorScalingLength
+ else
+ radius=+self%massDistribution_%radiusEnclosingSurfaceDensity( &
+ & + densitySurface &
+ & /self%factorScalingMass &
+ & *self%factorScalingLength**2 &
+ & ) &
+ & *self %factorScalingLength
+ end if
+ return
+ end function cylindricalScalerRadiusEnclosingSurfaceDensity
+
+ double precision function cylindricalScalerSurfaceDensity(self,coordinates)
!!{
Return the surface density at the specified {\normalfont \ttfamily coordinates} in a scaled cylindrical distribution.
!!}
use :: Coordinates, only : coordinate
implicit none
- class(massDistributionCylindricalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerSurfaceDensity=+self%massDistribution_%surfaceDensity ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(coordinate ), allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerSurfaceDensity=+self%massDistribution_%surfaceDensity (coordinatesScaled) &
+ & *self %factorScalingMass &
& /self %factorScalingLength**2
return
end function cylindricalScalerSurfaceDensity
- double precision function cylindricalScalerRotationCurve(self,radius,componentType,massType)
+ double precision function cylindricalScalerRotationCurve(self,radius)
!!{
Return the mid-plane rotation curve for a scaled cylindrical distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
class (massDistributionCylindricalScaler), intent(inout) :: self
double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerRotationCurve=+ self%massDistribution_%rotationCurve ( &
- & + radius &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *sqrt( &
- & +self %factorScalingMass &
- & /self %factorScalingLength &
+
+ cylindricalScalerRotationCurve=+ self%massDistribution_%rotationCurve ( &
+ & + radius &
+ & /self%factorScalingLength &
+ & ) &
+ & *sqrt( &
+ & +self %factorScalingMass &
+ & /self %factorScalingLength &
& )
+ if (self%massDistribution_%isDimensionless()) &
+ & cylindricalScalerRotationCurve=+cylindricalScalerRotationCurve &
+ & *sqrt(gravitationalConstantGalacticus)
return
end function cylindricalScalerRotationCurve
- double precision function cylindricalScalerRotationCurveGradient(self,radius,componentType,massType)
+ double precision function cylindricalScalerRotationCurveGradient(self,radius)
!!{
- Return the mid-plane rotation curve gradient for a scaled cylindrical distribution.
+ Return the mid-plane rotation curve gradient (specifically, $\mathrm{d}V^2_\mathrm{c}/\mathrm{d}r$) for a scaled cylindrical distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionCylindricalScaler), intent(inout) :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerRotationCurveGradient=+ self%massDistribution_%rotationCurveGradient( &
- & + radius &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *sqrt( &
- & +self %factorScalingMass &
- & /self %factorScalingLength &
- & ) &
- & / self %factorScalingLength
+ class (massDistributionCylindricalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ cylindricalScalerRotationCurveGradient=+self%massDistribution_%rotationCurveGradient( &
+ & + radius &
+ & /self%factorScalingLength &
+ & ) &
+ & *self%factorScalingMass &
+ & /self%factorScalingLength**2
+ if (self%massDistribution_%isDimensionless()) &
+ & cylindricalScalerRotationCurveGradient=+cylindricalScalerRotationCurveGradient &
+ & *gravitationalConstantGalacticus
return
end function cylindricalScalerRotationCurveGradient
- double precision function cylindricalScalerPotential(self,coordinates,componentType,massType)
+ logical function cylindricalScalerPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return if the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+
+ isAnalytic=self%massDistribution_%potentialIsAnalytic()
+ return
+ end function cylindricalScalerPotentialIsAnalytic
+
+ double precision function cylindricalScalerPotential(self,coordinates,status)
!!{
Return the gravitational potential for a scaled cylindrical distribution.
!!}
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class(massDistributionCylindricalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerPotential=+self%massDistribution_%potential ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ class(massDistributionCylindricalScaler), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ class(coordinate ) , allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerPotential=+self%massDistribution_%potential ( &
+ & coordinatesScaled, &
+ & status &
+ & ) &
+ & *self %factorScalingMass &
& /self %factorScalingLength
+ if (self%massDistribution_%isDimensionless()) &
+ & cylindricalScalerPotential=+cylindricalScalerPotential &
+ & *gravitationalConstantGalacticus
return
end function cylindricalScalerPotential
- double precision function cylindricalScalerSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function cylindricalScalerSurfaceDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Compute radial moments of a scaled cylindrical distribution.
!!}
@@ -325,13 +466,11 @@ double precision function cylindricalScalerSurfaceDensityRadialMoment(self,momen
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
+
cylindricalScalerSurfaceDensityRadialMoment=0.0d0
!![
- cylindricalScalerSurfaceDensityRadialMoment=self%massDistribution_%surfaceDensityRadialMoment(moment=moment,isInfinite=isInfinite,componentType=componentType,massType=massType{conditions})
+ cylindricalScalerSurfaceDensityRadialMoment=self%massDistribution_%surfaceDensityRadialMoment(moment=moment,isInfinite=isInfinite{conditions})
@@ -342,62 +481,62 @@ double precision function cylindricalScalerSurfaceDensityRadialMoment(self,momen
return
end function cylindricalScalerSurfaceDensityRadialMoment
- function cylindricalScalerAcceleration(self,coordinates,componentType,massType)
+ function cylindricalScalerAcceleration(self,coordinates)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for a scaled cylindrical distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gigaYear, gravitationalConstantGalacticus, megaParsec
+ use :: Numerical_Constants_Prefixes , only : kilo
implicit none
- double precision , dimension(3 ) :: cylindricalScalerAcceleration
- class (massDistributionCylindricalScaler), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerAcceleration=+self%massDistribution_%acceleration ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ double precision , dimension(3) :: cylindricalScalerAcceleration
+ class (massDistributionCylindricalScaler), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ class (coordinate ), allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerAcceleration=+self%massDistribution_%acceleration (coordinatesScaled) &
+ & *self %factorScalingMass &
& /self %factorScalingLength**2
- return
+ if (self%massDistribution_%isDimensionless()) &
+ & cylindricalScalerAcceleration=+cylindricalScalerAcceleration &
+ & *kilo &
+ & *gigaYear &
+ & /megaParsec &
+ & *gravitationalConstantGalacticus
+ return
end function cylindricalScalerAcceleration
- function cylindricalScalerTidalTensor(self,coordinates,componentType,massType)
+ function cylindricalScalerTidalTensor(self,coordinates)
!!{
Computes the gravitational tidal tensor at {\normalfont \ttfamily coordinates} for a scaled cylindrical distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- type (tensorRank2Dimension3Symmetric ) :: cylindricalScalerTidalTensor
- class(massDistributionCylindricalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- cylindricalScalerTidalTensor=+self%massDistribution_%tidalTensor ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ type (tensorRank2Dimension3Symmetric ) :: cylindricalScalerTidalTensor
+ class(massDistributionCylindricalScaler), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(coordinate ), allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ cylindricalScalerTidalTensor=+self%massDistribution_%tidalTensor (coordinatesScaled) &
+ & *self %factorScalingMass &
& /self %factorScalingLength**3
- return
+ if (self%massDistribution_%isDimensionless()) &
+ & cylindricalScalerTidalTensor=+cylindricalScalerTidalTensor &
+ & *gravitationalConstantGalacticus
+ return
end function cylindricalScalerTidalTensor
- function cylindricalScalerPositionSample(self,randomNumberGenerator_,componentType,massType)
+ function cylindricalScalerPositionSample(self,randomNumberGenerator_)
!!{
Sample a position from a scaled cylindrical distribution.
!!}
implicit none
- double precision , dimension(3) :: cylindricalScalerPositionSample
- class (massDistributionCylindricalScaler), intent(inout) :: self
- class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision , dimension(3) :: cylindricalScalerPositionSample
+ class (massDistributionCylindricalScaler), intent(inout) :: self
+ class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- cylindricalScalerPositionSample=+self%massDistribution_%positionSample (randomNumberGenerator_,componentType,massType) &
+ cylindricalScalerPositionSample=+self%massDistribution_%positionSample (randomNumberGenerator_) &
& *self %factorScalingLength
return
end function cylindricalScalerPositionSample
diff --git a/source/mass_distributions.spherical.Burkert.F90 b/source/mass_distributions.spherical.Burkert.F90
new file mode 100644
index 0000000000..56a1a60d62
--- /dev/null
+++ b/source/mass_distributions.spherical.Burkert.F90
@@ -0,0 +1,996 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a Burkert \citep{burkert_structure_1995} mass distribution class.
+ !!}
+
+ use :: Numerical_Interpolation , only : interpolator
+ use :: Numerical_Constants_Math, only : Pi
+
+ !![
+
+
+ A mass distribution class which implements the \citep{burkert_structure_1995} density profile:
+ \begin{equation}
+ \rho_\mathrm{dark matter}(r) = \rho_0 \left(1+{r\over r_\mathrm{s}}\right)^{-1} \left(1+[{r\over
+ r_\mathrm{s}}]^2\right)^{-1}.
+ \end{equation}
+ The mass enclosed within radius $r$ is given by
+ \begin{equation}
+ M(<r) = \pi \rho_0 r_\mathrm{s}^3 \left[ 2 \log(1 + R) + \log(1 + R^2) -2 \tan^{-1}(R) \right]
+ \end{equation}
+ where $R=r/r_\mathrm{s}$. The associated gravitational potential is
+ \begin{equation}
+ \Phi(r) = - \frac{\mathrm{G} \pi \rho_0 r_\mathrm{s}^2}{R} \left[ (R-1) \log \left(R^2+1\right)-2 (R+1) \log (R+1)-2 (R+1) \cot^{-1}(R)+\pi \right]
+ \end{equation}
+ The peak of the rotation curve occurs at $R=3.2446257246042642$ (found by numerical solution) at which point the rotation
+ curve amplitude is 1.644297750532498, and the Fourier transform of the profile, $F(k) = \int_0^c 4 \pi r^2 \exp(-i k r)
+ \rho(r) \mathrm{d} r / k r$ (needed in calculations of clustering using the halo model) is given by
+ \begin{eqnarray}
+ F(k) &=& (1+i) \frac{\pi}{k m(c) } \left( \right. \nonumber \\
+ & & + \exp( k) \left\{ -i \pi -\mathrm{E}_\mathrm{i}[- k]+\mathrm{E}_\mathrm{i}[(-1+ic)k] \right\} \nonumber \\
+ & & +(1-i) \exp(-k) \left\{ +\mathrm{E}_\mathrm{i}[-i k]+\mathrm{E}_\mathrm{i}[(+i+ic)k] \right\} \nonumber \\
+ & & + i \exp(-k) \left\{ +\mathrm{E}_\mathrm{i}[+ k]+\mathrm{E}_\mathrm{i}[(+1+ic)k] \right\} \nonumber \\
+ & & \left. \right).
+ \end{eqnarray}
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionBurkert
+ !!{
+ The \citep{burkert_structure_1995} mass distribution.
+ !!}
+ private
+ double precision :: densityNormalization , scaleLength
+ double precision :: densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum
+ double precision :: densityScaleFreeMinimum , densityScaleFreeMaximum
+ type (interpolator), allocatable :: densityScaleFree_
+ double precision :: massScaleFreeRadiusMinimum , massScaleFreeRadiusMaximum
+ double precision :: massScaleFreeMinimum , massScaleFreeMaximum
+ type (interpolator), allocatable :: massScaleFree_
+ double precision :: angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum
+ double precision :: angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum
+ type (interpolator), allocatable :: angularMomentumSpecificScaleFree_
+ double precision :: timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum
+ double precision :: timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum
+ type (interpolator), allocatable :: timeFreefallScaleFree_
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: massTotal => burkertMassTotal
+ procedure :: density => burkertDensity
+ procedure :: densityGradientRadial => burkertDensityGradientRadial
+ procedure :: densityRadialMoment => burkertDensityRadialMoment
+ procedure :: massEnclosedBySphere => burkertMassEnclosedBySphere
+ procedure :: velocityRotationCurveMaximum => burkertVelocityRotationCurveMaximum
+ procedure :: radiusRotationCurveMaximum => burkertRadiusRotationCurveMaximum
+ procedure :: radiusEnclosingMass => burkertRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => burkertRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => burkertRadiusFromSpecificAngularMomentum
+ procedure :: fourierTransform => burkertFourierTransform
+ procedure :: radiusFreefall => burkertRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => burkertRadiusFreefallIncreaseRate
+ procedure :: timeFreefallTabulate => burkertTimeFreefallTabulate
+ procedure :: potentialIsAnalytic => burkertPotentialIsAnalytic
+ procedure :: potential => burkertPotential
+ procedure :: energyPotential => burkertEnergyPotential
+ procedure :: descriptor => burkertDescriptor
+ end type massDistributionBurkert
+
+ interface massDistributionBurkert
+ !!{
+ Constructors for the {\normalfont \ttfamily burkert} mass distribution class.
+ !!}
+ module procedure massDistributionBurkertConstructorParameters
+ module procedure massDistributionBurkertConstructorInternal
+ end interface massDistributionBurkert
+
+ ! The minimum (scale-free) freefall timescale in a Burkert profile.
+ double precision , parameter :: timeFreefallScaleFreeMinimum=sqrt(3.0d0*Pi)/4.0d0
+
+contains
+
+ function massDistributionBurkertConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily burkert} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ type (massDistributionBurkert) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: mass , scaleLength , &
+ & densityNormalization, radiusOuter
+ logical :: dimensionless
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ densityNormalization
+ 1.0d0/Pi/(log(8.0d0)-Pi/2.0d0)
+ The density normalization of the Burkert profile.
+
+
+
+ scaleLength
+ 1.0d0
+ The scale radius of the Burkert profile.
+
+
+
+ mass
+ 1.0d0
+ The mass of the Burkert profile.
+
+
+
+ radiusOuter
+ The outer radius of the Burkert profile.
+
+
+
+ dimensionless
+ .true.
+ If true the Burkert profile is considered to be dimensionless.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ self=massDistributionBurkert(scaleLength=scaleLength,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+
+
+ !!]
+ return
+ end function massDistributionBurkertConstructorParameters
+
+ function massDistributionBurkertConstructorInternal(scaleLength,densityNormalization,mass,radiusOuter,dimensionless,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``burkert'' mass distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ type (massDistributionBurkert ) :: self
+ double precision , intent(in ) :: scaleLength
+ double precision , intent(in ), optional :: densityNormalization, radiusOuter, &
+ & mass
+ logical , intent(in ), optional :: dimensionless
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ ! Determine density normalization.
+ if ( &
+ & present(densityNormalization) &
+ & ) then
+ self%densityNormalization=densityNormalization
+ else if ( &
+ & present(mass ).and. &
+ & present(radiusOuter ) &
+ & ) then
+ radiusScaleFree =+radiusOuter/self%scaleLength
+ self%densityNormalization=+mass/Pi/self%scaleLength**3/(2.0d0*log(1.0d0+radiusScaleFree)+log(1.0d0+radiusScaleFree**2)-2.0d0*atan(radiusScaleFree))
+ else
+ call Error_Report('either "densityNormalization", or "mass" and "radiusOuter" must be specified'//{introspection:location})
+ end if
+ ! Determine if profile is dimensionless.
+ if (present(dimensionless )) then
+ self%dimensionless=dimensionless
+ else
+ self%dimensionless=.false.
+ end if
+ ! Initialize memoized results.
+ self%densityScaleFreeMinimum =+huge(0.0d0)
+ self%densityScaleFreeMaximum =-huge(0.0d0)
+ self%densityScaleFreeRadiusMinimum =+1.0d0
+ self%densityScaleFreeRadiusMaximum =+1.0d0
+ self%massScaleFreeMinimum =+huge(0.0d0)
+ self%massScaleFreeMaximum =-huge(0.0d0)
+ self%massScaleFreeRadiusMinimum =+1.0d0
+ self%massScaleFreeRadiusMaximum =+1.0d0
+ self%angularMomentumSpecificScaleFreeMinimum =+huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeMaximum =-huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=+1.0d0
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=+1.0d0
+ self%timeFreefallScaleFreeMinimum =+huge(0.0d0)
+ self%timeFreefallScaleFreeMaximum =-huge(0.0d0)
+ self%timeFreefallScaleFreeRadiusMinimum =+1.0d0
+ self%timeFreefallScaleFreeRadiusMaximum =+1.0d0
+ return
+ end function massDistributionBurkertConstructorInternal
+
+ double precision function burkertMassTotal(self)
+ !!{
+ Return the total mass in an Burkert mass distribution.
+ !!}
+ implicit none
+ class(massDistributionBurkert), intent(inout) :: self
+
+ burkertMassTotal=huge(0.0d0)
+ return
+ end function burkertMassTotal
+
+ double precision function burkertDensity(self,coordinates)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Burkert mass distribution.
+ !!}
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radiusScaleFree
+
+ ! Compute the density at this position.
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ burkertDensity =+self %densityNormalization &
+ & /(+1.0d0+radiusScaleFree ) &
+ & /(+1.0d0+radiusScaleFree**2)
+ return
+ end function burkertDensity
+
+ double precision function burkertDensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Burkert \citep{burkert_structure_1995} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ radiusScaleFree =+coordinates%rSpherical() &
+ & /self %scaleLength
+ densityGradientRadial=-3.0d0 &
+ & +1.0d0/(1.0d0+radiusScaleFree ) &
+ & +2.0d0/(1.0d0+radiusScaleFree**2)
+ if (.not.logarithmic_) densityGradientRadial=+ densityGradientRadial &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ return
+ end function burkertDensityGradientRadial
+
+ double precision function burkertDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Computes radial moments of the density in an Burkert \citep{burkert_structure_1995} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: radialMomentMinimum, radialMomentMaximum
+
+ densityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ if (present(radiusMinimum)) then
+ radialMomentMinimum=radialMomentScaleFree(radiusMinimum/self%scaleLength)
+ else
+ radialMomentMinimum=radialMomentScaleFree( 0.0d0)
+ end if
+ if (present(radiusMaximum)) then
+ radialMomentMaximum=radialMomentScaleFree(radiusMaximum/self%scaleLength)
+ else
+ radialMomentMaximum=0.0d0
+ if (moment >= 3.0d0) then
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ return
+ else
+ call Error_Report('moment is infinite'//{introspection:location})
+ end if
+ end if
+ end if
+ densityRadialMoment=+self%densityNormalization &
+ & *self%scaleLength **(moment+1.0d0) &
+ & *( &
+ & +radialMomentMaximum &
+ & -radialMomentMinimum &
+ & )
+ return
+
+ contains
+
+ double precision function radialMomentScaleFree(radius)
+ !!{
+ Provides the scale-free part of the radial moment of the Burkert density profile.
+ !!}
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ use :: Numerical_Comparison , only : Values_Agree
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (Values_Agree(moment,0.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=+0.50d0*atan( radius ) &
+ & +0.50d0*log (+1.0d0+radius ) &
+ & -0.25d0*log (+1.0d0+radius**2)
+ else if (Values_Agree(moment,1.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=+0.50d0*atan( radius ) &
+ & -0.50d0*log (+1.0d0+radius ) &
+ & +0.25d0*log (+1.0d0+radius**2)
+ else if (Values_Agree(moment,2.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=-0.50d0*atan( radius ) &
+ & +0.50d0*log (+1.0d0+radius ) &
+ & +0.25d0*log (+1.0d0+radius**2)
+ else if (Values_Agree(moment,3.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=+ radius &
+ & -0.50d0*atan( radius ) &
+ & -0.50d0*log (+1.0d0+radius ) &
+ & -0.25d0*log (+1.0d0+radius**2)
+ else
+ radialMomentScaleFree=+radius**(1.0d0+moment) &
+ & / 2.0d0 &
+ & / (1.0d0+moment) &
+ & / (2.0d0+moment) &
+ & *( &
+ & +(2.0d0+moment)*Hypergeometric_2F1([1.0d0,0.5d0*(1.0d0+moment)],[0.5d0*(3.0d0+moment)],-radius**2) &
+ & +(2.0d0+moment)*Hypergeometric_2F1([1.0d0, (1.0d0+moment)],[ (2.0d0+moment)],-radius ) &
+ & +(1.0d0+moment)*Hypergeometric_2F1([1.0d0,0.5d0*(2.0d0+moment)],[0.5d0*(4.0d0+moment)],-radius**2) &
+ & )
+ end if
+ return
+ end function radialMomentScaleFree
+
+ end function burkertDensityRadialMoment
+
+ double precision function burkertMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for burkert mass distributions.
+ !!}
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radiusScaleFree
+
+ radiusScaleFree=+ radius &
+ & /self%scaleLength
+ mass =+self%densityNormalization &
+ & *self%scaleLength **3 &
+ & *massEnclosedScaleFree(radiusScaleFree)
+ return
+ end function burkertMassEnclosedBySphere
+
+ double precision function burkertRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for burkert mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range , rangeTypeLogarithmic
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision , allocatable , dimension(:) :: radii , masses
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: massScaleFree , mass_
+ integer :: countRadii
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_=mass
+ else if (present(massFractional)) then
+ call Error_Report('mass is unbounded, so mass fraction is undefined'//{introspection:location})
+ else
+ call Error_Report('either mass or massFractional must be supplied'//{introspection:location})
+ end if
+ massScaleFree=+ mass_ &
+ & /self%densityNormalization &
+ & /self%scaleLength **3
+ if ( &
+ & massScaleFree <= self%massScaleFreeMinimum &
+ & .or. &
+ & massScaleFree > self%massScaleFreeMaximum &
+ & ) then
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMinimum) >= massScaleFree)
+ self%massScaleFreeRadiusMinimum=0.5d0*self%massScaleFreeRadiusMinimum
+ end do
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMaximum) < massScaleFree)
+ self%massScaleFreeRadiusMaximum=2.0d0*self%massScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%massScaleFreeRadiusMaximum/self%massScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%massScaleFree_)) deallocate(self%massScaleFree_)
+ allocate( radii (countRadii))
+ allocate( masses (countRadii))
+ allocate(self%massScaleFree_ )
+ radii =Make_Range(self%massScaleFreeRadiusMinimum,self%massScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ masses =massEnclosedScaleFree( radii)
+ self%massScaleFreeMinimum=masses ( 1 )
+ self%massScaleFreeMaximum=masses (countRadii )
+ self%massScaleFree_ =interpolator (masses ,radii)
+ end if
+ radius=+self%massScaleFree_%interpolate(massScaleFree) &
+ & *self%scaleLength
+ return
+ end function burkertRadiusEnclosingMass
+
+ double precision function burkertRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for burkert mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+ double precision , allocatable , dimension(:) :: radii , densities
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: densityScaleFree
+ integer :: countRadii
+
+ densityScaleFree=+density &
+ & /self%densityNormalization
+ if ( &
+ & densityScaleFree <= self%densityScaleFreeMinimum &
+ & .or. &
+ & densityScaleFree > self%densityScaleFreeMaximum &
+ & ) then
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMinimum) < densityScaleFree)
+ self%densityScaleFreeRadiusMinimum=0.5d0*self%densityScaleFreeRadiusMinimum
+ end do
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMaximum) >= densityScaleFree)
+ self%densityScaleFreeRadiusMaximum=2.0d0*self%densityScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%densityScaleFreeRadiusMaximum/self%densityScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%densityScaleFree_)) deallocate(self%densityScaleFree_)
+ allocate( radii (countRadii))
+ allocate( densities (countRadii))
+ allocate(self%densityScaleFree_ )
+ radii = Make_Range(self%densityScaleFreeRadiusMinimum,self%densityScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ densities =-densityEnclosedScaleFree( radii)
+ self%densityScaleFreeMinimum=-densities (countRadii )
+ self%densityScaleFreeMaximum=-densities ( 1 )
+ self%densityScaleFree_ = interpolator (densities ,radii)
+ end if
+ radius=+self%densityScaleFree_%interpolate(-densityScaleFree) &
+ & *self%scaleLength
+ return
+ end function burkertRadiusEnclosingDensity
+
+ elemental double precision function massEnclosedScaleFree(radius) result(mass)
+ !!{
+ Evaluate the mass enclosed by a given radius in a scale-free Burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: minimumRadiusForExactSolution=1.0d-4
+
+ if (radius < minimumRadiusForExactSolution) then
+ ! Use a series solution for small radii.
+ mass =+4.0d0 &
+ & /3.0d0 &
+ & *Pi &
+ & * radius**3 &
+ & *(+1.0d0-radius)
+ else
+ ! Use the exact solution.
+ mass =+Pi &
+ & *( &
+ & +2.0d0*log (1.0d0+radius ) &
+ & + log (1.0d0+radius**2) &
+ & -2.0d0*atan( radius ) &
+ & )
+ end if
+ return
+ end function massEnclosedScaleFree
+
+ elemental double precision function densityEnclosedScaleFree(radius) result(density)
+ !!{
+ Evaluate the mean enclosed density at a given radius in a scale-free Burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+
+ density=+3.0d0 &
+ & /4.0d0 &
+ & /Pi &
+ & *massEnclosedScaleFree(radius) &
+ & / radius **3
+ return
+ end function densityEnclosedScaleFree
+
+ double precision function burkertRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for burkert mass distributions.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+ double precision , allocatable , dimension(:) :: radii , angularMomentaSpecific
+ double precision , parameter :: countRadiiPerDecade =100.0d0
+ double precision :: angularMomentumSpecificScaleFree
+ integer :: countRadii
+
+ if (angularMomentumSpecific > 0.0d0) then
+ angularMomentumSpecificScaleFree=+angularMomentumSpecific &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & / self%scaleLength **2
+ if ( &
+ & angularMomentumSpecificScaleFree <= self%angularMomentumSpecificScaleFreeMinimum &
+ & .or. &
+ & angularMomentumSpecificScaleFree > self%angularMomentumSpecificScaleFreeMaximum &
+ & ) then
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMinimum) >= angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=0.5d0*self%angularMomentumSpecificScaleFreeRadiusMinimum
+ end do
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMaximum) < angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=2.0d0*self%angularMomentumSpecificScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%angularMomentumSpecificScaleFreeRadiusMaximum/self%angularMomentumSpecificScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%angularMomentumSpecificScaleFree_)) deallocate(self%angularMomentumSpecificScaleFree_)
+ allocate( radii (countRadii))
+ allocate( angularMomentaSpecific (countRadii))
+ allocate(self%angularMomentumSpecificScaleFree_ )
+ radii =Make_Range(self%angularMomentumSpecificScaleFreeRadiusMinimum,self%angularMomentumSpecificScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ angularMomentaSpecific =angularMomentumSpecificEnclosedScaleFree( radii)
+ self%angularMomentumSpecificScaleFreeMinimum=angularMomentaSpecific ( 1 )
+ self%angularMomentumSpecificScaleFreeMaximum=angularMomentaSpecific ( countRadii )
+ self%angularMomentumSpecificScaleFree_ =interpolator (angularMomentaSpecific,radii)
+ end if
+ radius=+self%angularMomentumSpecificScaleFree_%interpolate(angularMomentumSpecificScaleFree) &
+ & *self%scaleLength
+ else
+ radius=+0.0d0
+ end if
+ return
+ end function burkertRadiusFromSpecificAngularMomentum
+
+ elemental double precision function angularMomentumSpecificEnclosedScaleFree(radius) result(angularMomentumSpecific)
+ !!{
+ Evaluate the specific angular momentum at a given radius in a scale-free Burkert mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ angularMomentumSpecific=+sqrt( &
+ & +massEnclosedScaleFree(radius) &
+ & * radius &
+ & )
+ return
+ end function angularMomentumSpecificEnclosedScaleFree
+
+ double precision function burkertVelocityRotationCurveMaximum(self) result(velocity)
+ !!{
+ Return the peak velocity in the rotation curve for an burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionBurkert ), intent(inout) :: self
+ double precision , parameter :: circularVelocityMaximumScaleFree=1.644297750532498d0 ! The circular velocity (in scale-free units) at the peak of the Burkert rotation curve.
+ ! Numerical value found using Mathematica.
+
+ velocity=+circularVelocityMaximumScaleFree &
+ & *sqrt( &
+ & +self%densityNormalization &
+ & ) &
+ & * self%scaleLength
+ if (.not.self%isDimensionless()) &
+ & velocity=+velocity &
+ & *sqrt(gravitationalConstantGalacticus)
+ return
+ end function burkertVelocityRotationCurveMaximum
+
+ double precision function burkertRadiusRotationCurveMaximum(self) result(radius)
+ !!{
+ Return the peak velocity in the rotation curve for an burkert mass distribution.
+ !!}
+ implicit none
+ class (massDistributionBurkert), intent(inout), target :: self
+ ! The radius (in scale-free units) at the peak of the Burkert rotation curve. Numerical value found using Mathematica.
+ double precision , parameter :: radiusCircularVelocityMaximumScaleFree=3.244625724604264d0
+
+ radius=+radiusCircularVelocityMaximumScaleFree &
+ & *self%scaleLength
+ return
+ end function burkertRadiusRotationCurveMaximum
+
+ logical function burkertPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionBurkert), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function burkertPotentialIsAnalytic
+
+ double precision function burkertPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an burkert mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionBurkert ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision :: radiusScaleFree
+
+ if (present(status)) status=structureErrorCodeSuccess
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ potential=+potentialScaleFree (radiusScaleFree) &
+ & *self%densityNormalization &
+ & *self%scaleLength **2
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
+ return
+ end function burkertPotential
+
+ elemental double precision function potentialScaleFree(radius) result(potential)
+ !!{
+ Compute the potential in a scale-free Burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: radiusSmall=1.0d-3
+
+ if (radius < radiusSmall) then
+ ! Use a series solution for very small radii.
+ potential=- Pi**2 &
+ & +2.0d0/ 3.0d0*Pi *radius**2 &
+ & -1.0d0/ 3.0d0*Pi *radius**3 &
+ & +2.0d0/21.0d0*Pi *radius**6
+ else
+ ! Use the full expression for larger radii.
+ potential=-Pi &
+ & /radius &
+ & *( &
+ & +2.0d0*(1.0d0+radius)*log (1.0d0+radius ) &
+ & + (1.0d0-radius)*log (1.0d0+radius**2) &
+ & -2.0d0 *atan( radius ) &
+ & +2.0d0* radius *atan(1.0d0/radius ) &
+ & )
+ end if
+ return
+ end function potentialScaleFree
+
+ double precision function potentialDifferenceScaleFree(radius1,radius2) result(potential)
+ !!{
+ Compute the potential difference in a scale-free Burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Comparison , only : Values_Agree
+ implicit none
+ double precision, intent(in ) :: radius1 , radius2
+ double precision, parameter :: radiusSmall =1.0d-3
+ double precision, parameter :: toleranceRelative =1.0d-3
+ double precision :: potentialGradientLogarithmic , radiusDifferenceLogarithmic
+
+ if (Values_Agree(radius1,radius2,relTol=toleranceRelative) .or. max(radius1,radius2) < radiusSmall) then
+ if (radius1 < radiusSmall) then
+ potentialGradientLogarithmic=-4.0d0/3.0d0/Pi *radius1**2 &
+ & +1.0d0 /Pi *radius1**3 &
+ & -8.0d0/9.0d0/Pi**2*radius1**4
+ else
+ potentialGradientLogarithmic=-( &
+ & + log (1.0d0+radius1**2) &
+ & +2.0d0 *log (1.0d0+radius1 ) &
+ & -2.0d0 *atan( radius1 ) &
+ & ) &
+ & /( &
+ & + (1.0d0-radius1)*log (1.0d0+radius1**2) &
+ & +2.0d0*(1.0d0+radius1)*log (1.0d0+radius1 ) &
+ & -2.0d0 *atan( radius1 ) &
+ & +2.0d0* radius1 *atan(1.0d0/radius1 ) &
+ & )
+ end if
+ radiusDifferenceLogarithmic=+1.0d0 &
+ & -radius2 &
+ & /radius1
+ potential =+potentialScaleFree (radius1) &
+ & *potentialGradientLogarithmic &
+ & *radiusDifferenceLogarithmic
+ else
+ potential=+potentialScaleFree(radius1) &
+ & -potentialScaleFree(radius2)
+ end if
+ return
+ end function potentialDifferenceScaleFree
+
+ double precision function burkertFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in an Burkert mass
+ distribution.
+ !!}
+ use :: Exponential_Integrals , only : Exponential_Integral
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter , wavenumber
+ double precision :: wavenumberScaleFree, radiusOuterScaleFree
+
+ waveNumberScaleFree =+waveNumber *self%scaleLength
+ radiusOuterScaleFree=+radiusOuter/self%scaleLength
+ fourierTransform =+dimag( &
+ & +dcmplx(1.0d0,1.0d0) &
+ & *Pi &
+ & /wavenumberScaleFree &
+ & *( &
+ & + exp(+ wavenumberScaleFree)*(dcmplx(0.0d0,-1.0d0)*Pi-Exponential_Integral(- wavenumberScaleFree)+Exponential_Integral(dcmplx(-1.0d0, +radiusOuterScaleFree)*wavenumberScaleFree)) &
+ & +dcmplx(1.0d0,-1.0d0)*exp(-dcmplx(0.0d0,1.0d0)*wavenumberScaleFree)*( +Exponential_Integral(+dcmplx(0.0d0,1.0d0)*wavenumberScaleFree)-Exponential_Integral(dcmplx(+0.0d0,+1.0d0+radiusOuterScaleFree)*wavenumberScaleFree)) &
+ & +dcmplx(0.0d0,+1.0d0)*exp(- wavenumberScaleFree)*( +Exponential_Integral(+ wavenumberScaleFree)-Exponential_Integral(dcmplx(+1.0d0, +radiusOuterScaleFree)*wavenumberScaleFree)) &
+ & ) &
+ & ) &
+ & /massEnclosedScaleFree(radiusOuterScaleFree)
+ return
+ end function burkertFourierTransform
+
+ double precision function burkertRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in an Burkert mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (timeScaleFree <= timeFreefallScaleFreeMinimum) then
+ radius=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radius=+self%timeFreefallScaleFree_%interpolate(timeScaleFree) &
+ & *self%scaleLength
+ return
+ end function burkertRadiusFreefall
+
+ double precision function burkertRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an burkert mass
+ distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (timeScaleFree <= timeFreefallScaleFreeMinimum) then
+ radiusIncreaseRate=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radiusIncreaseRate=+self%timeFreefallScaleFree_%derivative(timeScaleFree) &
+ & *self%scaleLength &
+ & / timeScale
+ return
+ end function burkertRadiusFreefallIncreaseRate
+
+ subroutine burkertTimeFreefallTabulate(self,timeScaleFree)
+ !!{
+ Tabulate the freefall radius at the given {\normalfont \ttfamily time} in an Burkert mass distribution.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: timeScaleFree
+ double precision , allocatable , dimension(:) :: radii , timesFreefall
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: radiusStart
+ integer :: countRadii , i
+ type (integrator ) :: integrator_
+
+ if ( &
+ & timeScaleFree <= self%timeFreefallScaleFreeMinimum &
+ & .or. &
+ & timeScaleFree > self%timeFreefallScaleFreeMaximum &
+ & ) then
+ integrator_=integrator(timeFreeFallIntegrand,toleranceRelative=1.0d-6)
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMinimum) >= timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMinimum=0.5d0*self%timeFreefallScaleFreeRadiusMinimum
+ end do
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMaximum) < timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMaximum=2.0d0*self%timeFreefallScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%timeFreefallScaleFreeRadiusMaximum/self%timeFreefallScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%timeFreefallScaleFree_)) deallocate(self%timeFreefallScaleFree_)
+ allocate( radii (countRadii))
+ allocate( timesFreefall (countRadii))
+ allocate(self%timeFreefallScaleFree_ )
+ radii=Make_Range(self%timeFreefallScaleFreeRadiusMinimum,self%timeFreefallScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ timesFreefall(i)=timeFreefallScaleFree(radii(i))
+ end do
+ self%timeFreefallScaleFreeMinimum=timesFreefall( 1 )
+ self%timeFreefallScaleFreeMaximum=timesFreefall( countRadii )
+ self%timeFreefallScaleFree_ =interpolator (timesFreefall,radii)
+ end if
+ return
+
+ contains
+
+ double precision function timeFreefallScaleFree(radius)
+ !!{
+ Evaluate the freefall time from a given radius in a scale-free Burkert mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ radiusStart = radius
+ timeFreefallScaleFree=integrator_%integrate(0.0d0,radius)
+ return
+ end function timeFreefallScaleFree
+
+ double precision function timeFreeFallIntegrand(radius)
+ !!{
+ Integrand used to find the freefall time in a scale-free Burkert mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision :: potentialDifference
+
+ if (radius == 0.0d0) then
+ timeFreeFallIntegrand=+0.0d0
+ else
+ potentialDifference=+potentialDifferenceScaleFree(radiusStart,radius)
+ if (potentialDifference > 0.0d0) then
+ timeFreeFallIntegrand=+1.0d0 &
+ & /sqrt( &
+ & +2.0d0 &
+ & *potentialDifference &
+ & )
+ else
+ timeFreeFallIntegrand=+0.0d0
+ end if
+ end if
+ return
+ end function timeFreeFallIntegrand
+
+ end subroutine burkertTimeFreefallTabulate
+
+ double precision function burkertEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in a Burkert mass distribution. This is
+ \begin{eqnarray}
+ W &=& \frac{1}{24} \pi ^2 \left(48 \mathrm{G}G + 48 i \text{Li}_2\left(\left(\frac{1}{2}+\frac{i}{2}\right) (x+1)\right)-48 i \text{Li}_2\left(\left(\frac{1}{2}-\frac{i}{2}\right) (x+1)\right)+48 i \text{Li}_2\left(\frac{i+x}{-i+x}\right)-96 \text{Li}_2\left(\left(-\frac{1}{2}+\frac{i}{2}\right) (-i+x)\right)-96 \text{Li}_2\left(\left(-\frac{1}{2}-\frac{i}{2}\right) (i+x)\right)-48 i \text{Li}_2\left(i \exp(2 i \tan ^{-1}(x))\right)+12 \left(\log ^2\left(x^2+1\right)+4 \log (x) \log \left(x^2+1\right)-4 \log (x+1) \log \left(x^2+1\right)+(2+4 i) \pi \log \left(x^2+1\right)+\tan ^{-1}(x) \left(4 \log \left(x^2+1\right)+8 \log \left(-\frac{2 i}{x-i}\right)+8 \log \left(1-i \exp(2 i \tan ^{-1}(x))\right)+2 i \pi \right)-4 \log ^2(x+1)-4 \log (x-i) \log ((1-i) (x+1))-4 \log (x+i) \log ((1+i) (x+1))+\log (64) \log (x-i)-4 \log (x) \log (x-i)+4 \log (x+1) \log (x-i)-3 i \pi \log (x-i)+\log (64) \log (x+i)-4 \log (x) \log (x+i)+4 \log (x+1) \log (x+i)-5 i \pi \log (x+i)+4 i \log (x+1) \log ((-1-i) (x+i))-4 \tan ^{-1}(x)^2+4 \pi \log \left(1+\exp(-2 i \tan ^{-1}(x))\right)+2 \pi \log \left(1-i \exp(2 i \tan ^{-1}(x))\right)-2 \pi \log \left(\sin \left(\tan ^{-1}(x)+\frac{\pi }{4}\right)\right)-\log (2) (7 \pi +\log (4))\right)-48 i \log ((1+i)-(1-i) x) \log (x+1)-\pi ^2 (14-9 i)\right)
+ \end{eqnarray}
+ where $x=r/r_\mathrm{s}$ and $\mathrm{G}$ is Catalan's constant.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi , catalan
+ use :: Dilogarithms , only : Dilogarithm
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ double precision :: radiusOuterScaleFree
+
+ radiusOuterScaleFree=+ radiusOuter &
+ & /self%scaleLength
+ energy =real( &
+ & -gravitationalConstantGalacticus &
+ & *self%scaleLength **5 &
+ & *self%densityNormalization **2 &
+ & *( &
+ & +Pi**2 &
+ & *( &
+ & + 48.0d0 *catalan &
+ & -dcmplx(14.0d0,- 9.0d0)*Pi**2 &
+ & -dcmplx( 0.0d0,+48.0d0)*log(dcmplx(1.0d0,1.0d0)-dcmplx(1.0d0,-1.0d0)*radiusOuterScaleFree)*log(1.0d0+radiusOuterScaleFree) &
+ & +12.0d0 &
+ & *( &
+ & -4.0d0*atan(radiusOuterScaleFree)**2 &
+ & - log(2.0d0)*(7.0d0*Pi + log(4.0d0)) &
+ & +4.0d0*Pi*log(1.0d0+ exp(-2.0d0*dcmplx(0.0d0,1.0d0)*atan(radiusOuterScaleFree))) &
+ & +2.0d0*Pi*log(1.0d0-dcmplx(0.0d0,1.0d0)*exp(+2.0d0*dcmplx(0.0d0,1.0d0)*atan(radiusOuterScaleFree))) &
+ & - dcmplx(0.0d0,3.0d0)*Pi*log(dcmplx(0.0d0,-1.0d0) +radiusOuterScaleFree) &
+ & +log (64.0d0 ) *log(dcmplx(+0.0d0,-1.0d0)+radiusOuterScaleFree)-log(dcmplx(+0.0d0,-1.0d0) +radiusOuterScaleFree) * 4.0d0 *log( radiusOuterScaleFree ) &
+ & -dcmplx( 0.0d0,5.0d0)*Pi*log(dcmplx(+0.0d0,+1.0d0)+radiusOuterScaleFree) &
+ & +log (64.0d0 ) *log(dcmplx(+0.0d0,+1.0d0)+radiusOuterScaleFree) &
+ & - 4.0d0 *log( +radiusOuterScaleFree)*log(dcmplx(+0.0d0,+1.0d0) +radiusOuterScaleFree) + 4.0d0 &
+ & *log(dcmplx(+0.0d0,-1.0d0) +radiusOuterScaleFree) *log(1.0d0+radiusOuterScaleFree ) &
+ & +dcmplx( 0.0d0,4.0d0) *log(dcmplx(-1.0d0,-1.0d0) * (dcmplx(+0.0d0,+1.0d0) +radiusOuterScaleFree)) *log(1.0d0+radiusOuterScaleFree ) &
+ & + 4.0d0 *log(dcmplx(+0.0d0,+1.0d0)+radiusOuterScaleFree)*log( 1.0d0+radiusOuterScaleFree )- 4.0d0 *log(1.0d0+radiusOuterScaleFree )**2 &
+ & - 4.0d0 *log(dcmplx(+0.0d0,-1.0d0)+radiusOuterScaleFree)*log(dcmplx(+1.0d0,-1.0d0)*(1.0d0+radiusOuterScaleFree)) &
+ & - 4.0d0 *log(dcmplx(+0.0d0,+1.0d0)+radiusOuterScaleFree)*log(dcmplx(+1.0d0,+1.0d0)*(1.0d0+radiusOuterScaleFree))+dcmplx(2.0d0,4.0d0)*Pi*log(1.0d0+radiusOuterScaleFree**2) &
+ & + 4.0d0 *log( radiusOuterScaleFree) *log(1.0d0+radiusOuterScaleFree**2) &
+ & - 4.0d0 *log( +1.0d0 +radiusOuterScaleFree) *log(1.0d0+radiusOuterScaleFree**2) &
+ & + log(1.0d0+radiusOuterScaleFree**2)**2 &
+ & + atan(radiusOuterScaleFree) &
+ & *(dcmplx(0.0d0,2.0d0)*Pi + 8.0d0*log(1 - dcmplx(0.0d0,1.0d0)*exp(2.0d0*dcmplx(0.0d0,1.0d0)*atan(radiusOuterScaleFree))) &
+ & +8.0d0*log(dcmplx(0.0d0,-2.0d0)/(dcmplx(0.0d0,-1.0d0)+radiusOuterScaleFree )) &
+ & +4.0d0*log( 1.0d0 +radiusOuterScaleFree**2)) &
+ & -2.0d0*Pi*log(sin(Pi/4.0d0+atan(radiusOuterScaleFree))) &
+ & ) &
+ & -dcmplx(0.0d0,48.0d0)*Dilogarithm(dcmplx(0.0d0,1.0d0)*exp(2.0d0*dcmplx(0.0d0,1.0d0)*atan(radiusOuterScaleFree))) &
+ & - 96.0d0 *Dilogarithm( dcmplx(-0.5d0,+0.5d0) *(dcmplx(0.0d0,-1.0d0)+radiusOuterScaleFree)) &
+ & - 96.0d0 *Dilogarithm( dcmplx(-0.5d0,-0.5d0) *(dcmplx(0.0d0,+1.0d0)+radiusOuterScaleFree)) &
+ & +dcmplx(0.0d0,48.0d0)*Dilogarithm((dcmplx(+0.0d0,+1.0d0)+radiusOuterScaleFree)/(dcmplx(0.0d0,-1.0d0)+radiusOuterScaleFree)) &
+ & -dcmplx(0.0d0,48.0d0)*Dilogarithm( dcmplx(+0.5d0,-0.5d0) *( +1.0d0 +radiusOuterScaleFree)) &
+ & +dcmplx(0.0d0,48.0d0)*Dilogarithm( dcmplx(+0.5d0,+0.5d0) *( +1.0d0 +radiusOuterScaleFree)) &
+ & ) &
+ & ) &
+ & /24.d0 &
+ & )
+ return
+ end function burkertEnergyPotential
+
+ subroutine burkertDescriptor(self,descriptor,includeClass,includeFileModificationTimes)
+ !!{
+ Return an input parameter list descriptor which could be used to recreate this object.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ class (massDistributionBurkert), intent(inout) :: self
+ type (inputParameters ), intent(inout) :: descriptor
+ logical , intent(in ), optional :: includeClass , includeFileModificationTimes
+ character(len=18 ) :: parameterLabel
+ type (inputParameters ) :: parameters
+
+ if (.not.present(includeClass).or.includeClass) call descriptor%addParameter('massDistribution','Burkert')
+ parameters=descriptor%subparameters('massDistribution')
+ write (parameterLabel,'(e17.10)') self%densityNormalization
+ call parameters%addParameter('densityNormalization',trim(adjustl(parameterLabel)))
+ write (parameterLabel,'(e17.10)') self%scaleLength
+ call parameters%addParameter('scaleLength' ,trim(adjustl(parameterLabel)))
+ return
+ end subroutine burkertDescriptor
+
diff --git a/source/mass_distributions.spherical.Einasto.F90 b/source/mass_distributions.spherical.Einasto.F90
new file mode 100644
index 0000000000..cadce55ff9
--- /dev/null
+++ b/source/mass_distributions.spherical.Einasto.F90
@@ -0,0 +1,868 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of an Einasto (e.g. \citealt{cardone_spherical_2005}) mass distribution class.
+ !!}
+
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+
+ An Einasto (e.g. \citealt{cardone_spherical_2005}) mass distribution class. The density profile is given by:
+ \begin{equation}
+ \rho_\mathrm{dark matter}(r) = \rho_{-2} \exp \left( - {2 \over \alpha} \left[ \left( {r \over r_{-2}} \right)^\alpha - 1
+ \right] \right).
+ \end{equation}
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionEinasto
+ !!{
+ The Einasto (e.g. \citealt{cardone_spherical_2005}) mass distribution.
+ !!}
+ private
+ double precision :: densityNormalization , scaleLength , &
+ & shapeParameter , massTotal_
+ double precision :: enclosedMassRadiusPrevious , enclosedMassPrevious
+ double precision :: massScaleFreeRadiusMinimum , massScaleFreeRadiusMaximum
+ double precision :: massScaleFreeMinimum , massScaleFreeMaximum
+ type (interpolator), allocatable :: massScaleFree_
+ double precision :: densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum
+ double precision :: densityScaleFreeMinimum , densityScaleFreeMaximum
+ type (interpolator), allocatable :: densityScaleFree_
+ double precision :: angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum
+ double precision :: angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum
+ type (interpolator), allocatable :: angularMomentumSpecificScaleFree_
+ double precision :: timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum
+ double precision :: timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum
+ type (interpolator), allocatable :: timeFreefallScaleFree_
+ contains
+ !![
+
+
+
+
+ !!]
+ procedure :: massTotal => einastoMassTotal
+ procedure :: density => einastoDensity
+ procedure :: densityGradientRadial => einastoDensityGradientRadial
+ procedure :: densityRadialMoment => einastoDensityRadialMoment
+ procedure :: massEnclosedBySphere => einastoMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => einastoRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => einastoRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => einastoRadiusFromSpecificAngularMomentum
+ procedure :: radiusFreefall => einastoRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => einastoRadiusFreefallIncreaseRate
+ procedure :: timeFreefallTabulate => einastoTimeFreefallTabulate
+ procedure :: timeFreefallMinimum => einastoTimeFreefallMinimum
+ procedure :: potentialIsAnalytic => einastoPotentialIsAnalytic
+ procedure :: potential => einastoPotential
+ procedure :: descriptor => einastoDescriptor
+ end type massDistributionEinasto
+
+ interface massDistributionEinasto
+ !!{
+ Constructors for the {\normalfont \ttfamily einasto} mass distribution class.
+ !!}
+ module procedure massDistributionEinastoConstructorParameters
+ module procedure massDistributionEinastoConstructorInternal
+ end interface massDistributionEinasto
+
+contains
+
+ function massDistributionEinastoConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily einasto} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Gamma_Functions , only : Gamma_Function
+ implicit none
+ type (massDistributionEinasto) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: mass , scaleLength , &
+ & densityNormalization, concentration, &
+ & virialRadius , shapeParameter
+ logical :: dimensionless
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ shapeParameter
+ The shape parameter, $\alpha$, of the Einasto profile.
+
+
+
+ densityNormalization
+ shapeParameter/4.0d0/Pi*(2.0d0/shapeParameter)**(3.0d0/shapeParameter)*exp(-2.0d0/shapeParameter)/Gamma_Function(3.0d0/shapeParameter)
+ The density normalization of the Einasto profile.
+
+
+
+ scaleLength
+ 1.0d0
+ The scale radius of the Einasto profile.
+
+
+
+ mass
+ 1.0d0
+ The mass of the Einasto profile.
+
+
+
+ concentration
+ 1.0d0
+ The concentration of the Einasto profile.
+
+
+
+ virialRadius
+ 1.0d0
+ The virial radius of the Einasto profile.
+
+
+
+ dimensionless
+ .true.
+ If true the Einasto profile is considered to be dimensionless.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ self=massDistributionEinasto(shapeParameter=shapeParameter,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+
+
+
+
+ !!]
+ return
+ end function massDistributionEinastoConstructorParameters
+
+ function massDistributionEinastoConstructorInternal(shapeParameter,scaleLength,concentration,densityNormalization,mass,virialRadius,dimensionless,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``einasto'' mass distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Gamma_Functions , only : Gamma_Function
+ implicit none
+ type (massDistributionEinasto ) :: self
+ double precision , intent(in ) :: shapeParameter
+ double precision , intent(in ), optional :: scaleLength , concentration, &
+ & densityNormalization, mass , &
+ & virialRadius
+ logical , intent(in ), optional :: dimensionless
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ ! Determine scale length
+ if ( &
+ & present(scaleLength ) &
+ & ) then
+ self%scaleLength =scaleLength
+ else if ( &
+ & present(concentration).and. &
+ & present(virialRadius ) &
+ & ) then
+ self%scaleLength=virialRadius/concentration
+ else
+ call Error_Report('no means to determine scale length'//{introspection:location})
+ end if
+ ! Determine density normalization.
+ if ( &
+ & present(densityNormalization) &
+ & ) then
+ self%densityNormalization=+densityNormalization
+ self%massTotal_ =+4.0d0*Pi*densityNormalization*self%scaleLength**3/shapeParameter/(2.0d0/shapeParameter)**(3.0d0/shapeParameter)*Gamma_Function(3.0d0/shapeParameter)*exp(2.0d0/shapeParameter)
+ else if ( &
+ & present(mass ).and. &
+ & present(virialRadius ) &
+ & ) then
+ radiusScaleFree =+virialRadius/self%scaleLength
+ self%densityNormalization=+mass/4.0d0/Pi/self%scaleLength**3*shapeParameter*exp(-2.0d0/shapeParameter)*(2.0d0/shapeParameter)**(3.0d0/shapeParameter)/Gamma_Function(3.0d0/shapeParameter)
+ self%massTotal_ =+mass
+ else
+ call Error_Report('either "densityNormalization", or "mass" and "virialRadius" must be specified'//{introspection:location})
+ end if
+ ! Determine if profile is dimensionless.
+ if (present(dimensionless )) then
+ self%dimensionless=dimensionless
+ else
+ self%dimensionless=.false.
+ end if
+
+ ! Initialize memoized results.
+ self%enclosedMassPrevious =-huge(0.0d0)
+ self%enclosedMassRadiusPrevious =-huge(0.0d0)
+ self%densityScaleFreeMinimum =+huge(0.0d0)
+ self%densityScaleFreeMaximum =-huge(0.0d0)
+ self%densityScaleFreeRadiusMinimum =+1.0d0
+ self%densityScaleFreeRadiusMaximum =+1.0d0
+ self%angularMomentumSpecificScaleFreeMinimum =+huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeMaximum =-huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=+1.0d0
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=+1.0d0
+ self%timeFreefallScaleFreeMinimum =+huge(0.0d0)
+ self%timeFreefallScaleFreeMaximum =-huge(0.0d0)
+ self%timeFreefallScaleFreeRadiusMinimum =+1.0d0
+ self%timeFreefallScaleFreeRadiusMaximum =+1.0d0
+ self%massScaleFreeMinimum =+huge(0.0d0)
+ self%massScaleFreeMaximum =-huge(0.0d0)
+ self%massScaleFreeRadiusMinimum =+1.0d0
+ self%massScaleFreeRadiusMaximum =+1.0d0
+ return
+ end function massDistributionEinastoConstructorInternal
+
+ double precision function einastoMassTotal(self)
+ !!{
+ Return the total mass in an Einasto mass distribution.
+ !!}
+ implicit none
+ class(massDistributionEinasto), intent(inout) :: self
+
+ einastoMassTotal=self%massTotal_
+ return
+ end function einastoMassTotal
+
+ double precision function einastoDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Einasto mass distribution.
+ !!}
+ implicit none
+ class (massDistributionEinasto ), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radiusScaleFree
+
+ ! Compute the density at this position.
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ density =+self %densityNormalization &
+ & *exp( &
+ & -(2.0d0/self%shapeParameter) &
+ & *( &
+ & +radiusScaleFree**self%shapeParameter &
+ & -1.0d0 &
+ & ) &
+ & )
+ return
+ end function einastoDensity
+
+ double precision function einastoDensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Einasto \citep{navarro_structure_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionEinasto), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ radiusScaleFree =+coordinates%rSpherical() &
+ & /self %scaleLength
+ if (radiusScaleFree <= 0.0d0) then
+ densityGradientRadial=+0.0d0
+ else
+ densityGradientRadial=-2.0d0 &
+ & *radiusScaleFree**self%shapeParameter
+ if (.not.logarithmic_) densityGradientRadial=+ densityGradientRadial &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ end if
+ return
+ end function einastoDensityGradientRadial
+
+ double precision function einastoDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Computes radial moments of the density in an Einasto \citep{navarro_structure_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionEinasto), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: radialMomentMinimum, radialMomentMaximum
+
+ densityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ if ((.not.present(radiusMinimum) .or. radiusMinimum <= 0.0d0) .and. moment <= -1) then
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ return
+ else
+ call Error_Report('radial moment is infinite'//{introspection:location})
+ end if
+ end if
+ if (present(radiusMinimum)) then
+ radialMomentMinimum=radialMomentScaleFree(radiusMinimum/self%scaleLength)
+ else
+ radialMomentMinimum=radialMomentScaleFree( 0.0d0)
+ end if
+ if (present(radiusMaximum)) then
+ radialMomentMaximum=radialMomentScaleFree(radiusMaximum/self%scaleLength)
+ else
+ radialMomentMaximum=0.0d0
+ end if
+ densityRadialMoment=+self%densityNormalization &
+ & *self%scaleLength **(moment+1.0d0) &
+ & *( &
+ & +radialMomentMaximum &
+ & -radialMomentMinimum &
+ & )
+ return
+
+ contains
+
+ double precision function radialMomentScaleFree(radius)
+ !!{
+ Provides the scale-free part of the radial moment of the Einasto density profile.
+ !!}
+ use :: Gamma_Functions, only : Gamma_Function, Gamma_Function_Incomplete
+ implicit none
+ double precision, intent(in ) :: radius
+
+ radialMomentScaleFree=-exp ( 2.0d0 /self%shapeParameter ) &
+ & *Gamma_Function ((1.0d0+moment)/self%shapeParameter ) &
+ & *Gamma_Function_Incomplete((1.0d0+moment)/self%shapeParameter,2.0d0*radius**self%shapeParameter/self%shapeParameter) &
+ & / self%shapeParameter &
+ & / ( 2.0d0 /self%shapeParameter )**((1.0d0+moment)/self%shapeParameter)
+ return
+ end function radialMomentScaleFree
+
+ end function einastoDensityRadialMoment
+
+ double precision function einastoMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for einasto mass distributions.
+ !!}
+ implicit none
+ class (massDistributionEinasto), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radiusScaleFree
+
+ if (radius /= self%enclosedMassRadiusPrevious) then
+ self%enclosedMassRadiusPrevious=+ radius
+ radiusScaleFree =+ radius &
+ & /self%scaleLength
+ self%enclosedMassPrevious =+ massEnclosedScaleFree(radiusScaleFree,self%shapeParameter) &
+ & *self%densityNormalization &
+ & *self%scaleLength **3
+ end if
+ mass=self%enclosedMassPrevious
+ return
+ end function einastoMassEnclosedBySphere
+
+ double precision function einastoRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for einasto mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range , rangeTypeLogarithmic
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionEinasto), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision :: mass_
+ double precision , allocatable , dimension(:) :: radii , masses
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: massScaleFree , mass_
+ integer :: countRadii , i
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_= mass
+ else if (present(massFractional)) then
+ mass_=massFractional*self%massTotal_
+ else
+ call Error_Report('either mass or massFractional must be supplied'//{introspection:location})
+ end if
+ massScaleFree=+ mass_ &
+ & /self%densityNormalization &
+ & /self%scaleLength **3
+ if ( &
+ & massScaleFree <= self%massScaleFreeMinimum &
+ & .or. &
+ & massScaleFree > self%massScaleFreeMaximum &
+ & ) then
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMinimum,self%shapeParameter) >= massScaleFree)
+ self%massScaleFreeRadiusMinimum=0.5d0*self%massScaleFreeRadiusMinimum
+ end do
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMaximum,self%shapeParameter) < massScaleFree)
+ self%massScaleFreeRadiusMaximum=2.0d0*self%massScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%massScaleFreeRadiusMaximum/self%massScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%massScaleFree_)) deallocate(self%massScaleFree_)
+ allocate( radii (countRadii))
+ allocate( masses (countRadii))
+ allocate(self%massScaleFree_ )
+ radii =Make_Range(self%massScaleFreeRadiusMinimum,self%massScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ masses (i)=massEnclosedScaleFree( radii(i),self%shapeParameter)
+ end do
+ self%massScaleFreeMinimum =masses ( 1 )
+ self%massScaleFreeMaximum =masses (countRadii )
+ self%massScaleFree_ =interpolator (masses ,radii )
+ end if
+ radius=+self%massScaleFree_%interpolate(massScaleFree) &
+ & *self%scaleLength
+ return
+ end function einastoRadiusEnclosingMass
+
+ double precision function einastoRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for Einasto mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionEinasto), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+ double precision , allocatable , dimension(:) :: radii , densities
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: densityScaleFree
+ integer :: countRadii , i
+
+ densityScaleFree=+density &
+ & /self%densityNormalization
+ if ( &
+ & densityScaleFree <= self%densityScaleFreeMinimum &
+ & .or. &
+ & densityScaleFree > self%densityScaleFreeMaximum &
+ & ) then
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMinimum,self%shapeParameter) < densityScaleFree)
+ self%densityScaleFreeRadiusMinimum=0.5d0*self%densityScaleFreeRadiusMinimum
+ end do
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMaximum,self%shapeParameter) >= densityScaleFree)
+ self%densityScaleFreeRadiusMaximum=2.0d0*self%densityScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%densityScaleFreeRadiusMaximum/self%densityScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%densityScaleFree_)) deallocate(self%densityScaleFree_)
+ allocate( radii (countRadii))
+ allocate( densities (countRadii))
+ allocate(self%densityScaleFree_ )
+ radii = Make_Range(self%densityScaleFreeRadiusMinimum,self%densityScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ densities (i)=-densityEnclosedScaleFree(radii (i),self%shapeParameter)
+ end do
+ self%densityScaleFreeMinimum =-densities (countRadii )
+ self%densityScaleFreeMaximum =-densities ( 1 )
+ self%densityScaleFree_ = interpolator (densities ,radii )
+ end if
+ radius=+self%densityScaleFree_%interpolate(-densityScaleFree) &
+ & *self%scaleLength
+ return
+ end function einastoRadiusEnclosingDensity
+
+ double precision function massEnclosedScaleFree(radius,shapeParameter) result(mass)
+ !!{
+ Evaluate the mass enclosed by a given radius in a scale-free Einasto mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Gamma_Functions , only : Gamma_Function, Gamma_Function_Incomplete_Complementary
+ implicit none
+ double precision, intent(in ) :: radius, shapeParameter
+
+ mass =+4.0d0 &
+ & *Pi &
+ & / shapeParameter &
+ & / (2.0d0/shapeParameter )**(3.0d0/shapeParameter) &
+ & *exp (2.0d0/shapeParameter ) &
+ & *Gamma_Function (3.0d0/shapeParameter ) &
+ & *Gamma_Function_Incomplete_Complementary(3.0d0/shapeParameter,2.0d0*radius**shapeParameter/shapeParameter)
+ return
+ end function massEnclosedScaleFree
+
+ double precision function densityEnclosedScaleFree(radius,shapeParameter) result(density)
+ !!{
+ Evaluate the mean enclosed density at a given radius in a scale-free Einasto mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius, shapeParameter
+
+ density=+3.0d0 &
+ & /4.0d0 &
+ & /Pi &
+ & *massEnclosedScaleFree(radius,shapeParameter) &
+ & / radius **3
+ return
+ end function densityEnclosedScaleFree
+
+ double precision function einastoRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for einasto mass distributions.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ implicit none
+ class (massDistributionEinasto), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+ double precision , allocatable , dimension(:) :: radii , angularMomentaSpecific
+ double precision , parameter :: countRadiiPerDecade =100.0d0
+ double precision :: angularMomentumSpecificScaleFree
+ integer :: countRadii , i
+
+ if (angularMomentumSpecific > 0.0d0) then
+ angularMomentumSpecificScaleFree=+angularMomentumSpecific &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & / self%scaleLength **2
+ if ( &
+ & angularMomentumSpecificScaleFree <= self%angularMomentumSpecificScaleFreeMinimum &
+ & .or. &
+ & angularMomentumSpecificScaleFree > self%angularMomentumSpecificScaleFreeMaximum &
+ & ) then
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMinimum,self%shapeParameter) >= angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=0.5d0*self%angularMomentumSpecificScaleFreeRadiusMinimum
+ end do
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMaximum,self%shapeParameter) < angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=2.0d0*self%angularMomentumSpecificScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%angularMomentumSpecificScaleFreeRadiusMaximum/self%angularMomentumSpecificScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%angularMomentumSpecificScaleFree_)) deallocate(self%angularMomentumSpecificScaleFree_)
+ allocate( radii (countRadii))
+ allocate( angularMomentaSpecific (countRadii))
+ allocate(self%angularMomentumSpecificScaleFree_ )
+ radii =Make_Range(self%angularMomentumSpecificScaleFreeRadiusMinimum,self%angularMomentumSpecificScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ angularMomentaSpecific (i)=angularMomentumSpecificEnclosedScaleFree( radii(i),self%shapeParameter)
+ end do
+ self%angularMomentumSpecificScaleFreeMinimum =angularMomentaSpecific ( 1 )
+ self%angularMomentumSpecificScaleFreeMaximum =angularMomentaSpecific ( countRadii )
+ self%angularMomentumSpecificScaleFree_ =interpolator (angularMomentaSpecific,radii )
+ end if
+ radius=+self%angularMomentumSpecificScaleFree_%interpolate(angularMomentumSpecificScaleFree) &
+ & *self%scaleLength
+ else
+ radius=+0.0d0
+ end if
+ return
+ end function einastoRadiusFromSpecificAngularMomentum
+
+ double precision function angularMomentumSpecificEnclosedScaleFree(radius,shapeParameter) result(angularMomentumSpecific)
+ !!{
+ Evaluate the specific angular momentum at a given radius in a scale-free Einasto mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius, shapeParameter
+
+ angularMomentumSpecific=+sqrt( &
+ & +massEnclosedScaleFree(radius,shapeParameter) &
+ & * radius &
+ & )
+ return
+ end function angularMomentumSpecificEnclosedScaleFree
+
+ logical function einastoPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionEinasto), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function einastoPotentialIsAnalytic
+
+ double precision function einastoPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an einasto mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionEinasto ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision :: radiusScaleFree
+
+ if (present(status)) status=structureErrorCodeSuccess
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ potential=+potentialScaleFree (radiusScaleFree,self%shapeParameter) &
+ & *self%densityNormalization &
+ & *self%scaleLength **2
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
+ return
+ end function einastoPotential
+
+ double precision function potentialScaleFree(radius,shapeParameter) result(potential)
+ !!{
+ Compute the potential in a scale-free Einasto mass distribution. Uses the results from \cite{retana-montenegro_analytical_2012},
+ their equations (19) and (20), but with different normalizations for the density and scale radius.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Gamma_Functions , only : Gamma_Function, Gamma_Function_Incomplete, Gamma_Function_Incomplete_Complementary
+ implicit none
+ double precision, intent(in ) :: radius, shapeParameter
+
+ if (radius <= 0.0d0) then
+ potential=-2.0d0 **(+1.0d0-2.0d0/shapeParameter ) &
+ & *shapeParameter **( 2.0d0/shapeParameter ) &
+ & *exp ( 2.0d0/shapeParameter ) &
+ & *Pi &
+ & *Gamma_Function (+1.0d0+2.0d0/shapeParameter )
+ else
+ potential=-4.0d0 &
+ & *Pi &
+ & *exp(2.0d0/shapeParameter) &
+ & / shapeParameter &
+ & / radius &
+ & *( &
+ & + radius &
+ & *(2.0d0/shapeParameter) **( -2.0d0/shapeParameter ) &
+ & *Gamma_Function_Incomplete ( +2.0d0/shapeParameter,2.0d0*radius**shapeParameter/shapeParameter) &
+ & *Gamma_Function ( +2.0d0/shapeParameter ) &
+ & +shapeParameter **( +3.0d0/shapeParameter ) &
+ & / 8.0d0 **( +1.0d0/shapeParameter ) &
+ & *Gamma_Function_Incomplete_Complementary( +3.0d0/shapeParameter,2.0d0*radius**shapeParameter/shapeParameter) &
+ & *Gamma_Function ( +3.0d0/shapeParameter ) &
+ & )
+ end if
+ return
+ end function potentialScaleFree
+
+ double precision function potentialDifferenceScaleFree(radius1,radius2,shapeParameter) result(potential)
+ !!{
+ Compute the potential difference in a scale-free Einasto mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius1 , radius2, &
+ & shapeParameter
+
+ potential=+potentialScaleFree(radius1,shapeParameter) &
+ & -potentialScaleFree(radius2,shapeParameter)
+ return
+ end function potentialDifferenceScaleFree
+
+ double precision function einastoRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in an Einasto mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionEinasto), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0 &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (timeScaleFree <= self%timeFreefallMinimum()) then
+ radius=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radius=+self%timeFreefallScaleFree_%interpolate(timeScaleFree) &
+ & *self%scaleLength
+ return
+ end function einastoRadiusFreefall
+
+ double precision function einastoRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an einasto mass
+ distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionEinasto), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0 &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (timeScaleFree <= self%timeFreefallMinimum()) then
+ radiusIncreaseRate=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radiusIncreaseRate=+self%timeFreefallScaleFree_%derivative(timeScaleFree) &
+ & *self%scaleLength &
+ & / timeScale
+ return
+ end function einastoRadiusFreefallIncreaseRate
+
+ double precision function einastoTimeFreefallMinimum(self) result(timeScaleFreeMinimum)
+ !!{
+ Compute the minimum freefall time in a scale-free Einasto profile.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class(massDistributionEinasto), intent(inout) :: self
+
+ timeScaleFreeMinimum=+sqrt( &
+ & + 3.0d0 &
+ & /16.0d0 &
+ & *Pi &
+ & ) &
+ & *exp( &
+ & -1.0d0 &
+ & /self%shapeParameter &
+ & )
+ return
+ end function einastoTimeFreefallMinimum
+
+ subroutine einastoTimeFreefallTabulate(self,timeScaleFree)
+ !!{
+ Tabulate the freefall radius at the given {\normalfont \ttfamily time} in an Einasto mass distribution.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionEinasto), intent(inout) :: self
+ double precision , intent(in ) :: timeScaleFree
+ double precision , allocatable , dimension(:) :: radii , timesFreefall
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: radiusStart
+ integer :: countRadii , i
+ type (integrator ) :: integrator_
+
+ if ( &
+ & timeScaleFree <= self%timeFreefallScaleFreeMinimum &
+ & .or. &
+ & timeScaleFree > self%timeFreefallScaleFreeMaximum &
+ & ) then
+ integrator_=integrator(timeFreeFallIntegrand,toleranceRelative=1.0d-6)
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMinimum) >= timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMinimum=0.5d0*self%timeFreefallScaleFreeRadiusMinimum
+ end do
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMaximum) < timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMaximum=2.0d0*self%timeFreefallScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%timeFreefallScaleFreeRadiusMaximum/self%timeFreefallScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%timeFreefallScaleFree_)) deallocate(self%timeFreefallScaleFree_)
+ allocate( radii (countRadii))
+ allocate( timesFreefall (countRadii))
+ allocate(self%timeFreefallScaleFree_ )
+ radii=Make_Range(self%timeFreefallScaleFreeRadiusMinimum,self%timeFreefallScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ timesFreefall(i)=timeFreefallScaleFree(radii(i))
+ end do
+ self%timeFreefallScaleFreeMinimum=timesFreefall( 1 )
+ self%timeFreefallScaleFreeMaximum=timesFreefall( countRadii )
+ self%timeFreefallScaleFree_ =interpolator (timesFreefall,radii)
+ end if
+ return
+
+ contains
+
+ double precision function timeFreefallScaleFree(radius)
+ !!{
+ Evaluate the freefall time from a given radius in a scale-free Einasto mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ radiusStart = radius
+ timeFreefallScaleFree=integrator_%integrate(0.0d0,radius)
+ return
+ end function timeFreefallScaleFree
+
+ double precision function timeFreeFallIntegrand(radius)
+ !!{
+ Integrand used to find the freefall time in a scale-free Einasto mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision :: potentialDifference
+
+ if (radius == 0.0d0) then
+ timeFreeFallIntegrand=+0.0d0
+ else
+ potentialDifference=+potentialDifferenceScaleFree(radiusStart,radius,self%shapeParameter)
+ if (potentialDifference > 0.0d0) then
+ timeFreeFallIntegrand=+1.0d0 &
+ & /sqrt( &
+ & +2.0d0 &
+ & *potentialDifference &
+ & )
+ else
+ timeFreeFallIntegrand=+0.0d0
+ end if
+ end if
+ return
+ end function timeFreeFallIntegrand
+
+ end subroutine einastoTimeFreefallTabulate
+
+ subroutine einastoDescriptor(self,descriptor,includeClass,includeFileModificationTimes)
+ !!{
+ Return an input parameter list descriptor which could be used to recreate this object.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ class (massDistributionEinasto), intent(inout) :: self
+ type (inputParameters ), intent(inout) :: descriptor
+ logical , intent(in ), optional :: includeClass , includeFileModificationTimes
+ character(len=18 ) :: parameterLabel
+ type (inputParameters ) :: parameters
+
+ if (.not.present(includeClass).or.includeClass) call descriptor%addParameter('massDistribution','Einasto')
+ parameters=descriptor%subparameters('massDistribution')
+ write (parameterLabel,'(e17.10)') self%densityNormalization
+ call parameters%addParameter('densityNormalization',trim(adjustl(parameterLabel)))
+ write (parameterLabel,'(e17.10)') self%scaleLength
+ call parameters%addParameter('scaleLength' ,trim(adjustl(parameterLabel)))
+ write (parameterLabel,'(e17.10)') self%shapeParameter
+ call parameters%addParameter('shapeParameter' ,trim(adjustl(parameterLabel)))
+ return
+ end subroutine einastoDescriptor
+
diff --git a/source/mass_distributions.spherical.Enzo_hydrostatic.F90 b/source/mass_distributions.spherical.Enzo_hydrostatic.F90
new file mode 100644
index 0000000000..64a26e51f1
--- /dev/null
+++ b/source/mass_distributions.spherical.Enzo_hydrostatic.F90
@@ -0,0 +1,222 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a mass distribution class that mimics the ``hydrostatic'' profile used by the Enzo simulation code.
+ !!}
+
+ !![
+
+
+ A hot halo mass distribution class which adopts a spherically symmetric density profile for the hot halo motivated by the
+ ``hydrostatic'' profile available in the \gls{enzo} code. Specifically,
+ \begin{equation}
+ \rho_\mathrm{hot halo}(r) \propto \left\{ \begin{array}{ll} T^{-1} r^{-1} & \hbox{ if } r > r_\mathrm{core} \\ T^{-1}
+ r_\mathrm{core}^{-1} & \hbox{ if } r \le r_\mathrm{core}, \end{array} \right.
+ \end{equation}
+ where the core radius, $r_\mathrm{core}$, is set using the selected cored profile core radius method (see
+ \refPhysics{hotHaloMassDistributionCoreRadius}). The profile is normalized such that the current mass in the
+ hot gas profile is contained within the outer radius of the hot halo, $r_\mathrm{hot, outer}$. Note that the \gls{enzo}
+ hydrostatic profile does not include this core, but without introducing this the profile mass can be divergent at small
+ radii.
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionEnzoHydrostatic
+ !!{
+ The Enzo hydrostatic mass distribution.
+ !!}
+ private
+ double precision :: radiusScale , radiusOuter , &
+ & mass , normalizationDensity_
+ logical :: truncateAtOuterRadius, normalizationDensityComputed
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: normalizationDensity => enzoHydrostaticNormalizationDensity
+ procedure :: density => enzoHydrostaticDensity
+ procedure :: densityGradientRadial => enzoHydrostaticDensityGradientRadial
+ end type massDistributionEnzoHydrostatic
+
+ interface massDistributionEnzoHydrostatic
+ !!{
+ Constructors for the {\normalfont \ttfamily enzoHydrostatic} mass distribution class.
+ !!}
+ module procedure enzoHydrostaticConstructorParameters
+ module procedure enzoHydrostaticConstructorInternal
+ end interface massDistributionEnzoHydrostatic
+
+contains
+
+ function enzoHydrostaticConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily enzoHydrostatic} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionEnzoHydrostatic) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+ double precision :: radiusScale , radiusOuter, &
+ & mass
+ logical :: truncateAtOuterRadius
+
+ !![
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ mass
+ The mass within the outer radius.
+
+
+
+ radiusOuter
+ The outer radius of the mass distribution.
+
+
+
+ radiusScale
+ The core radius of the mass distribution.
+
+
+
+ truncateAtOuterRadius
+ .false.
+ If true then the mass distribution is truncated beyond the outer radius.
+
+
+ !!]
+ self=massDistributionEnzoHydrostatic(mass,radiusOuter,radiusScale,truncateAtOuterRadius,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ !![
+
+ !!]
+ return
+ end function enzoHydrostaticConstructorParameters
+
+ function enzoHydrostaticConstructorInternal(mass,radiusOuter,radiusScale,truncateAtOuterRadius,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``enzoHydrostatic'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionEnzoHydrostatic) :: self
+ double precision , intent(in ) :: radiusScale , radiusOuter, &
+ & mass
+ logical , intent(in ), optional :: truncateAtOuterRadius
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! This distribution profile is never dimensionless.
+ self%dimensionless =.false.
+ self%normalizationDensityComputed=.false.
+ return
+ end function enzoHydrostaticConstructorInternal
+
+ double precision function enzoHydrostaticNormalizationDensity(self) result(normalizationDensity)
+ !!{
+ Return the density normalization in a {\normalfont \ttfamily enzoHydrostatic} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionEnzoHydrostatic), intent(inout) :: self
+ double precision :: massEnclosed
+
+ if (.not.self%normalizationDensityComputed) then
+ self%normalizationDensityComputed=.true.
+ if ( &
+ & self%mass <= 0.0d0 &
+ & .or. &
+ & self%radiusOuter <= 0.0d0 &
+ & ) then
+ normalizationDensity=+0.0d0
+ else
+ ! Compute the normalization. Initially set this to 1, then compute the enclosed mass, then compute the multiplicative
+ ! factor needed to get the required mass.
+ self%normalizationDensity_=+1.0d0
+ massEnclosed =+self%massEnclosedBySphere(self%radiusOuter)
+ self%normalizationDensity_=+self%mass &
+ & / massEnclosed
+ end if
+ end if
+ normalizationDensity=self%normalizationDensity_
+ return
+ end function enzoHydrostaticNormalizationDensity
+
+ double precision function enzoHydrostaticDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Enzo hydrostatic mass distribution.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ class (massDistributionEnzoHydrostatic), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: coordinatesEffective
+ double precision :: radiusEffective
+
+ radiusEffective = max(coordinates%rSpherical(),self%radiusScale)
+ coordinatesEffective= coordinates &
+ & * radiusEffective &
+ & /coordinates%rSpherical ()
+ density =+self %normalizationDensity( ) &
+ & /self%kinematicsDistribution_%temperature (coordinatesEffective) &
+ & /radiusEffective **3
+
+ return
+ end function enzoHydrostaticDensity
+
+ double precision function enzoHydrostaticDensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an EnzoHydrostatic \citep{navarro_structure_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionEnzoHydrostatic), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ if (coordinates%rSpherical() > self%radiusScale) then
+ densityGradientRadial =-self %kinematicsDistribution_%temperatureGradientLogarithmic(coordinates) &
+ & -3.0d0
+ if (.not.logarithmic_) densityGradientRadial=+ densityGradientRadial &
+ & *self %density (coordinates) &
+ & /coordinates %rSpherical ( )
+ else
+ densityGradientRadial =+0.0d0
+ end if
+ return
+ end function enzoHydrostaticDensityGradientRadial
diff --git a/source/mass_distributions.spherical.F90 b/source/mass_distributions.spherical.F90
index a324e75d20..3fc4059a50 100644
--- a/source/mass_distributions.spherical.F90
+++ b/source/mass_distributions.spherical.F90
@@ -34,23 +34,61 @@
contains
!![
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
!!]
- procedure :: symmetry => sphericalSymmetry
- procedure :: massEnclosedBySphere => sphericalMassEnclosedBySphere
- procedure :: radiusHalfMass => sphericalRadiusHalfMass
- procedure :: acceleration => sphericalAcceleration
- procedure :: tidalTensor => sphericalTidalTensor
- procedure :: radiusEnclosingMass => sphericalRadiusEnclosingMass
- procedure :: positionSample => sphericalPositionSample
+ procedure :: symmetry => sphericalSymmetry
+ procedure :: isSphericallySymmetric => sphericalIsSphericallySymmetric
+ procedure :: densityGradientRadial => sphericalDensityGradientRadial
+ procedure :: densityGradientRadialNumerical => sphericalDensityGradientRadialNumerical
+ procedure :: massEnclosedBySphere => sphericalMassEnclosedBySphere
+ procedure :: massEnclosedBySphereNumerical => sphericalMassEnclosedBySphereNumerical
+ procedure :: densityRadialMoment => sphericalDensityRadialMoment
+ procedure :: densityRadialMomentNumerical => sphericalDensityRadialMomentNumerical
+ procedure :: potential => sphericalPotential
+ procedure :: potentialNumerical => sphericalPotentialNumerical
+ procedure :: fourierTransform => sphericalFourierTransform
+ procedure :: fourierTransformNumerical => sphericalFourierTransformNumerical
+ procedure :: radiusFreefall => sphericalRadiusFreefall
+ procedure :: radiusFreefallNumerical => sphericalRadiusFreefallNumerical
+ procedure :: radiusFreefallIncreaseRate => sphericalRadiusFreefallIncreaseRate
+ procedure :: radiusFreefallIncreaseRateNumerical => sphericalRadiusFreefallIncreaseRateNumerical
+ procedure :: energy => sphericalEnergy
+ procedure :: energyNumerical => sphericalEnergyNumerical
+ procedure :: energyPotential => sphericalEnergyPotential
+ procedure :: energyKinetic => sphericalEnergyKinetic
+ procedure :: energyPotentialNumerical => sphericalEnergyPotentialNumerical
+ procedure :: energyKineticNumerical => sphericalEnergyKineticNumerical
+ procedure :: densitySphericalAverage => sphericalDensitySphericalAverage
+ procedure :: potentialDifferenceNumerical => sphericalPotentialDifferenceNumerical
+ procedure :: surfaceDensity => sphericalSurfaceDensity
+ procedure :: chandrasekharIntegral => sphericalChandrasekharIntegral
+ procedure :: acceleration => sphericalAcceleration
+ procedure :: tidalTensor => sphericalTidalTensor
+ procedure :: positionSample => sphericalPositionSample
+ procedure :: rotationCurve => sphericalRotationCurve
+ procedure :: rotationCurveNumerical => sphericalRotationCurve
+ procedure :: rotationCurveGradient => sphericalRotationCurveGradient
+ procedure :: radiusHalfMass => sphericalRadiusHalfMass
end type massDistributionSpherical
! Module scope variables used in integration and root finding.
class (massDistributionSpherical), pointer :: self_
- double precision :: massTarget
- !$omp threadprivate(self_,massTarget)
+ double precision :: time_, radiusFreefall_
+ !$omp threadprivate(self_,time_,radiusFreefall_)
contains
@@ -67,7 +105,85 @@ function sphericalSymmetry(self)
return
end function sphericalSymmetry
- double precision function sphericalMassEnclosedBySphere(self,radius,componentType,massType)
+ logical function sphericalIsSphericallySymmetric(self) result(isSphericallySymmetric)
+ !!{
+ Return true if the distribution is spherically symmetric.
+ !!}
+ implicit none
+ class(massDistributionSpherical), intent(inout) :: self
+
+ isSphericallySymmetric=.true.
+ return
+ end function sphericalIsSphericallySymmetric
+
+ double precision function sphericalDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the radial density gradient at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+
+ densityGradient=self%densityGradientRadialNUmerical(coordinates,logarithmic)
+ return
+ end function sphericalDensityGradientRadial
+
+ double precision function sphericalDensityGradientRadialNumerical(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the radial density gradient at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution using a numerical calculation.
+ !!}
+ use :: Numerical_Differentiation, only : differentiator
+ implicit none
+ class (massDistributionSpherical ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision , parameter :: radiusLogarithmicStep=0.1d0
+ type (differentiator ) :: differentiator_
+ double precision :: radius
+ !![
+
+ !!]
+
+ self_ => self
+ radius = coordinates %rSpherical( )
+ differentiator_ = differentiator (densityEvaluate )
+ densityGradient = +differentiator_%derivative(log(radius) ,radiusLogarithmicStep)
+ if (.not.logarithmic_) &
+ & densityGradient=+ densityGradient &
+ & *self%density (coordinates) &
+ & / radius
+ return
+ end function sphericalDensityGradientRadialNumerical
+
+ double precision function densityEvaluate(radiusLogarithmic) result(density)
+ !!{
+ GSL-callable function to evaluate the density of the dark matter profile.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ double precision , intent(in ), value :: radiusLogarithmic
+ type (coordinateSpherical) :: coordinates
+
+ coordinates=[exp(radiusLogarithmic),0.0d0,0.0d0]
+ density =log(self_%density(coordinates))
+ return
+ end function densityEvaluate
+
+ double precision function sphericalMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for spherically-symmetric mass
+ distributions.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ mass=self%massEnclosedBySphereNumerical(radius)
+ return
+ end function sphericalMassEnclosedBySphere
+
+ double precision function sphericalMassEnclosedBySphereNumerical(self,radius) result(mass)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for spherically-symmetric mass
distributions using numerical integration.
@@ -75,25 +191,35 @@ double precision function sphericalMassEnclosedBySphere(self,radius,componentTyp
use :: Numerical_Constants_Math, only : Pi
use :: Numerical_Integration , only : integrator
implicit none
- class (massDistributionSpherical ), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (integrator ) :: integrator_
-
- if (.not.self%matches(componentType,massType)) then
- sphericalMassEnclosedBySphere=0.0d0
- return
- end if
- self_ => self
- integrator_ = integrator(sphericalMassEnclosedBySphereIntegrand,toleranceRelative=1.0d-6)
- sphericalMassEnclosedBySphere = +4.0d0 &
- & *Pi &
- & *integrator_%integrate(0.0d0,radius)
+ class (massDistributionSpherical), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ type (integrator ) :: integrator_
+
+ self_ => self
+ integrator_ = integrator(sphericalMassEnclosedBySphereIntegrand,toleranceRelative=1.0d-6)
+ mass = +4.0d0 &
+ & *Pi &
+ & *integrator_%integrate(0.0d0,radius)
return
- end function sphericalMassEnclosedBySphere
+ end function sphericalMassEnclosedBySphereNumerical
- double precision function sphericalMassEnclosedBySphereIntegrand(radius)
+ double precision function sphericalDensitySphericalAverage(self,radius)
+ !!{
+ Computes the density averaged over a spherical shell.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateSpherical
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical ) :: position
+
+ ! For a spherical mass distribution, the density averaged over a spherical shell, is just the regular density at that radius.
+ position =[radius,0.0d0,0.0d0]
+ sphericalDensitySphericalAverage=self%density(position)
+ return
+ end function sphericalDensitySphericalAverage
+
+ double precision function sphericalMassEnclosedBySphereIntegrand(radius) result(integrand)
!!{
Enclosed mass integrand for spherical mass distributions.
!!}
@@ -102,82 +228,140 @@ double precision function sphericalMassEnclosedBySphereIntegrand(radius)
double precision , intent(in ) :: radius
type (coordinateSpherical) :: position
- position =[radius,0.0d0,0.0d0]
- sphericalMassEnclosedBySphereIntegrand=+radius **2 &
- & *self_%density(position)
+ if (radius > 0.0d0) then
+ position =[radius,0.0d0,0.0d0]
+ integrand=+radius**2 &
+ & *self_%density(position)
+ else
+ integrand=+0.0d0
+ end if
return
end function sphericalMassEnclosedBySphereIntegrand
- double precision function sphericalRadiusEnclosingMass(self,mass,componentType,massType)
+ double precision function sphericalRadiusHalfMass(self)
!!{
- Computes the radius enclosing a given mass in a spherically symmetric mass distribution using numerical root finding.
+ Computes the half-mass radius of a spherically symmetric mass distribution using numerical root finding.
!!}
- use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder, &
- & GSL_Root_fSolver_Brent
implicit none
- class (massDistributionSpherical ), intent(inout), target :: self
- double precision , intent(in ) :: mass
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (rootFinder ), save :: finder
- logical , save :: finderConstructed=.false.
- !$omp threadprivate(finder,finderConstructed)
- double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-6
-
- if (mass <= 0.0d0 .or. .not.self%matches(componentType,massType)) then
- sphericalRadiusEnclosingMass=0.0d0
- return
- end if
- if (.not.finderConstructed) then
- finder =rootFinder( &
- & rootFunction =sphericalMassRoot , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & solverType =GSL_Root_fSolver_Brent , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
- & )
- finderConstructed=.true.
- end if
- self_ => self
- massTarget = mass
- sphericalRadiusEnclosingMass = finder%find(rootGuess=1.0d0)
+ class(massDistributionSpherical), intent(inout) :: self
+
+ sphericalRadiusHalfMass=self%radiusEnclosingMass(0.5d0*self%massTotal())
return
- end function sphericalRadiusEnclosingMass
+ end function sphericalRadiusHalfMass
- double precision function sphericalRadiusHalfMass(self,componentType,massType)
+ double precision function sphericalPotential(self,coordinates,status) result(potential)
!!{
- Computes the half-mass radius of a spherically symmetric mass distribution using numerical root finding.
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSpherical ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+
+ potential=self%potentialNumerical(coordinates,status)
+ return
+ end function sphericalPotential
+
+ double precision function sphericalPotentialNumerical(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution.
!!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Integration , only : integrator
+ use :: Numerical_Comparison , only : Values_Agree
implicit none
- class(massDistributionSpherical ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionSpherical ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision , parameter :: toleranceRelative =1.0d-3
+ double precision , parameter :: radiusMaximumFactor=1.0d+1
+ type (integrator ), save :: integrator_
+ logical , save :: initialized =.false.
+ !$omp threadprivate(integrator_,initialized)
+ double precision :: radiusMaximum , potentialPrevious, &
+ & radius
- if (.not.self%matches(componentType,massType)) then
- sphericalRadiusHalfMass=0.0d0
- return
+ if (present(status)) status=structureErrorCodeSuccess
+ if (.not.initialized) then
+ integrator_=integrator(integrandPotential,toleranceRelative=toleranceRelative)
+ initialized=.true.
end if
- sphericalRadiusHalfMass=self%radiusEnclosingMass(0.5d0*self%massTotal())
+ self_ => self
+ potential = +0.0d0
+ potentialPrevious = +1.0d0
+ radius = +coordinates%rSpherical()
+ radiusMaximum = + radius
+ do while (.not.Values_Agree(potential,potentialPrevious,relTol=toleranceRelative))
+ potentialPrevious=+potential
+ radiusMaximum =+radiusMaximum &
+ & *radiusMaximumFactor
+ potential =+integrator_%integrate( &
+ & radius , &
+ & radiusMaximum &
+ & )
+ end do
+ ! Convert to dimensionful units.
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
return
- end function sphericalRadiusHalfMass
+ end function sphericalPotentialNumerical
- double precision function sphericalMassRoot(radius)
+ double precision function sphericalPotentialDifferenceNumerical(self,coordinates1,coordinates2,status) result(potential)
!!{
- Root function used in finding half mass radii of spherically symmetric mass distributions.
+ Return the potential difference between the two specified
+ {\normalfont \ttfamily coordinates} in a spherical mass
+ distribution using a numerical calculation.
!!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Integration , only : integrator
+ use :: Numerical_Comparison , only : Values_Agree
implicit none
- double precision, intent(in ) :: radius
+ class (massDistributionSpherical ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates1 , coordinates2
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision , parameter :: toleranceRelative =1.0d-3
+ double precision , parameter :: radiusMaximumFactor=1.0d+1
+ type (integrator ), save :: integrator_
+ logical , save :: initialized =.false.
+ !$omp threadprivate(integrator_,initialized)
- sphericalMassRoot=+self_%massEnclosedBySphere(radius) &
- & - massTarget
+ if (present(status)) status=structureErrorCodeSuccess
+ if (.not.initialized) then
+ integrator_=integrator(integrandPotential,toleranceRelative=toleranceRelative)
+ initialized=.true.
+ end if
+ self_ => self
+ potential = integrator_%integrate( &
+ & coordinates1%rSpherical(), &
+ & coordinates2%rSpherical() &
+ & )
+ ! Convert to dimensionful units.
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
return
- end function sphericalMassRoot
+ end function sphericalPotentialDifferenceNumerical
- function sphericalAcceleration(self,coordinates,componentType,massType)
+ double precision function integrandPotential(radius)
+ !!{
+ Integrand for gravitational potential in a spherical mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ integrandPotential=-self_%massEnclosedBySphere(radius) &
+ & / radius **2
+ else
+ integrandPotential=0.0d0
+ end if
+ return
+ end function integrandPotential
+
+ function sphericalAcceleration(self,coordinates) result(acceleration)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for spherically-symmetric mass
distributions.
@@ -186,39 +370,37 @@ function sphericalAcceleration(self,coordinates,componentType,massType)
use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec , gravitationalConstantGalacticus
use :: Numerical_Constants_Prefixes , only : kilo
implicit none
- double precision , dimension(3) :: sphericalAcceleration
- class (massDistributionSpherical ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: coordinatesSpherical
- type (coordinateCartesian ) :: coordinatesCartesian
- double precision :: radius
- double precision , dimension(3) :: positionCartesian
+ double precision , dimension(3) :: acceleration
+ class (massDistributionSpherical ), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: coordinatesSpherical
+ type (coordinateCartesian ) :: coordinatesCartesian
+ double precision :: radius
+ double precision , dimension(3) :: positionCartesian
- if (.not.self%matches(componentType,massType)) then
- sphericalAcceleration=0.0d0
- return
- end if
! Get position in spherical and Cartesian coordinate systems.
coordinatesSpherical=coordinates
coordinatesCartesian=coordinates
! Compute the density at this position.
- positionCartesian = coordinatesCartesian
- radius =+coordinatesSpherical%r ( )
- sphericalAcceleration=-self %massEnclosedBySphere(radius ) &
- & * positionCartesian &
- & / radius **3
- if (.not.self%isDimensionless()) &
- & sphericalAcceleration=+sphericalAcceleration &
- & *kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus
+ positionCartesian=coordinatesCartesian
+ radius =coordinatesSpherical%r()
+ if (radius > 0.0d0) then
+ acceleration=-self%massEnclosedBySphere(radius ) &
+ & * positionCartesian &
+ & / radius **3
+ if (.not.self%isDimensionless()) &
+ & acceleration=+acceleration &
+ & *kilo &
+ & *gigaYear &
+ & /megaParsec &
+ & *gravitationalConstantGalacticus
+ else
+ acceleration=0.0d0
+ end if
return
end function sphericalAcceleration
- function sphericalTidalTensor(self,coordinates,componentType,massType)
+ function sphericalTidalTensor(self,coordinates)
!!{
Computes the gravitational tidal tensor at {\normalfont \ttfamily coordinates} for spherically-symmetric mass
distributions.
@@ -226,25 +408,19 @@ function sphericalTidalTensor(self,coordinates,componentType,massType)
use :: Coordinates , only : assignment(=) , coordinateSpherical, coordinateCartesian
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
- use :: Tensors , only : tensorIdentityR2D3Sym , tensorNullR2D3Sym ,assignment(=) , operator(*)
+ use :: Tensors , only : tensorIdentityR2D3Sym , assignment(=) , operator(*)
use :: Vectors , only : Vector_Outer_Product
implicit none
- type (tensorRank2Dimension3Symmetric) :: sphericalTidalTensor
- class (massDistributionSpherical ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: coordinatesSpherical
- type (coordinateCartesian ) :: coordinatesCartesian
- double precision :: radius , massEnclosed, &
- & density
- double precision , dimension(3) :: positionCartesian
- type (tensorRank2Dimension3Symmetric) :: positionTensor
-
- if (.not.self%matches(componentType,massType)) then
- sphericalTidalTensor=tensorNullR2D3Sym
- return
- end if
+ type (tensorRank2Dimension3Symmetric) :: sphericalTidalTensor
+ class (massDistributionSpherical ), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: coordinatesSpherical
+ type (coordinateCartesian ) :: coordinatesCartesian
+ double precision :: radius , massEnclosed, &
+ & density
+ double precision , dimension(3) :: positionCartesian
+ type (tensorRank2Dimension3Symmetric) :: positionTensor
+
! Get position in spherical and Cartesian coordinate systems.
coordinatesSpherical=coordinates
coordinatesCartesian=coordinates
@@ -264,25 +440,69 @@ function sphericalTidalTensor(self,coordinates,componentType,massType)
& *gravitationalConstantGalacticus
return
end function sphericalTidalTensor
-
- function sphericalPositionSample(self,randomNumberGenerator_,componentType,massType)
+
+ double precision function sphericalRotationCurve(self,radius)
+ !!{
+ Return the rotation curve for a spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ if (radius <= 0.0d0) then
+ sphericalRotationCurve=+0.0d0
+ else
+ sphericalRotationCurve=+sqrt( &
+ & +self%massEnclosedBySphere(radius) &
+ & / radius &
+ & )
+ ! Make dimensionful if necessary.
+ if (.not.self%dimensionless) sphericalRotationCurve= &
+ & +sqrt(gravitationalConstantGalacticus) &
+ & *sphericalRotationCurve
+ end if
+ return
+ end function sphericalRotationCurve
+
+ double precision function sphericalRotationCurveGradient(self,radius)
+ !!{
+ Return the rotation curve gradient for a spherical mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=) , coordinateSpherical
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical ) :: position
+
+ position =[radius,0.0d0,0.0d0]
+ sphericalRotationCurveGradient=+4.0d0 &
+ & *Pi &
+ & * radius &
+ & *self%density (position) &
+ & -self%massEnclosedBySphere(radius ) &
+ & / radius **2
+ ! Make dimensionful if necessary.
+ if (.not.self%dimensionless) sphericalRotationCurveGradient= &
+ & +gravitationalConstantGalacticus &
+ & *sphericalRotationCurveGradient
+ return
+ end function sphericalRotationCurveGradient
+
+ function sphericalPositionSample(self,randomNumberGenerator_)
!!{
Computes the half-mass radius of a spherically symmetric mass distribution using numerical root finding.
!!}
use :: Numerical_Constants_Math, only : Pi
implicit none
- double precision , dimension(3) :: sphericalPositionSample
- class (massDistributionSpherical ), intent(inout) :: self
- class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: mass , radius, &
- & theta , phi
-
- if (.not.self%matches(componentType,massType)) then
- sphericalPositionSample=0.0d0
- return
- end if
+ double precision , dimension(3) :: sphericalPositionSample
+ class (massDistributionSpherical ), intent(inout) :: self
+ class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
+ double precision :: mass , radius, &
+ & theta , phi
+
! Choose an enclosed mass and find the radius enclosing that mass. Choose angular
! coordinates at random and finally convert to Cartesian.
mass =+ self %massTotal ( ) &
@@ -298,3 +518,480 @@ function sphericalPositionSample(self,randomNumberGenerator_,componentType,massT
& ]
return
end function sphericalPositionSample
+
+ double precision function sphericalSurfaceDensity(self,coordinates)
+ !!{
+ Return the surface density at the specified {\normalfont \ttfamily coordinates} in an exponential disk mass distribution.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class(massDistributionSpherical), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ sphericalSurfaceDensity=0.0d0
+ call Error_Report('surface density is not defined for spherically-symmetric distributions'//{introspection:location})
+ return
+ end function sphericalSurfaceDensity
+
+ function sphericalChandrasekharIntegral(self,massDistributionEmbedding,massDistributionPerturber,massPerturber,coordinates,velocity)
+ !!{
+ Compute the Chandrasekhar integral at the specified {\normalfont \ttfamily coordinates} in a spherical mass distribution.
+ !!}
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
+ use :: Ideal_Gases_Thermodynamics, only : Ideal_Gas_Sound_Speed
+ use :: Error , only : Error_Report
+ implicit none
+ double precision , dimension(3) :: sphericalChandrasekharIntegral
+ class (massDistributionSpherical ), intent(inout) :: self
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding , massDistributionPerturber
+ double precision , intent(in ) :: massPerturber
+ class (coordinate ), intent(in ) :: coordinates , velocity
+ double precision , dimension(3) :: velocityCartesian_
+ double precision , parameter :: XvMaximum =10.0d0
+ type (coordinateCartesian ) :: velocityCartesian
+ double precision :: radius , velocity_ , &
+ & density , velocityDispersion , &
+ & xV
+ !$GLC attributes unused :: massDistributionPerturber, massPerturber
+
+ sphericalChandrasekharIntegral=0.0d0
+ velocity_=velocity%rSpherical()
+ if (velocity_ <= 0.0d0) return
+ radius =coordinates%rSpherical( )
+ density=self %density (coordinates)
+ if (density <= 0.0d0) return
+ if (.not.associated(self%kinematicsDistribution_)) call Error_Report('a kinematics distribution is needed to compute the Chandrasekhar integral'//{introspection:location})
+ if (self%kinematicsDistribution_%isCollisional()) then
+ velocityDispersion=Ideal_Gas_Sound_Speed(self%kinematicsDistribution_%temperature (coordinates ))
+ else
+ velocityDispersion= self%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding)
+ end if
+ if (velocityDispersion > 0.0d0) then
+ xV =+velocity_ &
+ & /velocityDispersion &
+ & /sqrt(2.0d0)
+ else
+
+ xV =+huge(0.0d0)
+ end if
+ velocityCartesian = velocity
+ velocityCartesian_ = velocityCartesian
+ sphericalChandrasekharIntegral=-density &
+ & *velocityCartesian_ &
+ & /velocity_ **3
+ if (Xv <= XvMaximum) &
+ & sphericalChandrasekharIntegral=+sphericalChandrasekharIntegral &
+ & *( &
+ & +erf ( xV ) &
+ & -2.0d0 &
+ & * xV &
+ & *exp (-xV**2) &
+ & /sqrt( Pi ) &
+ & )
+ return
+ end function sphericalChandrasekharIntegral
+
+ double precision function sphericalFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter, wavenumber
+
+ fourierTransform=self%fourierTransformNumerical(radiusOuter,wavenumber)
+ return
+ end function sphericalFourierTransform
+
+ double precision function sphericalFourierTransformNumerical(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in a spherical mass
+ distribution using a numerical calculation.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter, wavenumber
+ type (integrator ) :: integrator_
+
+ fourierTransform=0.0d0
+ integrator_ = integrator (integrandFourierTransform,toleranceRelative=1.0d-3)
+ fourierTransform=+integrator_%integrate (0.0d0 ,radiusOuter ) &
+ & /self %massEnclosedBySphere( radiusOuter )
+ return
+
+ contains
+
+ double precision function integrandFourierTransform(radius)
+ !!{
+ Integrand for Fourier transform of a spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Coordinates , only : assignment(=), coordinateSpherical
+ implicit none
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+
+ if (radius > 0.0d0) then
+ coordinates =[radius,0.0d0,0.0d0]
+ integrandFourierTransform=+4.0d0 &
+ & *Pi &
+ & * radius **2 &
+ & *sin(wavenumber*radius) &
+ & / (wavenumber*radius) &
+ & *self%density(coordinates)
+ else
+ integrandFourierTransform=0.0d0
+ end if
+ return
+ end function integrandFourierTransform
+
+ end function sphericalFourierTransformNumerical
+
+ double precision function sphericalRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radius=self%radiusFreefallNumerical(time)
+ return
+ end function sphericalRadiusFreefall
+
+ double precision function sphericalRadiusFreefallNumerical(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily
+ time} in a spherical mass distribution using a numerical
+ calculation.
+ !!}
+ use :: Root_Finder , only : rangeExpandMultiplicative , rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionSpherical), intent(inout), target :: self
+ double precision , intent(in ) :: time
+ double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-3
+ type (rootFinder ) :: finder
+ type (coordinateSpherical ) :: coordinates
+ double precision :: timeFreefallMinimum
+
+ radius=0.0d0
+ coordinates=[0.0d0,0.0d0,0.0d0]
+ if (self%densityGradientRadial(coordinates,logarithmic=.true.) == 0.0d0) then
+ ! For mass distributions with a constant density core, the potential in the center is harmonic. This means there is a
+ ! minimum to the freefall time as a function of radius. Compute that minimum here so that we can return a zero radius for
+ ! times less than this.
+ timeFreefallMinimum=+sqrt( &
+ & + 3.0d0 &
+ & /16.0d0 &
+ & *Pi &
+ & /gravitationalConstantGalacticus &
+ & /self%density(coordinates) &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ else
+ timeFreefallMinimum=+0.0d0
+ end if
+ if (time < timeFreefallMinimum) return
+ self_ => self
+ time_ = time
+ finder = rootFinder( &
+ & rootFunction =rootRadiusFreefall , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
+ & )
+ radius=finder%find(rootGuess=1.0d0)
+ return
+ end function sphericalRadiusFreefallNumerical
+
+ double precision function rootRadiusFreefall(radiusFreefall)
+ !!{
+ Root function used in finding the radius corresponding to a given freefall time.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ implicit none
+ double precision , intent(in ) :: radiusFreefall
+ type (integrator) :: integrator_
+
+ radiusFreefall_ =+radiusFreefall
+ integrator_ = integrator (integrandTimeFreefall,toleranceRelative=1.0d-3)
+ rootRadiusFreefall=+integrator_ %integrate(0.0d0 ,radiusFreefall ) &
+ & -time_
+ return
+ end function rootRadiusFreefall
+
+ double precision function integrandTimeFreefall(radius)
+ !!{
+ Integrand for freefall time in a spherical mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=) , coordinateSpherical
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
+ implicit none
+ double precision , intent(in ) :: radius
+ double precision :: potentialDifference
+ type (coordinateSpherical) :: coordinates , coordinatesFreefall
+
+ coordinates =[radius ,0.0d0,0.0d0]
+ coordinatesFreefall=[radiusFreefall_,0.0d0,0.0d0]
+ potentialDifference=+self_%potentialDifference(coordinates,coordinatesFreefall)
+ if (potentialDifference < 0.0d0) then
+ integrandTimeFreefall=+Mpc_per_km_per_s_To_Gyr &
+ & /sqrt( &
+ & -2.0d0 &
+ & *potentialDifference &
+ & )
+ else
+ ! Avoid floating point errors arising from rounding errors.
+ integrandTimeFreefall=0.0d0
+ end if
+ return
+ end function integrandTimeFreefall
+
+ double precision function sphericalRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an spherical mass
+ distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radiusIncreaseRate=self%radiusFreefallIncreaseRateNumerical(time)
+ return
+ end function sphericalRadiusFreefallIncreaseRate
+
+ double precision function sphericalRadiusFreefallIncreaseRateNumerical(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an spherical mass
+ distribution using a numerical calculation.
+ !!}
+ use :: Numerical_Differentiation, only : differentiator
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision , parameter :: timeLogarithmicStep=1.0d-2
+ type (differentiator ) :: differentiator_
+
+ differentiator_ = differentiator (radiusFreefallEvaluate )
+ radiusIncreaseRate=+differentiator_%derivative(log(time) ,timeLogarithmicStep) &
+ & / time
+ return
+ end function sphericalRadiusFreefallIncreaseRateNumerical
+
+ double precision function radiusFreefallEvaluate(timeLogarithmic)
+ !!{
+ GSL-callable function to evaluate the freefall radius of the mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ), value :: timeLogarithmic
+
+ radiusFreefallEvaluate=self_%radiusFreefall(exp(timeLogarithmic))
+ return
+ end function radiusFreefallEvaluate
+
+ double precision function sphericalEnergy(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout), target :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout), target :: massDistributionEmbedding
+
+ energy=self%energyNumerical(radiusOuter,massDistributionEmbedding)
+ return
+ end function sphericalEnergy
+
+ double precision function sphericalEnergyNumerical(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution using a numerical calculation.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ energy=+self%energyPotential(radiusOuter ) &
+ & +self%energyKinetic (radiusOuter,massDistributionEmbedding)
+ return
+ end function sphericalEnergyNumerical
+
+ double precision function sphericalEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ energy=self%energyPotentialNumerical(radiusOuter)
+ return
+ end function sphericalEnergyPotential
+
+ double precision function sphericalEnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ energy=self%energyKineticNumerical(radiusOuter,massDistributionEmbedding)
+ return
+ end function sphericalEnergyKinetic
+
+ double precision function sphericalEnergyPotentialNumerical(self,radiusOuter) result(energy)
+ !!{
+ Compute (numerically) the potential energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Integration , only : integrator
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ type (integrator ) :: integrator_
+
+ integrator_= integrator(integrandEnergyPotential,toleranceRelative=1.0d-3)
+ energy =-0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +integrator_%integrate (0.0d0,radiusOuter) &
+ & +self %massEnclosedBySphere( radiusOuter)**2 &
+ & / radiusOuter &
+ & )
+ return
+
+ contains
+
+ double precision function integrandEnergyPotential(radius)
+ !!{
+ Integrand for potential energy of a spherical mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ integrandEnergyPotential=( &
+ & +self%massEnclosedBySphere(radius) &
+ & / radius &
+ & )**2
+ else
+ integrandEnergyPotential=0.0d0
+ end if
+ return
+ end function integrandEnergyPotential
+
+ end function sphericalEnergyPotentialNumerical
+
+ double precision function sphericalEnergyKineticNumerical(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute (numerically) the kinetic energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Integration , only : integrator
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ type (integrator ) :: integrator_
+
+ integrator_= integrator(integrandEnergyKinetic,toleranceRelative=1.0d-3)
+ energy =+6.0d0 &
+ & *Pi &
+ & *integrator_%integrate(0.0d0,radiusOuter)
+ return
+
+ contains
+
+ double precision function integrandEnergyKinetic(radius)
+ !!{
+ Integrand for kinetic energy of the halo.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+
+ if (radius > 0.0d0) then
+ coordinates =[radius,0.0d0,0.0d0]
+ integrandEnergyKinetic=+self %density (coordinates ) &
+ & *self%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding)**2 &
+ & * radius **2
+ else
+ integrandEnergyKinetic=0.0d0
+ end if
+ return
+ end function integrandEnergyKinetic
+
+ end function sphericalEnergyKineticNumerical
+
+ double precision function sphericalDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Returns a radial density moment for a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ densityRadialMoment=self%densityRadialMomentNumerical(moment,radiusMinimum,radiusMaximum,isInfinite)
+ return
+ end function sphericalDensityRadialMoment
+
+ double precision function sphericalDensityRadialMomentNumerical(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Returns a radial density moment for a spherical mass distribution using a numerical calculation.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Integration, only : integrator
+ implicit none
+ class (massDistributionSpherical), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ type (integrator ) :: integrator_
+ double precision :: radiusMinimum_
+
+ densityRadialMoment=0.0d0
+ if (.not.present(radiusMaximum)) call Error_Report('a maximum radius must be provided'//{introspection:location})
+ if (present(isInfinite)) isInfinite=.false.
+ radiusMinimum_=0.0d0
+ if (present(radiusMinimum)) radiusMinimum_=radiusMinimum
+ integrator_ = integrator (integrandMoment,toleranceRelative=1.0d-3)
+ densityRadialMoment=+integrator_%integrate(radiusMinimum_ ,radiusMaximum )
+ return
+
+ contains
+
+ double precision function integrandMoment(radius)
+ !!{
+ Integrand for radial density moment in a spherical mass distribution.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateSpherical
+ implicit none
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+
+ if (radius > 0.0d0) then
+ coordinates = [radius,0.0d0,0.0d0]
+ integrandMoment=+self%density(coordinates) &
+ & * radius **moment
+ else
+ integrandMoment=+0.0d0
+ end if
+ return
+ end function integrandMoment
+
+ end function sphericalDensityRadialMomentNumerical
diff --git a/source/mass_distributions.spherical.Hernquist.F90 b/source/mass_distributions.spherical.Hernquist.F90
index d01620674b..2735280366 100644
--- a/source/mass_distributions.spherical.Hernquist.F90
+++ b/source/mass_distributions.spherical.Hernquist.F90
@@ -34,11 +34,14 @@
double precision :: densityNormalization, mass, &
& scaleLength
contains
- procedure :: density => hernquistDensity
- procedure :: densityRadialMoment => hernquistDensityRadialMoment
- procedure :: massEnclosedBySphere => hernquistMassEnclosedBySphere
- procedure :: potential => hernquistPotential
- procedure :: radiusHalfMass => hernquistRadiusHalfMass
+ procedure :: massTotal => hernquistMassTotal
+ procedure :: density => hernquistDensity
+ procedure :: densityGradientRadial => hernquistDensityGradientRadial
+ procedure :: densityRadialMoment => hernquistDensityRadialMoment
+ procedure :: massEnclosedBySphere => hernquistMassEnclosedBySphere
+ procedure :: potentialIsAnalytic => hernquistPotentialIsAnalytic
+ procedure :: potential => hernquistPotential
+ procedure :: radiusHalfMass => hernquistRadiusHalfMass
end type massDistributionHernquist
interface massDistributionHernquist
@@ -171,35 +174,65 @@ function hernquistConstructorInternal(densityNormalization,mass,scaleLength,dime
return
end function hernquistConstructorInternal
- double precision function hernquistDensity(self,coordinates,componentType,massType)
+ double precision function hernquistMassTotal(self)
+ !!{
+ Return the total mass in an Hernquist mass distribution.
+ !!}
+ implicit none
+ class(massDistributionHernquist), intent(inout) :: self
+
+ hernquistMassTotal=self%mass
+ return
+ end function hernquistMassTotal
+
+ double precision function hernquistDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a Hernquist mass distribution.
!!}
- use :: Coordinates, only : assignment(=), coordinateSpherical
implicit none
- class (massDistributionHernquist ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision :: r
+ class (massDistributionHernquist), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- hernquistDensity=0.0d0
- return
- end if
- ! Get position in spherical coordinate system.
- position = coordinates
! Compute the density at this position.
- r =+position%r () &
- & /self %scaleLength
- hernquistDensity=+self %densityNormalization &
- & / r &
+ r =+coordinates%rSpherical () &
+ & /self %scaleLength
+ hernquistDensity=+self %densityNormalization &
+ & / r &
& /(+1.0d0+r)**3
return
end function hernquistDensity
- double precision function hernquistDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function hernquistDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density gradient in the radial direction in a scaled spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionHernquist), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: r
+ !![
+
+ !!]
+
+ ! Compute the density at this position.
+ r =+coordinates%rSpherical () &
+ & /self %scaleLength
+ if (logarithmic_) then
+ hernquistDensityGradientRadial=-(+1.0d0+4.0d0*r) &
+ & /(+1.0d0+ r)
+ else
+ hernquistDensityGradientRadial=-self %densityNormalization &
+ & /self %scaleLength &
+ & *(+1.0d0+4.0d0*r) &
+ & / r **2 &
+ & /(+1.0d0+ r)**4
+ end if
+ return
+ end function hernquistDensityGradientRadial
+
+ double precision function hernquistDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Returns a radial density moment for the Hernquist mass distribution.
!!}
@@ -207,17 +240,11 @@ double precision function hernquistDensityRadialMoment(self,moment,radiusMinimum
use :: Numerical_Comparison , only : Values_Agree
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionHernquist ), intent(inout) :: self
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
- logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionHernquist), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
- if (.not.self%matches(componentType,massType)) then
- hernquistDensityRadialMoment=0.0d0
- return
- end if
! Abort on limited ranges.
if (present(radiusMinimum).or.present(radiusMaximum)) call Error_Report('ranges are not supported'//{introspection:location})
if (moment <= 0.0d0 .or. moment >= 3.0d0) then
@@ -248,23 +275,17 @@ double precision function hernquistDensityRadialMoment(self,moment,radiusMinimum
return
end function hernquistDensityRadialMoment
- double precision function hernquistMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function hernquistMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for Hernquist mass distributions.
!!}
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionHernquist ), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , parameter :: fractionalRadiusLarge=1.0d6
- double precision :: fractionalRadius
+ class (massDistributionHernquist), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision , parameter :: fractionalRadiusLarge=1.0d6
+ double precision :: fractionalRadius
- if (.not.self%matches(componentType,massType)) then
- hernquistMassEnclosedBySphere=0.0d0
- return
- end if
fractionalRadius=radius/self%scaleLength
if (fractionalRadius > fractionalRadiusLarge) then
! For very large radius approximate the mass enclosed as the total mass.
@@ -275,23 +296,31 @@ double precision function hernquistMassEnclosedBySphere(self,radius,componentTyp
return
end function hernquistMassEnclosedBySphere
- double precision function hernquistPotential(self,coordinates,componentType,massType)
+ logical function hernquistPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionHernquist), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function hernquistPotentialIsAnalytic
+
+ double precision function hernquistPotential(self,coordinates,status)
!!{
Return the potential at the specified {\normalfont \ttfamily coordinates} in a Hernquist mass distribution.
!!}
use :: Coordinates , only : assignment(=) , coordinateSpherical
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class(massDistributionHernquist ), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
+ class(massDistributionHernquist ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateSpherical ) :: position
- if (.not.self%matches(componentType,massType)) then
- hernquistPotential=0.0d0
- return
- end if
+ if (present(status)) status=structureErrorCodeSuccess
! Get position in spherical coordinate system.
position=coordinates
! Compute the potential at this position.
@@ -301,20 +330,14 @@ double precision function hernquistPotential(self,coordinates,componentType,mass
return
end function hernquistPotential
- double precision function hernquistRadiusHalfMass(self,componentType,massType)
+ double precision function hernquistRadiusHalfMass(self)
!!{
Return the half-mass radius of a Hernquist mass distribution.
!!}
implicit none
- class (massDistributionHernquist ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , parameter :: radiusHalfMassToScaleRadius=1.0d0/(sqrt(2.0d0)-1.0d0)
+ class (massDistributionHernquist), intent(inout) :: self
+ double precision , parameter :: radiusHalfMassToScaleRadius=1.0d0/(sqrt(2.0d0)-1.0d0)
- if (.not.self%matches(componentType,massType)) then
- hernquistRadiusHalfMass=0.0d0
- return
- end if
hernquistRadiusHalfMass=+radiusHalfMassToScaleRadius &
& *self%scaleLength
return
diff --git a/source/mass_distributions.spherical.NFW.F90 b/source/mass_distributions.spherical.NFW.F90
index bf7300e069..2e647caa27 100644
--- a/source/mass_distributions.spherical.NFW.F90
+++ b/source/mass_distributions.spherical.NFW.F90
@@ -21,9 +21,19 @@
Implementation of an NFW \citep{navarro_structure_1996} mass distribution class.
!!}
+ use :: Numerical_Interpolation, only : interpolator
+
+ public :: massDistributionNFWStateStore, massDistributionNFWStateRestore
+
!![
- An NFW \citep{navarro_structure_1996} mass distribution class.
+
+ An NFW \citep{navarro_structure_1996} mass distribution class. The density profile is given by:
+ \begin{equation}
+ \rho_\mathrm{dark matter}(r) \propto \left({r\over r_\mathrm{s}}\right)^{-1} \left[1 + \left({r\over r_\mathrm{s}}\right)
+ \right]^{-2}.
+ \end{equation}
+
!!]
type, public, extends(massDistributionSpherical) :: massDistributionNFW
@@ -31,23 +41,66 @@
The NFW \citep{navarro_structure_1996} mass distribution.
!!}
private
- double precision :: densityNormalization, scaleLength
+ double precision :: densityNormalization , scaleLength
+ double precision :: enclosedMassRadiusPrevious, enclosedMassPrevious
contains
- procedure :: density => nfwDensity
- procedure :: descriptor => nfwDescriptor
+ !![
+
+
+
+ !!]
+ procedure :: massTotal => nfwMassTotal
+ procedure :: density => nfwDensity
+ procedure :: densityGradientRadial => nfwDensityGradientRadial
+ procedure :: densityRadialMoment => nfwDensityRadialMoment
+ procedure :: massEnclosedBySphere => nfwMassEnclosedBySphere
+ procedure :: velocityRotationCurveMaximum => nfwVelocityRotationCurveMaximum
+ procedure :: radiusRotationCurveMaximum => nfwRadiusRotationCurveMaximum
+ procedure :: radiusEnclosingMass => nfwRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => nfwRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => nfwRadiusFromSpecificAngularMomentum
+ procedure :: fourierTransform => nfwFourierTransform
+ procedure :: radiusFreefall => nfwRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => nfwRadiusFreefallIncreaseRate
+ procedure :: timeFreefallTabulate => nfwTimeFreefallTabulate
+ procedure :: potentialIsAnalytic => nfwPotentialIsAnalytic
+ procedure :: potential => nfwPotential
+ procedure :: energyPotential => nfwEnergyPotential
+ procedure :: energyKinetic => nfwEnergyKinetic
+ procedure :: descriptor => nfwDescriptor
end type massDistributionNFW
-
+
interface massDistributionNFW
!!{
Constructors for the {\normalfont \ttfamily nfw} mass distribution class.
!!}
- module procedure nfwConstructorParameters
- module procedure nfwConstructorInternal
+ module procedure massDistributionNFWConstructorParameters
+ module procedure massDistributionNFWConstructorInternal
end interface massDistributionNFW
+ ! Tabulated solutions.
+ double precision :: densityScaleFreeRadiusMinimum =+ 2.0d0 , densityScaleFreeRadiusMaximum =+ 0.5d0
+ double precision :: densityScaleFreeMinimum =+huge(0.0d0), densityScaleFreeMaximum =-huge(0.0d0)
+ type (interpolator), allocatable :: densityScaleFree_
+ double precision :: angularMomentumSpecificScaleFreeRadiusMinimum=+ 2.0d0 , angularMomentumSpecificScaleFreeRadiusMaximum=+ 0.5d0
+ double precision :: angularMomentumSpecificScaleFreeMinimum =+huge(0.0d0), angularMomentumSpecificScaleFreeMaximum =-huge(0.0d0)
+ type (interpolator), allocatable :: angularMomentumSpecificScaleFree_
+ double precision :: timeFreefallScaleFreeRadiusMinimum =+ 2.0d0 , timeFreefallScaleFreeRadiusMaximum =+ 0.5d0
+ double precision :: timeFreefallScaleFreeMinimum =+huge(0.0d0), timeFreefallScaleFreeMaximum =-huge(0.0d0)
+ type (interpolator), allocatable :: timeFreefallScaleFree_
+ !$omp threadprivate(densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum )
+ !$omp threadprivate(densityScaleFreeMinimum , densityScaleFreeMaximum )
+ !$omp threadprivate(densityScaleFree_ )
+ !$omp threadprivate(angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum)
+ !$omp threadprivate(angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum )
+ !$omp threadprivate(angularMomentumSpecificScaleFree_ )
+ !$omp threadprivate(timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum )
+ !$omp threadprivate(timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum )
+ !$omp threadprivate(timeFreefallScaleFree_ )
+
contains
- function nfwConstructorParameters(parameters) result(self)
+ function massDistributionNFWConstructorParameters(parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily nfw} mass distribution class which builds the object from a parameter
set.
@@ -58,7 +111,7 @@ function nfwConstructorParameters(parameters) result(self)
implicit none
type (massDistributionNFW) :: self
type (inputParameters ), intent(inout) :: parameters
- double precision :: mass , scaleLength, &
+ double precision :: mass , scaleLength , &
& densityNormalization, concentration, &
& virialRadius
logical :: dimensionless
@@ -68,7 +121,7 @@ function nfwConstructorParameters(parameters) result(self)
!![
densityNormalization
- 0.5d0/Pi
+ 1.0d0/2.0d0/Pi/(log(4.0d0)-1.0d0)
The density normalization of the NFW profile.
@@ -126,9 +179,9 @@ function nfwConstructorParameters(parameters) result(self)
!!]
return
- end function nfwConstructorParameters
+ end function massDistributionNFWConstructorParameters
- function nfwConstructorInternal(scaleLength,concentration,densityNormalization,mass,virialRadius,dimensionless,componentType,massType) result(self)
+ function massDistributionNFWConstructorInternal(scaleLength,concentration,densityNormalization,mass,virialRadius,dimensionless,componentType,massType) result(self)
!!{
Internal constructor for ``nfw'' mass distribution class.
!!}
@@ -142,7 +195,7 @@ function nfwConstructorInternal(scaleLength,concentration,densityNormalization,m
logical , intent(in ), optional :: dimensionless
type (enumerationComponentTypeType), intent(in ), optional :: componentType
type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r
+ double precision :: radiusScaleFree
!![
!!]
@@ -169,8 +222,8 @@ function nfwConstructorInternal(scaleLength,concentration,densityNormalization,m
& present(mass ).and. &
& present(virialRadius ) &
& ) then
- r=virialRadius/self%scaleLength
- self%densityNormalization=mass/4.0d0/Pi/self%scaleLength**3/(log(1.0d0+r)-r/(1.0d0+r))
+ radiusScaleFree =+virialRadius/self%scaleLength
+ self%densityNormalization=+mass/4.0d0/Pi/self%scaleLength**3/(log(1.0d0+radiusScaleFree)-radiusScaleFree/(1.0d0+radiusScaleFree))
else
call Error_Report('either "densityNormalization", or "mass" and "virialRadius" must be specified'//{introspection:location})
end if
@@ -180,37 +233,788 @@ function nfwConstructorInternal(scaleLength,concentration,densityNormalization,m
else
self%dimensionless=.false.
end if
+ ! Initialize memoized results.
+ self%enclosedMassPrevious =-huge(0.0d0)
+ self%enclosedMassRadiusPrevious=-huge(0.0d0)
return
- end function nfwConstructorInternal
+ end function massDistributionNFWConstructorInternal
- double precision function nfwDensity(self,coordinates,componentType,massType)
+ double precision function nfwMassTotal(self)
+ !!{
+ Return the total mass in an NFW mass distribution.
+ !!}
+ implicit none
+ class(massDistributionNFW), intent(inout) :: self
+
+ nfwMassTotal=huge(0.0d0)
+ return
+ end function nfwMassTotal
+
+ double precision function nfwDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in an NFW mass distribution.
!!}
- use :: Coordinates, only : assignment(=), coordinate, coordinateSpherical
implicit none
- class (massDistributionNFW ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (coordinateSpherical ) :: position
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: r
+ class (massDistributionNFW), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radiusScaleFree
- if (.not.self%matches(componentType,massType)) then
- nfwDensity=0.0d0
- return
- end if
- ! Get position in spherical coordinate system.
- position = coordinates
! Compute the density at this position.
- r =+position%r () &
- & /self %scaleLength
- nfwDensity=+self %densityNormalization &
- & / r &
- & /(1.0d0+r)**2
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ nfwDensity =+self %densityNormalization &
+ & / radiusScaleFree &
+ & /(1.0d0+radiusScaleFree)**2
return
end function nfwDensity
+
+ double precision function nfwDensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an NFW \citep{navarro_structure_1996} mass distribution.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionNFW), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ densityGradientRadial=0.0d0
+ radiusScaleFree =+coordinates%rSpherical() &
+ & /self %scaleLength
+ if (radiusScaleFree <= 0.0d0) then
+ if (logarithmic_) then
+ densityGradientRadial=-1.0d0
+ else
+ call Error_Report('gradient is divergent at r=0'//{introspection:location})
+ end if
+ else
+ densityGradientRadial=-self %densityNormalization &
+ & /self %scaleLength &
+ & / radiusScaleFree **2 &
+ & *(1.0d0+3.0d0*radiusScaleFree) &
+ & /(1.0d0+ radiusScaleFree)**3
+ if (logarithmic_) densityGradientRadial=+ densityGradientRadial &
+ & /self %density (coordinates) &
+ & *coordinates%rSpherical ( )
+ end if
+ return
+ end function nfwDensityGradientRadial
+
+ double precision function nfwDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Computes radial moments of the density in an NFW \citep{navarro_structure_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: radialMomentMinimum, radialMomentMaximum
+
+ densityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ if (present(radiusMinimum)) then
+ radialMomentMinimum=radialMomentScaleFree(radiusMinimum/self%scaleLength)
+ else
+ radialMomentMinimum=radialMomentScaleFree( 0.0d0)
+ end if
+ if (present(radiusMaximum)) then
+ radialMomentMaximum=radialMomentScaleFree(radiusMaximum/self%scaleLength)
+ else
+ radialMomentMaximum=0.0d0
+ if (moment >= 3.0d0) then
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ return
+ else
+ call Error_Report('moment is infinite'//{introspection:location})
+ end if
+ end if
+ end if
+ densityRadialMoment=+self%densityNormalization &
+ & *self%scaleLength **(moment+1.0d0) &
+ & *( &
+ & +radialMomentMaximum &
+ & -radialMomentMinimum &
+ & )
+ return
+
+ contains
+
+ double precision function radialMomentScaleFree(radius)
+ !!{
+ Provides the scale-free part of the radial moment of the NFW density profile.
+ !!}
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ use :: Numerical_Comparison , only : Values_Agree
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (Values_Agree(moment,0.0d0,absTol=1.0d-6)) then
+ ! Take the real part of this improper integral. The imaginary parts must cancel when taking differences to compute a
+ ! proper integral.
+ radialMomentScaleFree=+1.0d0/ (1.0d0+ radius ) &
+ & -2.0d0*real(atanh(dcmplx(1.0d0+2.0d0*radius,0.0d0)))
+ else if (Values_Agree(moment,1.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=-1.0d0/ (1.0d0 +radius )
+ else if (Values_Agree(moment,2.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=+1.0d0/ (1.0d0 +radius ) &
+ & + log (1.0d0 +radius )
+ else if (Values_Agree(moment,3.0d0,absTol=1.0d-6)) then
+ radialMomentScaleFree=+ radius &
+ & -1.0d0/ (1.0d0 +radius ) &
+ & -2.0d0*log (1.0d0 +radius )
+ else
+ radialMomentScaleFree=+(1.0d0+radius)**(moment-1.0d0) &
+ & /moment &
+ & / (moment-1.0d0) &
+ & *( &
+ & - moment &
+ & * Hypergeometric_2F1([1.0d0-moment,-moment],[2.0d0-moment],1.0d0/(1.0d0+radius)) &
+ & +(1.0d0+radius) &
+ & *(moment-1.0d0) &
+ & *( &
+ & +(radius/(1.0d0+radius))**moment &
+ & -Hypergeometric_2F1([ -moment,-moment],[1.0d0-moment],1.0d0/(1.0d0+radius)) &
+ & ) &
+ & )
+ end if
+ return
+ end function radialMomentScaleFree
+
+ end function nfwDensityRadialMoment
+
+ double precision function nfwMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for nfw mass distributions.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionNFW ), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radiusScaleFree
+
+ if (radius /= self%enclosedMassRadiusPrevious) then
+ self%enclosedMassRadiusPrevious=+ radius
+ radiusScaleFree =+ radius &
+ & /self%scaleLength
+ self%enclosedMassPrevious =+ massEnclosedScaleFree(radiusScaleFree) &
+ & *self%densityNormalization &
+ & *self%scaleLength **3
+ end if
+ mass=self%enclosedMassPrevious
+ return
+ end function nfwMassEnclosedBySphere
+
+ double precision function nfwRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for nfw mass distributions.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Lambert_Ws , only : Lambert_W0
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionNFW ), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision , parameter :: massScaleFreeSmall=3.0d-4
+ double precision :: mass_ , massScaleFree
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_=mass
+ else if (present(massFractional)) then
+ call Error_Report('mass is unbounded, so mass fraction is undefined'//{introspection:location})
+ else
+ call Error_Report('either mass or massFractional must be supplied' //{introspection:location})
+ end if
+ massScaleFree=+ mass_ &
+ & / 4.0d0 &
+ & / Pi &
+ & /self%densityNormalization &
+ & /self%scaleLength**3
+ if (massScaleFree <= 0.0d0 ) then
+ radius=+0.0d0
+ else if (massScaleFree < massScaleFreeSmall) then
+ ! Use a series solution for very small radii.
+ radius=+ sqrt(2.0d0)*massScaleFree**0.5d0 &
+ & + 4.0d0/ 3.0d0 *massScaleFree &
+ & + 13.0d0/ 9.0d0/sqrt(2.0d0)*massScaleFree**1.5d0 &
+ & + 92.0d0/ 135.0d0 *massScaleFree**2 &
+ & + 313.0d0/ 540.0d0/sqrt(2.0d0)*massScaleFree**2.5d0 &
+ & + 1928.0d0/ 8505.0d0 *massScaleFree**3 &
+ & +56201.0d0/340200.0d0/sqrt(2.0d0)*massScaleFree**3.5d0 &
+ & + 358.0d0/ 1701.0d0 *massScaleFree**4
+ else
+ radius=-1.0d0 &
+ & /Lambert_W0( &
+ & -exp( &
+ & -1.0d0 &
+ & -massScaleFree &
+ & ) &
+ & ) &
+ & -1.0d0
+ end if
+ radius=+radius &
+ & *self%scaleLength
+ return
+ end function nfwRadiusEnclosingMass
+ double precision function nfwRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for nfw mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+ double precision , allocatable , dimension(:) :: radii , densities
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: densityScaleFree
+ integer :: countRadii
+
+ densityScaleFree=+density &
+ & /self%densityNormalization
+ if ( &
+ & densityScaleFree < densityScaleFreeMinimum &
+ & .or. &
+ & densityScaleFree > densityScaleFreeMaximum &
+ & ) then
+ do while (densityEnclosedScaleFree(densityScaleFreeRadiusMinimum) < densityScaleFree)
+ densityScaleFreeRadiusMinimum=0.5d0*densityScaleFreeRadiusMinimum
+ end do
+ do while (densityEnclosedScaleFree(densityScaleFreeRadiusMaximum) > densityScaleFree)
+ densityScaleFreeRadiusMaximum=2.0d0*densityScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(densityScaleFreeRadiusMaximum/densityScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(densityScaleFree_)) deallocate(densityScaleFree_)
+ allocate(radii (countRadii))
+ allocate(densities (countRadii))
+ allocate(densityScaleFree_ )
+ radii = Make_Range(densityScaleFreeRadiusMinimum,densityScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ densities =-densityEnclosedScaleFree( radii)
+ densityScaleFreeMinimum=-densities (countRadii )
+ densityScaleFreeMaximum=-densities ( 1 )
+ densityScaleFree_ = interpolator (densities ,radii)
+ end if
+ radius=+densityScaleFree_%interpolate(-densityScaleFree) &
+ & *self %scaleLength
+ return
+ end function nfwRadiusEnclosingDensity
+
+ elemental double precision function massEnclosedScaleFree(radius) result(mass)
+ !!{
+ Evaluate the mass enclosed by a given radius in a scale-free NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: minimumRadiusForExactSolution =1.0d-6
+ double precision, parameter :: nfwNormalizationFactorUnitRadius=log(2.0d0)-0.5d0 ! Precomputed NFW normalization factor for unit radius.
+
+ if (radius == 1.0d0 ) then
+ mass=nfwNormalizationFactorUnitRadius
+ else if (radius >= minimumRadiusForExactSolution) then
+ mass=log(1.0d0+radius)-radius/(1.0d0+radius)
+ else
+ mass=radius**2*(0.5d0+radius*(-2.0d0/3.0d0+radius*(0.75d0+radius*(-0.8d0))))
+ end if
+ mass =+4.0d0 &
+ & *Pi &
+ & *mass
+ return
+ end function massEnclosedScaleFree
+
+ elemental double precision function densityEnclosedScaleFree(radius) result(density)
+ !!{
+ Evaluate the mean enclosed density at a given radius in a scale-free NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+
+ density=+3.0d0 &
+ & /4.0d0 &
+ & /Pi &
+ & *massEnclosedScaleFree(radius) &
+ & / radius **3
+ return
+ end function densityEnclosedScaleFree
+
+ double precision function nfwRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for nfw mass distributions.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ implicit none
+ class (massDistributionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+ double precision , allocatable , dimension(:) :: radii , angularMomentaSpecific
+ double precision , parameter :: countRadiiPerDecade =100.0d0
+ double precision :: angularMomentumSpecificScaleFree
+ integer :: countRadii
+
+ if (angularMomentumSpecific > 0.0d0) then
+ angularMomentumSpecificScaleFree=+angularMomentumSpecific &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & / self%scaleLength **2
+ if ( &
+ & angularMomentumSpecificScaleFree < angularMomentumSpecificScaleFreeMinimum &
+ & .or. &
+ & angularMomentumSpecificScaleFree > angularMomentumSpecificScaleFreeMaximum &
+ & ) then
+ do while (angularMomentumSpecificEnclosedScaleFree(angularMomentumSpecificScaleFreeRadiusMinimum) > angularMomentumSpecificScaleFree)
+ angularMomentumSpecificScaleFreeRadiusMinimum=0.5d0*angularMomentumSpecificScaleFreeRadiusMinimum
+ end do
+ do while (angularMomentumSpecificEnclosedScaleFree(angularMomentumSpecificScaleFreeRadiusMaximum) < angularMomentumSpecificScaleFree)
+ angularMomentumSpecificScaleFreeRadiusMaximum=2.0d0*angularMomentumSpecificScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(angularMomentumSpecificScaleFreeRadiusMaximum/angularMomentumSpecificScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(angularMomentumSpecificScaleFree_)) deallocate(angularMomentumSpecificScaleFree_)
+ allocate(radii (countRadii))
+ allocate(angularMomentaSpecific (countRadii))
+ allocate(angularMomentumSpecificScaleFree_ )
+ radii =Make_Range(angularMomentumSpecificScaleFreeRadiusMinimum,angularMomentumSpecificScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ angularMomentaSpecific =angularMomentumSpecificEnclosedScaleFree( radii)
+ angularMomentumSpecificScaleFreeMinimum=angularMomentaSpecific ( 1)
+ angularMomentumSpecificScaleFreeMaximum=angularMomentaSpecific (countRadii)
+ angularMomentumSpecificScaleFree_ =interpolator (angularMomentaSpecific,radii)
+ end if
+ radius=+angularMomentumSpecificScaleFree_%interpolate(angularMomentumSpecificScaleFree) &
+ & *self %scaleLength
+ else
+ radius=+0.0d0
+ end if
+ return
+ end function nfwRadiusFromSpecificAngularMomentum
+
+ elemental double precision function angularMomentumSpecificEnclosedScaleFree(radius) result(angularMomentumSpecific)
+ !!{
+ Evaluate the specific angular momentum at a given radius in a scale-free NFW mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ angularMomentumSpecific=+sqrt( &
+ & +massEnclosedScaleFree(radius) &
+ & * radius &
+ & )
+ return
+ end function angularMomentumSpecificEnclosedScaleFree
+
+ double precision function nfwVelocityRotationCurveMaximum(self) result(velocity)
+ !!{
+ Return the peak velocity in the rotation curve for an nfw mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , parameter :: circularVelocityMaximumScaleFree=0.4649909628174221d0 ! The circular velocity (in scale-free units) at the peak of the NFW rotation curve.
+ ! Numerical value found using Mathematica.
+
+ velocity=+circularVelocityMaximumScaleFree &
+ & *sqrt( &
+ & +4.0d0 &
+ & *Pi &
+ & *self%densityNormalization &
+ & ) &
+ & * self%scaleLength
+ if (.not.self%isDimensionless()) &
+ & velocity=+velocity &
+ & *sqrt(gravitationalConstantGalacticus)
+ return
+ end function nfwVelocityRotationCurveMaximum
+
+ double precision function nfwRadiusRotationCurveMaximum(self) result(radius)
+ !!{
+ Return the peak velocity in the rotation curve for an nfw mass distribution.
+ !!}
+ implicit none
+ class (massDistributionNFW ), intent(inout), target :: self
+ ! The radius (in scale-free units) at the peak of the NFW rotation curve. Numerical value found using Mathematica.
+ double precision , parameter :: radiusCircularVelocityMaximumScaleFree=2.162581587064612d0
+
+ radius=+radiusCircularVelocityMaximumScaleFree &
+ & *self%scaleLength
+ return
+ end function nfwRadiusRotationCurveMaximum
+
+ logical function nfwPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionNFW), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function nfwPotentialIsAnalytic
+
+ double precision function nfwPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an nfw mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionNFW ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision :: radiusScaleFree
+
+ if (present(status)) status=structureErrorCodeSuccess
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ potential=+potentialScaleFree (radiusScaleFree) &
+ & *self%densityNormalization &
+ & *self%scaleLength **2
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
+ return
+ end function nfwPotential
+
+ elemental double precision function potentialScaleFree(radius) result(potential)
+ !!{
+ Compute the potential in a scale-free NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: radiusSmall=1.0d-6
+ double precision :: radiusTerm
+
+ if (radius < radiusSmall) then
+ ! Use a series solution for very small radii.
+ radiusTerm=+1.0d0-radius/2.0d0+radius**2/3.0d0
+ else
+ ! Use the full expression for larger radii.
+ radiusTerm=log(1.0d0+radius)/radius
+ end if
+ potential=-4.0d0 &
+ & *Pi &
+ & *radiusTerm
+ return
+ end function potentialScaleFree
+
+ double precision function potentialDifferenceScaleFree(radius1,radius2) result(potential)
+ !!{
+ Compute the potential difference in a scale-free NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Comparison , only : Values_Agree
+ implicit none
+ double precision, intent(in ) :: radius1 , radius2
+ double precision, parameter :: radiusSmall =1.0d-6
+ double precision, parameter :: toleranceRelative =1.0d-3
+ double precision :: potentialGradientLogarithmic , radiusDifferenceLogarithmic
+
+ if (Values_Agree(radius1,radius2,relTol=toleranceRelative)) then
+ if (radius1 < radiusSmall) then
+ potentialGradientLogarithmic=- radius1 / 2.0d0 &
+ & +5.0d0*radius1**2/12.0d0
+ else
+ potentialGradientLogarithmic=- 1.0d0 &
+ & + radius1 &
+ & / (1.0d0+radius1) &
+ & /log(1.0d0+radius1)
+ end if
+ radiusDifferenceLogarithmic=+1.0d0 &
+ & -radius2 &
+ & /radius1
+ potential =+potentialScaleFree (radius1) &
+ & *potentialGradientLogarithmic &
+ & *radiusDifferenceLogarithmic
+ else
+ potential=+potentialScaleFree(radius1) &
+ & -potentialScaleFree(radius2)
+ end if
+ return
+ end function potentialDifferenceScaleFree
+
+ double precision function nfwFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in an NFW mass
+ distribution, using the expression given in \citeauthor{cooray_halo_2002}~(\citeyear{cooray_halo_2002}; eqn.~81).
+ !!}
+ use :: Exponential_Integrals, only : Cosine_Integral, Sine_Integral
+ implicit none
+ class (massDistributionNFW ), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter , wavenumber
+ double precision :: wavenumberScaleFree, radiusOuterScaleFree
+
+ waveNumberScaleFree =+waveNumber *self%scaleLength
+ radiusOuterScaleFree=+radiusOuter/self%scaleLength
+ fourierTransform =+( &
+ & +sin(+ waveNumberScaleFree)*(Sine_Integral ((1.0d0+radiusOuterScaleFree)*waveNumberScaleFree)-Sine_Integral (waveNumberScaleFree)) &
+ & -sin(+radiusOuterScaleFree*waveNumberScaleFree)/ (1.0d0+radiusOuterScaleFree)/waveNumberScaleFree &
+ & +cos(+ waveNumberScaleFree)*(Cosine_Integral((1.0d0+radiusOuterScaleFree)*waveNumberScaleFree)-Cosine_Integral(waveNumberScaleFree)) &
+ & ) &
+ & /(log(1.0d0+radiusOuterScaleFree)-radiusOuterScaleFree/(1.0d0+radiusOuterScaleFree))
+ return
+ end function nfwFourierTransform
+
+ double precision function nfwRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in an NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ if (time > 0.0d0) then
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ call self%timeFreefallTabulate(timeScaleFree)
+ radius=+timeFreefallScaleFree_%interpolate(timeScaleFree) &
+ & *self %scaleLength
+ else
+ ! For non-positive freefall times, return a zero freefall radius.
+ radius=+0.0d0
+ end if
+ return
+ end function nfwRadiusFreefall
+
+ double precision function nfwRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an nfw mass
+ distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ if (time > 0.0d0) then
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ call self%timeFreefallTabulate(timeScaleFree)
+ radiusIncreaseRate=+timeFreefallScaleFree_%derivative(timeScaleFree) &
+ & *self %scaleLength &
+ & / timeScale
+ else
+ ! For non-positive freefall times, return the limiting value for small radii.
+ radiusIncreaseRate=+0.0d0
+ end if
+ return
+ end function nfwRadiusFreefallIncreaseRate
+
+ subroutine nfwTimeFreefallTabulate(self,timeScaleFree)
+ !!{
+ Tabulate the freefall radius at the given {\normalfont \ttfamily time} in an NFW mass distribution.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , intent(in ) :: timeScaleFree
+ double precision , allocatable , dimension(:) :: radii , timesFreefall
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: radiusStart
+ integer :: countRadii , i
+ type (integrator ) :: integrator_
+
+ if ( &
+ & timeScaleFree < timeFreefallScaleFreeMinimum &
+ & .or. &
+ & timeScaleFree > timeFreefallScaleFreeMaximum &
+ & ) then
+ integrator_=integrator(timeFreeFallIntegrand,toleranceRelative=1.0d-3)
+ do while (timeFreefallScaleFree(timeFreefallScaleFreeRadiusMinimum) > timeScaleFree)
+ timeFreefallScaleFreeRadiusMinimum=0.5d0*timeFreefallScaleFreeRadiusMinimum
+ end do
+ do while (timeFreefallScaleFree(timeFreefallScaleFreeRadiusMaximum) < timeScaleFree)
+ timeFreefallScaleFreeRadiusMaximum=2.0d0*timeFreefallScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(timeFreefallScaleFreeRadiusMaximum/timeFreefallScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(timeFreefallScaleFree_)) deallocate(timeFreefallScaleFree_)
+ allocate(radii (countRadii))
+ allocate(timesFreefall (countRadii))
+ allocate(timeFreefallScaleFree_ )
+ radii=Make_Range(timeFreefallScaleFreeRadiusMinimum,timeFreefallScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ timesFreefall(i)=timeFreefallScaleFree(radii(i))
+ end do
+ timeFreefallScaleFreeMinimum=timesFreefall( 1 )
+ timeFreefallScaleFreeMaximum=timesFreefall( countRadii )
+ timeFreefallScaleFree_ =interpolator (timesFreefall,radii)
+ end if
+ return
+
+ contains
+
+ double precision function timeFreefallScaleFree(radius)
+ !!{
+ Evaluate the freefall time from a given radius in a scale-free NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: radiusSmall=4.0d-6
+
+ if (radius > radiusSmall) then
+ radiusStart = radius
+ timeFreefallScaleFree=integrator_%integrate(0.0d0,radius)
+ else
+ ! Use an approximation here, found by taking series expansions of the logarithms in the integrand and keeping only the
+ ! first order terms.
+ timeFreefallScaleFree=2.0d0*sqrt(radius/4.0d0/Pi)
+ end if
+ return
+ end function timeFreefallScaleFree
+
+ double precision function timeFreeFallIntegrand(radius)
+ !!{
+ Integrand used to find the freefall time in a scale-free NFW mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision :: potentialDifference
+
+ if (radius == 0.0d0) then
+ timeFreeFallIntegrand=+0.0d0
+ else
+ potentialDifference=+potentialDifferenceScaleFree(radiusStart,radius)
+ if (potentialDifference > 0.0d0) then
+ timeFreeFallIntegrand=+1.0d0 &
+ & /sqrt( &
+ & +2.0d0 &
+ & *potentialDifference &
+ & )
+ else
+ timeFreeFallIntegrand=+0.0d0
+ end if
+ end if
+ return
+ end function timeFreeFallIntegrand
+
+ end subroutine nfwTimeFreefallTabulate
+
+ double precision function nfwEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in an NFW mass distribution. This is
+ \begin{eqnarray}
+ W &=& - \frac{\mathrm{G}}{2} \rho_0^2 r_\mathrm{s}^5 \int_0^{x_\mathrm{out}} \frac{m^2(x)}{x^2} \mathrm{d} x, \nonumber \\
+ &-& - \frac{\mathrm{G}}{2} \rho_0^2 r_\mathrm{s}^5 \left[ \frac{x}{1+x} - \frac{\log^2(1+x)}{x} + \frac{\left\{\log(1+x)-x/(1+x)\right\}^2}{x} \right],
+ \end{eqnarray}
+ where $x=r/r_\mathrm{s}$ and $m(x)$ is the scale-free mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionNFW), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ double precision :: radiusOuterScaleFree
+
+ radiusOuterScaleFree=+ radiusOuter &
+ & /self%scaleLength
+ energy=-gravitationalConstantGalacticus &
+ & *self%scaleLength **5 &
+ & *self%densityNormalization **2 &
+ & *8.0d0 &
+ & *Pi**2 &
+ & *( &
+ & +radiusOuterScaleFree/(1.0d0+radiusOuterScaleFree) &
+ & - log(1.0d0+radiusOuterScaleFree)**2 /radiusOuterScaleFree &
+ & +(log(1.0d0+radiusOuterScaleFree) -radiusOuterScaleFree/(1.0d0+radiusOuterScaleFree))**2/radiusOuterScaleFree &
+ & )
+ return
+ end function nfwEnergyPotential
+
+ double precision function nfwEnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius} in an NFW mass distribution. This is
+ \begin{eqnarray}
+ T &=& 6 \pi \mathrm{G} \rho_0^2 r_\mathrm{s}^5 \int_0^{x_\mathrm{out}} \rho(x) \sigma^2(x) x^2 \mathrm{d} x, \nonumber \\
+ &=& \pi \mathrm{G} \rho_0^2 r_\mathrm{s}^5 \left[ 6 x^3 \text{Li}_2(-x)+x^3 (-\log (x))+\log (x+1) \left(3 x^3 \log (x+1)+((x-6) x+3) x-2\right)+\left(x \left(\pi ^2 x-7\right)+5\right) x+\frac{3}{x+1} \right],
+ \end{eqnarray}
+ where $x=r/r_\mathrm{s}$, $\rho(x)$ is the scale-free density, and $\sigma^2(x)$ is the scale-free velocity dispersion.
+ !!}
+ use :: Dilogarithms , only : Dilogarithm
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionNFW ), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass), intent(inout) :: massDistributionEmbedding
+ logical :: analytic
+ double precision :: radiusOuterScaleFree
+
+ analytic=.false.
+ select type (massDistributionEmbedding)
+ class is (massDistributionNFW)
+ select type (kinematicsDistribution_ => massDistributionEmbedding%kinematicsDistribution_)
+ class is (kinematicsDistributionNFW)
+ analytic =.true.
+ radiusOuterScaleFree=+ radiusOuter &
+ & /self%scaleLength
+ energy =+gravitationalConstantGalacticus &
+ & *self%scaleLength **5 &
+ & *self%densityNormalization **2 &
+ & *4.0d0 &
+ & *Pi**2 &
+ & *( &
+ & +( &
+ & +2.0d0 &
+ & + radiusOuterScaleFree &
+ & *( &
+ & -2.0d0 &
+ & + radiusOuterScaleFree &
+ & *( &
+ & -7.0d0 &
+ & +Pi**2 &
+ & *(1.0d0+radiusOuterScaleFree) &
+ & ) &
+ & ) &
+ & ) &
+ & * radiusOuterScaleFree &
+ & + radiusOuterScaleFree**4 *log (+1.0d0+1.0d0/radiusOuterScaleFree) &
+ & - radiusOuterScaleFree**3 *log (+ radiusOuterScaleFree) &
+ & +( &
+ & - 2.0d0 &
+ & + radiusOuterScaleFree &
+ & - 3.0d0*radiusOuterScaleFree**2 &
+ & - 5.0d0*radiusOuterScaleFree**3 &
+ & + 3.0d0*radiusOuterScaleFree**3 &
+ & * (1.0d0+radiusOuterScaleFree ) &
+ & *log(1.0d0+radiusOuterScaleFree ) &
+ & ) &
+ & * log (1.0d0+radiusOuterScaleFree) &
+ & +6.0d0 &
+ & * radiusOuterScaleFree**3 &
+ & * (1.0d0+radiusOuterScaleFree ) &
+ & * Dilogarithm( -radiusOuterScaleFree) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree)
+ end select
+ end select
+ if (.not.analytic) energy=self%energyKineticNumerical(radiusOuter,massDistributionEmbedding)
+ return
+ end function nfwEnergyKinetic
+
subroutine nfwDescriptor(self,descriptor,includeClass,includeFileModificationTimes)
!!{
Return an input parameter list descriptor which could be used to recreate this object.
@@ -233,3 +1037,55 @@ subroutine nfwDescriptor(self,descriptor,includeClass,includeFileModificationTim
return
end subroutine nfwDescriptor
+ !![
+
+ massDistributionNFWStateStore
+
+ !!]
+ subroutine massDistributionNFWStateStore(stateFile,gslStateFile,stateOperationID)
+ !!{
+ Write the tabulation state to file.
+ !!}
+ use :: Display , only : displayMessage, verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
+ implicit none
+ integer , intent(in ) :: stateFile
+ integer(c_size_t), intent(in ) :: stateOperationID
+ type (c_ptr ), intent(in ) :: gslStateFile
+
+ call displayMessage('Storing state for: massDistributionNFW',verbosity=verbosityLevelInfo)
+ write (stateFile) densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum , &
+ & densityScaleFreeMinimum , densityScaleFreeMaximum , &
+ & angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum, &
+ & angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum , &
+ & timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum , &
+ & timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum
+ return
+ end subroutine massDistributionNFWStateStore
+
+ !![
+
+ massDistributionNFWStateRestore
+
+ !!]
+ subroutine massDistributionNFWStateRestore(stateFile,gslStateFile,stateOperationID)
+ !!{
+ Retrieve the tabulation state from the file.
+ !!}
+ use :: Display , only : displayMessage, verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
+ implicit none
+ integer , intent(in ) :: stateFile
+ integer(c_size_t), intent(in ) :: stateOperationID
+ type (c_ptr ), intent(in ) :: gslStateFile
+
+ call displayMessage('Retrieving state for: massDistributionNFW',verbosity=verbosityLevelInfo)
+ read (stateFile) densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum , &
+ & densityScaleFreeMinimum , densityScaleFreeMaximum , &
+ & angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum, &
+ & angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum , &
+ & timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum , &
+ & timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum
+ return
+ end subroutine massDistributionNFWStateRestore
+
diff --git a/source/mass_distributions.spherical.PatejLoeb2015.F90 b/source/mass_distributions.spherical.PatejLoeb2015.F90
new file mode 100644
index 0000000000..0fb4a47294
--- /dev/null
+++ b/source/mass_distributions.spherical.PatejLoeb2015.F90
@@ -0,0 +1,446 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of mass distribution for the \cite{patej_simple_2015} model of the circumgalactic medium.
+ !!}
+
+ !![
+
+ A mass distribution for the \cite{patej_simple_2015} model of the circumgalactic medium.
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionPatejLoeb2015
+ !!{
+ The \cite{patej_simple_2015} model of the circumgalactic medium.
+ !!}
+ class (massDistributionClass), pointer :: massDistribution_ => null()
+ logical :: truncateAtOuterRadius
+ double precision :: radiusShock , radiusOuter, &
+ & densityNormalization , gamma , &
+ & mass
+ contains
+ !![
+
+
+
+
+ !!]
+ final :: patejLoeb2015Destructor
+ procedure :: density => patejLoeb2015Density
+ procedure :: densityGradientRadial => patejLoeb2015DensityGradientRadial
+ procedure :: densityRadialMoment => patejLoeb2015DensityRadialMoment
+ procedure :: massEnclosedBySphere => patejLoeb2015MassEnclosedBySphere
+ procedure :: potentialIsAnalytic => patejLoeb2015PotentialIsAnalytic
+ procedure :: potential => patejLoeb2015Potential
+ procedure :: radiusDarkMatter => patejLoeb2015RadiusDarkMatter
+ procedure :: coordinatesDarkMatter => patejLoeb2015CoordinatesDarkMatter
+ end type massDistributionPatejLoeb2015
+
+ interface massDistributionPatejLoeb2015
+ !!{
+ Constructors for the {\normalfont \ttfamily patejLoeb2015} mass distribution class.
+ !!}
+ module procedure patejLoeb2015ConstructorParameters
+ module procedure patejLoeb2015ConstructorInternal
+ end interface massDistributionPatejLoeb2015
+
+contains
+
+ function patejLoeb2015ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily patejLoeb2015} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionPatejLoeb2015) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: radiusShock , radiusOuter, &
+ & mass , gamma , &
+ & densityNormalization
+ class (massDistributionClass ), pointer :: massDistribution_
+ logical :: truncateAtOuterRadius
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ gamma
+ 1.15d0
+ The parameter $\Gamma$ in the \cite{patej_simple_2015} mass distribution.
+
+
+
+ densityNormalization
+ 0.0d0
+ The density normalization of the \cite{patej_simple_2015} mass distribution.
+
+
+
+ mass
+ 0.0d0
+ The mass of the \cite{patej_simple_2015} mass distribution.
+
+
+
+ radiusOuter
+ 0.0d0
+ The outer radius of the \cite{patej_simple_2015} mass distribution.
+
+
+
+ truncateAtOuterRadius
+ .false.
+ If true then the \cite{patej_simple_2015} mass distribution is truncated beyond the outer radius.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ self=massDistributionPatejLoeb2015(radiusShock=radiusShock,gamma=gamma,massDistribution_=massDistribution_,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+
+
+
+ !!]
+ return
+ end function patejLoeb2015ConstructorParameters
+
+ function patejLoeb2015ConstructorInternal(gamma,massDistribution_,densityNormalization,mass,radiusOuter,radiusShock,truncateAtOuterRadius,componentType,massType) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily patejLoeb2015} mass distribution class.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ type (massDistributionPatejLoeb2015) :: self
+ double precision , intent(in ) :: gamma , radiusShock
+ double precision , intent(in ), optional :: densityNormalization , mass , &
+ & radiusOuter
+ logical , intent(in ), optional :: truncateAtOuterRadius
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionClass ), intent(in ), target :: massDistribution_
+ !![
+
+ !!]
+
+ ! This mass distribution is never dimensionless.
+ self%dimensionless=.false.
+ ! Determine density normalization.
+ if ( &
+ & present(densityNormalization) &
+ & ) then
+ self%densityNormalization=densityNormalization
+ else if ( &
+ & present(mass ) &
+ & ) then
+ self%densityNormalization=mass/self%massDistribution_%massEnclosedBySphere(self%radiusDarkMatter(self%radiusShock))
+ else
+ call Error_Report('no way to determine density normalization'//{introspection:location})
+ end if
+ ! Check for truncation.
+ if (present(truncateAtOuterRadius)) then
+ self%truncateAtOuterRadius=truncateAtOuterRadius
+ else
+ self%truncateAtOuterRadius=.false.
+ end if
+ if (self%truncateAtOuterRadius) then
+ if (.not.present(radiusOuter)) call Error_Report('can not truncate profile without an outer radius'//{introspection:location})
+ self%radiusOuter=radiusOuter
+ end if
+ return
+ end function patejLoeb2015ConstructorInternal
+
+ subroutine patejLoeb2015Destructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily patejLoeb2015} mass distribution class.
+ !!}
+ type(massDistributionPatejLoeb2015), intent(inout) :: self
+ implicit none
+
+ !![
+
+ !!]
+ return
+ end subroutine patejLoeb2015Destructor
+
+ double precision function patejLoeb2015RadiusDarkMatter(self,radius) result(radiusDarkMatter)
+ !!{
+ Return the corresponding radius in the dark matter distribution the specified {\normalfont \ttfamily radius} in a \cite{patej_simple_2015} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionPatejLoeb2015), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+
+ radiusDarkMatter=+ self%radiusShock &
+ & *( &
+ & + radius &
+ & /self%radiusShock &
+ & )**self%gamma
+ return
+ end function patejLoeb2015RadiusDarkMatter
+
+ function patejLoeb2015CoordinatesDarkMatter(self,coordinates) result(coordinatesDarkMatter)
+ !!{
+ Return the corresponding coordinates in the dark matter distribution the specified {\normalfont \ttfamily radius} in a \cite{patej_simple_2015} mass distribution.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ type (coordinateSpherical ) :: coordinatesDarkMatter
+ class(massDistributionPatejLoeb2015), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ coordinatesDarkMatter=coordinates &
+ & *(coordinates%rSpherical()/self%radiusShock)**(self%gamma-1.0d0)
+ return
+ end function patejLoeb2015CoordinatesDarkMatter
+
+ double precision function patejLoeb2015Density(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a \cite{patej_simple_2015} mass distribution.
+ !!}
+ implicit none
+ class(massDistributionPatejLoeb2015), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ density=0.0d0
+ if (self%truncateAtOuterRadius .and. coordinates%rSpherical() > self%radiusOuter) return
+ ! Evaluate density using equation 12 of Patej & Loeb (2015).
+ density=+self%gamma &
+ & *self%densityNormalization &
+ & *( &
+ & +coordinates%rSpherical () &
+ & /self %radiusShock &
+ & )**(3.0d0*self%gamma-3.0d0) &
+ & *self%massDistribution_%density(self%coordinatesDarkMatter(coordinates))
+ return
+ end function patejLoeb2015Density
+
+ double precision function patejLoeb2015DensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a \cite{patej_simple_2015} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionPatejLoeb2015), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ densityGradientRadial=0.0d0
+ if (self%truncateAtOuterRadius .and. coordinates%rSpherical() > self%radiusOuter) return
+ densityGradientRadial=+3.0d0 &
+ & *(self%gamma-1.0d0) &
+ & + self%gamma &
+ & * self%massDistribution_%densityGradientRadial( &
+ & self%coordinatesDarkMatter(coordinates), &
+ & logarithmic=.true. &
+ & )
+ if (.not.logarithmic_) densityGradientRadial=densityGradientRadial*self%density(coordinates)/coordinates%rSpherical()
+ return
+ end function patejLoeb2015DensityGradientRadial
+
+ double precision function patejLoeb2015MassEnclosedBySphere(self,radius) result(massEnclosedBySphere)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a \cite{patej_simple_2015} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionPatejLoeb2015), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radius_
+
+ ! Compute the enclosed mass (eqn. 4 of Patej & Loeb 2015).
+ radius_=radius
+ if (self%truncateAtOuterRadius) radius_=min(radius_,self%radiusOuter)
+ massEnclosedBySphere=+self %densityNormalization &
+ & *self%massDistribution_%massEnclosedBySphere(self%radiusDarkMatter(radius_))
+ return
+ end function patejLoeb2015MassEnclosedBySphere
+
+ logical function patejLoeb2015PotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionPatejLoeb2015), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function patejLoeb2015PotentialIsAnalytic
+
+ double precision function patejLoeb2015Potential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in a \cite{patej_simple_2015} mass distribution. The
+ potential is given by
+ \begin{equation}
+ \phi(r) = - \int^\infty_r \mathrm{d}r \frac{\mathrm{G}M(r)}{r^2}.
+ \end{equation}
+ Given that $M(r) = f M^\prime(r^\prime)$ where $M^\prime(r^\prime)$ is the mass profile of the dark matter distribution,
+ $r^\prime = r_\mathrm{s} (r/r_\mathrm{s})^\Gamma$ (and, therefore, $r = r_\mathrm{s} (r^\prime/r_\mathrm{s})^{1/\Gamma}$), and
+ $f$ is the normalization factor, we can write this as:
+ \begin{eqnarray}
+ \phi(r) &=& - \int^\infty_{r^\prime(r)} \mathrm{d}r^\prime f \frac{\mathrm{d}r}{\mathrm{d}r^\prime} (r^\prime/r_\mathrm{s})^{-2/\Gamma} \frac{\mathrm{G}M^\prime(r^\prime)}{r_\mathrm{s}^2} \nonumber \\
+ &=& - \int^\infty_{r^\prime(r)} \mathrm{d}r^\prime \frac{f}{\Gamma} (r^\prime/r_\mathrm{s})^{-1/\Gamma-1} \frac{\mathrm{G}M^\prime(r^\prime)}{r_\mathrm{s}^2}
+ \end{eqnarray}
+ which we can then integrate by parts to get:
+ \begin{equation}
+ \phi(r) = + f \left(\frac{r^\prime}{r_\mathrm{s}}\right)^{-1/\Gamma} \frac{\mathrm{G}M^\prime(r^\prime)}{r_\mathrm{s}} - 4 \pi f \frac{\mathrm{G} r_\mathrm{s}^{1/\Gamma} \mathcal{R}(r^\prime,\infty,2-1/\Gamma)}{r_\mathrm{s}},
+ \end{equation}
+ where $\mathcal{R}(a,b,m)$ is the $m^\mathrm{th}$ radial density moment between $r^\prime=a$ and $r^\prime=b$ of the dark
+ matter distribution, and we have assumed that $(r^\prime/r_\mathrm{s})^{-1/\Gamma} M^\prime(r^\prime) \rightarrow 0$ as
+ $r^\prime \rightarrow \infty$.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
+ implicit none
+ class (massDistributionPatejLoeb2015 ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision :: radiusOuter , radiusDarkMatterInner, &
+ & radiusDarkMatterOuter, potentialTermInner , &
+ & potentialTermOuter , potentialTermMass
+ logical :: computePotential
+
+ potential=0.0d0
+ if (present(status)) status=structureErrorCodeSuccess
+ ! Note that we should check that the condition -1/Γ+3+α < 0, where α is the logarithmic slope of the dark matter density
+ ! profile as r → ∞, holds to ensure that our assumption that the r=∞ term in the first term in the integration by parts is
+ ! zero is a valid assumption. Currently we do not perform this check.
+ if (self%truncateAtOuterRadius) then
+ ! Add the potential due to a point mass outside of the outer radius.
+ radiusOuter =max( &
+ & coordinates%rSpherical (), &
+ & self %radiusOuter &
+ & )
+ potential =- gravitationalConstantGalacticus &
+ & *self%massEnclosedBySphere (radiusOuter) &
+ & / radiusOuter
+ computePotential=radiusOuter < self%radiusOuter
+ else
+ radiusOuter =huge(0.0d0)
+ computePotential=.true.
+ end if
+ if (.not.computePotential) return
+ ! Add on the contribution from radii inside the mass distribution.
+ radiusDarkMatterInner=self%radiusDarkMatter(coordinates%rSpherical())
+ if (self%truncateAtOuterRadius) then
+ radiusDarkMatterOuter=+self%radiusDarkMatter(radiusOuter)
+ potentialTermMass =-4.0d0 &
+ & *Pi &
+ & *self %radiusShock**(1.0d0/self%gamma) &
+ & *self%massDistribution_%densityRadialMoment(2.0d0-1.0d0/self%gamma,radiusDarkMatterInner,radiusDarkMatterOuter)
+ potentialTermOuter =+( &
+ & + radiusDarkMatterOuter &
+ & /self%radiusShock &
+ & )**(-1.0d0/self%gamma) &
+ & *self%massDistribution_%massEnclosedBySphere(radiusDarkMatterOuter)
+ else
+ ! There is no outer truncation, so the outer radius is at infinity.
+ potentialTermMass =-4.0d0 &
+ & *Pi &
+ & *self %radiusShock**(1.0d0/self%gamma) &
+ & *self%massDistribution_%densityRadialMoment(2.0d0-1.0d0/self%gamma,radiusDarkMatterInner )
+ potentialTermOuter =+0.0d0
+ end if
+ potentialTermInner =+( &
+ & + radiusDarkMatterInner &
+ & /self%radiusShock &
+ & )**(-1.0d0/self%gamma) &
+ & *self%massDistribution_%massEnclosedBySphere(radiusDarkMatterInner)
+ potential =+ potential &
+ & + gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & /self%radiusShock &
+ & *( &
+ & +potentialTermOuter &
+ & -potentialTermInner &
+ & +potentialTermMass &
+ & )
+ return
+ end function patejLoeb2015Potential
+
+ double precision function patejLoeb2015DensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Computes radial moments of the density in a \cite{patej_simple_2015} mass distribution. For this profile we have:
+ \begin{equation}
+ \rho_\mathrm{g}(r) = f \Gamma (r/s)^{3 \Gamma - 3} \rho_\mathrm{DM}(s[r/s]^\Gamma).
+ \end{equation}
+ Defining $R=s[r/s]^\Gamma$, such that $r/s = (R/s)^{1/\Gamma}$, and $\mathrm{d}r = \Gamma^{-1} (R/s)^{1/\Gamma-1} \mathrm{d}R$, then
+ \begin{equation}
+ \int r^m \rho_\mathrm{g}(r) \mathrm{d}r = f s^{(m-2)(\Gamma-1)/\Gamma} \int R^{(2\Gamma-2+m)/\Gamma} \rho_\mathrm{DM}(R) \mathrm{d}R,
+ \end{equation}
+ or
+ \begin{equation}
+ \mathcal{R}_\mathrm{g}(r;m) = f s^{(m-2)(\Gamma-1)/\Gamma} \mathcal{R}_\mathrm{DM}(R;(2\Gamma-2+m)/\Gamma),
+ \end{equation}
+ where $\mathcal{R}(r;m)$ is the $m^\mathrm{th}$ radial moment of the density profile.
+ !!}
+ implicit none
+ class (massDistributionPatejLoeb2015), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: radiusMinimum_ , radiusMaximum_, &
+ & momentEffective
+
+ if (present(radiusMinimum)) then
+ radiusMinimum_=radiusMinimum
+ else
+ radiusMinimum_=0.0d0
+ end if
+ if (present(radiusMaximum)) then
+ radiusMaximum_=radiusMaximum
+ else
+ radiusMaximum_=huge(0.0d0)
+ end if
+ if (self%truncateAtOuterRadius) then
+ radiusMinimum_=min(radiusMinimum_,self%radiusOuter)
+ radiusMaximum_=min(radiusMaximum_,self%radiusOuter)
+ end if
+ momentEffective=(2.0d0*self%gamma-2.0d0+moment)/self%gamma
+ if (radiusMaximum_ < huge(0.0d0)) then
+ densityRadialMoment=+self %densityNormalization &
+ & *self %radiusShock **(moment-momentEffective) &
+ & *self%massDistribution_%densityRadialMoment (momentEffective,self%radiusDarkMatter(radiusMinimum_),self%radiusDarkMatter(radiusMaximum_))
+ else
+ densityRadialMoment=+self %densityNormalization &
+ & *self %radiusShock **(moment-momentEffective) &
+ & *self%massDistribution_%densityRadialMoment (momentEffective,self%radiusDarkMatter(radiusMinimum_))
+ end if
+ return
+ end function patejLoeb2015DensityRadialMoment
diff --git a/source/mass_distributions.spherical.SIDM.F90 b/source/mass_distributions.spherical.SIDM.F90
new file mode 100644
index 0000000000..8b16885aae
--- /dev/null
+++ b/source/mass_distributions.spherical.SIDM.F90
@@ -0,0 +1,128 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements an abstract spherical mass distribution class for SIDM models.
+ !!}
+
+ use :: Dark_Matter_Particles, only : darkMatterParticleClass
+
+ !![
+
+
+ An abstract mass distribution class for spherical SIDM models. Provides a method to compute interaction radii.
+
+
+ !!]
+ type, abstract, extends(massDistributionSphericalDecorator) :: massDistributionSphericalSIDM
+ !!{
+ Implementation of a spherical mass distribution for SIDM models.
+ !!}
+ private
+ class (darkMatterParticleClass), pointer :: darkMatterParticle_ => null()
+ double precision :: timeAge
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: radiusInteraction => sidmRadiusInteraction
+ end type massDistributionSphericalSIDM
+
+ ! Submodule-scope variables used in root finding.
+ class (massDistributionSphericalSIDM), pointer :: self_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ double precision :: crossSection_
+ !$omp threadprivate(self_,kinematicsDistribution_,crossSection_)
+
+contains
+
+ double precision function sidmRadiusInteraction(self) result(radiusInteraction)
+ !!{
+ Returns the characteristic interaction radius (in Mpc) of the self-interacting dark matter profile of {\normalfont \ttfamily node}.
+ !!}
+ use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Prefixes , only : centi , kilo
+ use :: Numerical_Constants_Astronomical, only : megaParsec , massSolar
+ use :: Root_Finder , only : rootFinder , rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
+ use :: ISO_Varying_String , only : char
+ implicit none
+ class (massDistributionSphericalSIDM), intent(inout), target :: self
+ type (rootFinder ), save :: finder
+ logical , save :: finderInitialized=.false.
+ double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-3
+ !$omp threadprivate(finder,finderInitialized)
+
+ self_ => self
+ select type (darkMatterParticle_ => self%darkMatterParticle_)
+ class is (darkMatterParticleSelfInteractingDarkMatter)
+ crossSection_=+darkMatterParticle_%crossSectionSelfInteraction() &
+ & *centi **2 &
+ & /megaParsec**2 &
+ & *kilo &
+ & *massSolar
+ class default
+ call Error_Report('expected self-interacting dark matter particle but found type "'//char(darkMatterParticle_%objectType())//'"'//{introspection:location})
+ end select
+ if (.not.finderInitialized) then
+ finder=rootFinder( &
+ & rootFunction =sidmRadiusInteractionRoot, &
+ & toleranceAbsolute=toleranceAbsolute , &
+ & toleranceRelative=toleranceRelative &
+ & )
+ call finder%rangeExpand( &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ finderInitialized=.true.
+ end if
+ kinematicsDistribution_ => self %massDistribution_%kinematicsDistribution( )
+ radiusInteraction = finder %find (rootGuess=1.0d0)
+ !![
+
+ !!]
+ return
+ end function sidmRadiusInteraction
+
+ double precision function sidmRadiusInteractionRoot(radius) result(residual)
+ !!{
+ Root function used in seeking the characteristic interaction radius in self-interacting dark matter profiles.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ implicit none
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+
+ coordinates= [radius,0.0d0,0.0d0]
+ residual =+4.0d0 &
+ & /sqrt(Pi) &
+ & /Mpc_per_km_per_s_To_Gyr &
+ & *self_%massDistribution_ %density (coordinates ) &
+ & * kinematicsDistribution_%velocityDispersion1D(coordinates,self_%massDistribution_) &
+ & *crossSection_ &
+ & -1.0d0 &
+ & /self_%timeAge
+ return
+ end function sidmRadiusInteractionRoot
diff --git a/source/mass_distributions.spherical.SIDM.coreNFW.F90 b/source/mass_distributions.spherical.SIDM.coreNFW.F90
new file mode 100644
index 0000000000..9ebd68b9d4
--- /dev/null
+++ b/source/mass_distributions.spherical.SIDM.coreNFW.F90
@@ -0,0 +1,327 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An implementation of a cored-NFW mass distribution to approximate the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+
+ !![
+
+
+ A mass distribution class implementing a cored-NFW dark matter halo profile to approximate the effects of SIDM based
+ on the model of Jiang et al. (2022). The profile is defined by the enclosed mass, with \citep{jiang_semi-analytic_2023}:
+ \begin{equation}
+ M(r) = M_\mathrm{NFW}(r) \mathrm{tanh}\left(\frac{r}{r_\mathrm{c}}\right),
+ \end{equation}
+ where $r_\mathrm{c} = \alpha r_1$ is a characteristic core size related to the interaction radius $r_1$ by a constant factor
+ $\alpha = ${\normalfont \ttfamily [factorRadiusCore]}.
+
+
+ !!]
+ type, extends(massDistributionSphericalSIDM) :: massDistributionSphericalSIDMCoreNFW
+ !!{
+ Implementation of a cored-NFW mass distribution to approximate the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+ private
+ double precision :: factorRadiusCore
+ contains
+ !![
+
+
+
+ !!]
+ final :: sphericalSIDMCoreNFWDestructor
+ procedure :: radiusCore => sphericalSIDMCoreNFWRadiusCore
+ procedure :: density => sphericalSIDMCoreNFWDensity
+ procedure :: densityGradientRadial => sphericalSIDMCoreNFWDensityGradientRadial
+ procedure :: massEnclosedBySphere => sphericalSIDMCoreNFWMassEnclosedBySphere
+ end type massDistributionSphericalSIDMCoreNFW
+
+ interface massDistributionSphericalSIDMCoreNFW
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalSIDMCoreNFW} mass distribution class.
+ !!}
+ module procedure sphericalSIDMCoreNFWConstructorParameters
+ module procedure sphericalSIDMCoreNFWConstructorInternal
+ end interface massDistributionSphericalSIDMCoreNFW
+
+contains
+
+ function sphericalSIDMCoreNFWConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sidmCoreNFW} mass distribution class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalSIDMCoreNFW) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (darkMatterParticleClass ), pointer :: darkMatterParticle_
+ double precision :: factorRadiusCore , timeAge
+ type (varying_string ) :: componentType , massType, &
+ & nonAnalyticSolver
+
+ !![
+
+ timeAge
+
+ The age of the halo (in Gyr).
+
+
+ factorRadiusCore
+ 0.45d0
+ \cite{jiang_semi-analytic_2023}
+
+ The factor $\alpha$ appearing in the definition of the core radius, $r_\mathrm{c}=\alpha r_1$ where $r_1$ is the radius at which an SIDM particle has had, on average, 1 interaction.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionNFW)
+ self=massDistributionSphericalSIDMCoreNFW(factorRadiusCore,timeAge,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,darkMatterParticle_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('an NFW mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalSIDMCoreNFWConstructorParameters
+
+ function sphericalSIDMCoreNFWConstructorInternal(factorRadiusCore,timeAge,nonAnalyticSolver,massDistribution_,darkMatterParticle_,componentType,massType) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily sidmCoreNFW} mass distribution class.
+ !!}
+ use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
+ implicit none
+ type (massDistributionSphericalSIDMCoreNFW) :: self
+ class (massDistributionNFW ), intent(in ), target :: massDistribution_
+ class (darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ double precision , intent(in ) :: factorRadiusCore , timeAge
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Validate the dark matter particle type.
+ select type (darkMatterParticle__ => self%darkMatterParticle_)
+ class is (darkMatterParticleSelfInteractingDarkMatter)
+ ! This is as expected.
+ class default
+ call Error_Report('this class expects a self-interacting dark matter particle'//{introspection:location})
+ end select
+ ! Initialize state.
+ self%dimensionless=.false.
+ return
+ end function sphericalSIDMCoreNFWConstructorInternal
+
+ subroutine sphericalSIDMCoreNFWDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalSIDMCoreNFW} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalSIDMCoreNFW), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalSIDMCoreNFWDestructor
+
+ double precision function sphericalSIDMCoreNFWRadiusCore(self) result(radiusCore)
+ !!{
+ Returns the core radius (in Mpc) of the ``coreNFW'' approximation to the self-interacting dark matter profile.
+ !!}
+ implicit none
+ class(massDistributionSphericalSIDMCoreNFW), intent(inout) :: self
+
+ radiusCore=+self%factorRadiusCore &
+ & *self%radiusInteraction()
+ return
+ end function sphericalSIDMCoreNFWRadiusCore
+
+ double precision function sphericalSIDMCoreNFWDensity(self,coordinates) result(density)
+ !!{
+ Compute the density at the specified {\normalfont \ttfamily coordinates} for the {\normalfont \ttfamily sidmCoreNFW}
+ mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMCoreNFW), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision , parameter :: radiusFractionalLarge=10.0d0
+ double precision :: radiusFractional , radiusCore
+
+ radiusCore =+self %radiusCore()
+ radiusFractional=+coordinates%rSpherical() &
+ & / radiusCore
+ if (radiusFractional < radiusFractionalLarge) then
+ ! Use the full solution for sufficiently small radii.
+ density=+self%massDistribution_%density (coordinates ) &
+ & *tanh( &
+ & +radiusFractional &
+ & ) &
+ & +self%massDistribution_%massEnclosedBySphere(coordinates%rSpherical()) &
+ & /4.0d0 &
+ & /Pi &
+ & / radiusFractional**2 &
+ & / radiusCore **3 &
+ & /cosh( &
+ & +radiusFractional &
+ & )**2
+ else
+ ! For large fractional radii avoid floating point overflow by approximating cosh(x) ~ 1/2/exp(-x).
+ density=+self%massDistribution_%density (coordinates ) &
+ & *tanh( &
+ & +radiusFractional &
+ & ) &
+ & +self%massDistribution_%massEnclosedBySphere(coordinates%rSpherical()) &
+ & /4.0d0 &
+ & /Pi &
+ & / radiusFractional**2 &
+ & / radiusCore **3 &
+ & *4.0d0 &
+ & *exp( &
+ & -2.0d0 &
+ & * radiusFractional &
+ & )
+ end if
+ return
+ end function sphericalSIDMCoreNFWDensity
+
+ double precision function sphericalSIDMCoreNFWDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a truncated spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMCoreNFW), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusCore , massEnclosedNFW , &
+ & densityNFW , densityLogSlopeNFW, &
+ & radius
+ !![
+
+ !!]
+
+ radius =+coordinates %rSpherical ( )
+ radiusCore =+self %radiusCore ( )
+ massEnclosedNFW =+self%massDistribution_%massEnclosedBySphere (coordinates%rSpherical() )
+ densityNFW =+self%massDistribution_%density (coordinates )
+ densityLogSlopeNFW=+self%massDistribution_%densityGradientRadial(coordinates ,logarithmic=.true.)
+ densityGradient =+( &
+ & -2.0d0 &
+ & *massEnclosedNFW &
+ & *( &
+ & +radiusCore &
+ & +radius &
+ & *tanh( &
+ & +radius &
+ & /radiusCore &
+ & ) &
+ & ) &
+ & +radiusCore*radius &
+ & *( &
+ & +4.0d0 &
+ & *Pi &
+ & *radius**2 &
+ & *densityNFW &
+ & +2.0d0 &
+ & *Pi &
+ & *radius**2 &
+ & *( &
+ & +2.0d0 &
+ & *densityNFW &
+ & +( &
+ & +radiusCore &
+ & *sinh( &
+ & +2.0d0 &
+ & *radius &
+ & /radiusCore &
+ & ) &
+ & *densityLogSlopeNFW &
+ & *densityNFW &
+ & ) &
+ & /radius &
+ & ) &
+ & ) &
+ & ) &
+ & /( &
+ & +radiusCore &
+ & *( &
+ & +massEnclosedNFW &
+ & +2.0d0 &
+ & *Pi &
+ & *radiusCore &
+ & *radius **2 &
+ & *sinh( &
+ & +2.0d0 &
+ & *radius &
+ & /radiusCore &
+ & ) &
+ & *densityNFW &
+ & ) &
+ & )
+ if (.not.logarithmic_) &
+ & densityGradient=+ densityGradient &
+ & *self%density (coordinates) &
+ & / radius
+ return
+ end function sphericalSIDMCoreNFWDensityGradientRadial
+
+ double precision function sphericalSIDMCoreNFWMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for the {\normalfont \ttfamily sidmCoreNFW}
+ mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMCoreNFW), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ mass =+self%massDistribution_%massEnclosedBySphere(radius) &
+ & *tanh( &
+ & + radius &
+ & /self%radiusCore() &
+ & )
+ return
+ end function sphericalSIDMCoreNFWMassEnclosedBySphere
diff --git a/source/mass_distributions.spherical.SIDM.isothermal.F90 b/source/mass_distributions.spherical.SIDM.isothermal.F90
new file mode 100644
index 0000000000..12a19e6860
--- /dev/null
+++ b/source/mass_distributions.spherical.SIDM.isothermal.F90
@@ -0,0 +1,648 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Provides a mass distribution implementing the ``isothermal'' approximation to the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+
+ use, intrinsic :: ISO_C_Binding , only : c_size_t
+ use :: Numerical_Interpolation, only : interpolator
+ use :: Numerical_ODE_Solvers , only : odeSolver
+
+ !![
+
+
+ A mass distribution class for self-interacting dark matter following the ``isothermal'' model of \cite{jiang_semi-analytic_2023}. This
+ model assumes that the dark matter within the interaction radius, $r_1$, has thermalized and can therefore be described by a
+ constant velocity dispersion, $\sigma_0$. Under this assumption the spherical Jeans equation has a solution of the form:
+ \begin{equation}
+ \rho(r) = \rho_0 \exp\left[-\frac{\phi(r)}{\sigma_0^2}\right],
+ \end{equation}
+ where $\rho(r)$ is the density $\rho_0$ is the density at $r=0$, and the gravitational potential satisfies \citep{jiang_semi-analytic_2023}:
+ \begin{equation}
+ \nabla^2 \phi(r) = 4 \pi \mathrm{G} \rho_0 \exp \left( - \frac{\phi(r)}{\sigma_0^2} \right).
+ \end{equation}
+ This second-order differential equation is solved using the boundary conditions $\phi(r=0)=0$ and
+ $\mathrm{d}\phi/\mathrm{d}r(r=0)=0$. The values of $\rho_0$ and $\sigma_0$ are then found by minimizing a function
+ \begin{equation}
+ \delta^2(\rho_0,\sigma_0) = \left[ \frac{\rho(r_1)}{\rho^\prime(r_1)} - 1 \right]^2 + \left[ \frac{M(r_1)}{M^\prime(r_1)} - 1 \right]^2,
+ \end{equation}
+ where $M(r)$ is the mass contained within radius $r$, and primes indicate the profile prior to SIDM thermalization.
+
+ This can be expressed in a convenient dimensionless form. We define $x=r/r_1$, $y=\rho/\rho_1$, $z=\sigma/\sigma_1$, where
+ \begin{equation}
+ \sigma_1^2 = \frac{4 \pi}{3} \mathrm{G} \rho_1 r_1^2 \xi,
+ \end{equation}
+ and we define $\xi$ through the relation:
+ \begin{equation}
+ M_1 = \xi \frac{4 \pi}{3} \rho_1 r_1^3.
+ \end{equation}
+ Using these definitions we can define a dimensionless potential, $\Phi(r) = \phi(r) / \sigma_1^2$. The above differential
+ equation can then be written as
+ \begin{equation}
+ \nabla^{\prime 2} \Phi = \frac{3}{\xi} y_0 \exp\left[ - \frac{\Phi}{z_0^2} \right] ,
+ \end{equation}
+ where $\nabla^{\prime 2}$ indicates the Laplacian with respect to coordinate $x$. Written in this form it is straightforward
+ to see that this equation has three parameters, $\xi$, $y_0$, and $z_0$. The value of $\xi$ is determined from the initial
+ (pre-thermalization) density profile. We then have two constraints at $x=1$, namely $y=1$ and $m=M/M_1=1$. We can solve for
+ the values of $y_0$ and $z_0$ which satisfy these constraints for a given $\xi$. As a result, we can tabulate solutions
+ $y_0(\xi)$ and $z_0(\xi)$ which are applicable to any initial density profile and depend only on the effective slope of the
+ density profile inside $r_1$, since if $\rho \propto r^\alpha$ then $\xi = 1/(1+\alpha/3)$, such that $\alpha=0$ (the
+ largest physically-allowed value of $\alpha$) implies $\xi=1$.
+
+
+ !!]
+ type, extends(massDistributionSphericalSIDM) :: massDistributionSphericalSIDMIsothermal
+ !!{
+ A mass distribution implementing the ``isothermal'' approximation to the effects of SIDM based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+ private
+ double precision :: velocityDispersionCentral , radiusInteraction_ , &
+ & densityInteraction , massInteraction , &
+ & velocityDispersionInteraction
+ type (interpolator ), allocatable :: densityCentralDimensionless , velocityDispersionCentralDimensionless, &
+ & interpolatorRadiiDimensionless, interpolatorXi
+ double precision :: xiTabulatedMinimum , xiTabulatedMaximum
+ double precision , allocatable, dimension( : ,:) :: densityProfileDimensionless , massProfileDimensionless
+ double precision , allocatable, dimension( : ) :: radiiDimensionless
+ integer (c_size_t ) :: indexXi
+ double precision , dimension(0:1 ) :: factorsXi
+ contains
+ !![
+
+
+
+
+ !!]
+ final :: sphericalSIDMIsothermalDestructor
+ procedure :: computeSolution => sphericalSIDMIsothermalComputeSolution
+ procedure :: tabulateSolutions => sphericalSIDMIsothermalTabulateSolutions
+ procedure :: density => sphericalSIDMIsothermalDensity
+ procedure :: densityGradientRadial => sphericalSIDMIsothermalDensityGradientRadial
+ procedure :: massEnclosedBySphere => sphericalSIDMIsothermalMassEnclosedBySphere
+ procedure :: potential => sphericalSIDMIsothermalPotential
+ procedure :: potentialIsAnalytic => sphericalSIDMIsothermalPotentialIsAnalytic
+ end type massDistributionSphericalSIDMIsothermal
+
+ interface massDistributionSphericalSIDMIsothermal
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalSIDMIsothermal} mass distribution class.
+ !!}
+ module procedure sphericalSIDMIsothermalConstructorParameters
+ module procedure sphericalSIDMIsothermalConstructorInternal
+ end interface massDistributionSphericalSIDMIsothermal
+
+ ! Number of properties in ODE.
+ integer (c_size_t ), parameter :: propertyCount=2
+
+ ! Submodule-scope variables.
+ double precision :: xi_ , y0_, &
+ & z0_
+ type (odeSolver), allocatable :: odeSolver_
+ !$omp threadprivate(xi_,y0_,z0_,odeSolver_)
+
+contains
+
+ function sphericalSIDMIsothermalConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sidmIsothermal} mass distribution class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalSIDMIsothermal) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (darkMatterParticleClass ), pointer :: darkMatterParticle_
+ type (varying_string ) :: componentType , massType, &
+ & nonAnalyticSolver
+ double precision :: timeAge
+
+ !![
+
+ timeAge
+
+ The age of the halo (in Gyr).
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalSIDMIsothermal(timeAge,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,darkMatterParticle_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalSIDMIsothermalConstructorParameters
+
+ function sphericalSIDMIsothermalConstructorInternal(timeAge,nonAnalyticSolver,massDistribution_,darkMatterParticle_,componentType,massType) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily sidmIsothermal} mass distribution class.
+ !!}
+ use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
+ implicit none
+ type (massDistributionSphericalSIDMIsothermal) :: self
+ double precision , intent(in ) :: timeAge
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ class (darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Validate the dark matter particle type.
+ select type (darkMatterParticle__ => self%darkMatterParticle_)
+ class is (darkMatterParticleSelfInteractingDarkMatter)
+ ! This is as expected.
+ class default
+ call Error_Report('this class expects a self-interacting dark matter particle'//{introspection:location})
+ end select
+ self%dimensionless =.false.
+ self%xiTabulatedMinimum=+huge(0.0d0)
+ self%xiTabulatedMaximum=-huge(0.0d0)
+ call self%computeSolution()
+ return
+ end function sphericalSIDMIsothermalConstructorInternal
+
+ subroutine sphericalSIDMIsothermalDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily massDistributionSphericalSIDMIsothermal} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalSIDMIsothermal), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalSIDMIsothermalDestructor
+
+ subroutine sphericalSIDMIsothermalTabulateSolutions(self,xiRequired)
+ !!{
+ Tabulate solutions for $y_0(\xi)$, $z_0(\xi)$.
+ !!}
+ use :: Display , only : displayIndent , displayUnindent , displayMessage, verbosityLevelWorking, &
+ & displayCounter , displayCounterClear
+ use :: File_Utilities , only : Directory_Make , File_Exists , File_Lock , File_Path , &
+ & File_Unlock , lockDescriptor
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear
+ use :: Multidimensional_Minimizer, only : multiDMinimizer
+ implicit none
+ class (massDistributionSphericalSIDMIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: xiRequired
+ integer , parameter :: countXiPerUnit = 100
+ integer , parameter :: countRadii =1000
+ double precision , parameter :: Y0Minimum =1.0d+0, Y0Maximum =1.0d+6
+ double precision , parameter :: Z0Minimum =0.1d+0, Z0Maximum =3.0d+0
+ double precision , parameter :: xiMinimum =1.1d+0, xiMaximum =1.0d+1
+ double precision , parameter :: x1 =1.0d+0
+ double precision , parameter :: odeToleranceAbsolute=1.0d-9, odeToleranceRelative=1.0d-9
+ double precision , dimension(propertyCount+1 ) :: properties , propertyScales
+ double precision , dimension(propertyCount ) :: locationMinimum
+ double precision , dimension( : ), allocatable :: xi , y0 , &
+ & z0
+ double precision :: x
+ type (multiDMinimizer ) , allocatable :: minimizer_
+ integer :: countXi , count
+ integer (c_size_t ) :: i , j , &
+ & iteration
+ logical :: converged , retabulate
+ type (varying_string ) :: fileName
+ type (hdf5Object ) :: file
+ type (lockDescriptor ) :: fileLock
+ character (len=16 ) :: labelXiMinimum , labelXiMaximum
+
+ ! Return immediately if solutions have been tabulated with sufficient extent already.
+ if ( &
+ & xiRequired >= self%xiTabulatedMinimum &
+ & .and. &
+ & xiRequired <= self%xiTabulatedMaximum &
+ & ) return
+ ! Deallocate existing table if necessary.
+ if (allocated(self%radiiDimensionless )) deallocate(self%radiiDimensionless )
+ if (allocated(self%densityProfileDimensionless )) deallocate(self%densityProfileDimensionless )
+ if (allocated(self%massProfileDimensionless )) deallocate(self%massProfileDimensionless )
+ if (allocated(self%interpolatorXi )) deallocate(self%interpolatorXi )
+ if (allocated(self%interpolatorRadiiDimensionless )) deallocate(self%interpolatorRadiiDimensionless )
+ if (allocated(self%densityCentralDimensionless )) deallocate(self%densityCentralDimensionless )
+ if (allocated(self%velocityDispersionCentralDimensionless)) deallocate(self%velocityDispersionCentralDimensionless)
+ ! By default assume that we do need to retabulate.
+ retabulate=.true.
+ ! Construct a file name for the table.
+ fileName=inputPath(pathTypeDataDynamic)// &
+ & 'darkMatter/' // &
+ & self%objectType() // &
+ & '.hdf5'
+ call Directory_Make(char(File_Path(char(fileName))))
+ if (File_Exists(fileName)) then
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
+ ! Restore tables from file.
+ !$ call hdf5Access%set()
+ call file%openFile (char(fileName) )
+ call file%readDataset('xi' , xi )
+ call file%readDataset('radii' ,self%radiiDimensionless )
+ call file%readDataset('y0' , y0 )
+ call file%readDataset('z0' , z0 )
+ call file%readDataset('densityProfileDimensionless',self%densityProfileDimensionless)
+ call file%readDataset('massProfileDimensionless' ,self%massProfileDimensionless )
+ call file%close ( )
+ !$ call hdf5Access%unset()
+ self%xiTabulatedMinimum=xi( 1 )
+ self%xiTabulatedMaximum=xi(size(xi))
+ ! Check if the table is sufficient.
+ retabulate= xiRequired < self%xiTabulatedMinimum &
+ & .or. &
+ & xiRequired > self%xiTabulatedMaximum
+ call File_Unlock(fileLock)
+ end if
+ ! Retabulate now if necessary.
+ if (retabulate) then
+ if (allocated( xi )) deallocate( xi )
+ if (allocated( y0 )) deallocate( y0 )
+ if (allocated( z0 )) deallocate( z0 )
+ if (allocated(self%radiiDimensionless )) deallocate(self%radiiDimensionless )
+ if (allocated(self%densityProfileDimensionless)) deallocate(self%densityProfileDimensionless)
+ if (allocated(self%massProfileDimensionless )) deallocate(self%massProfileDimensionless )
+ ! Set extent for tabulation.
+ self%xiTabulatedMinimum=min(1.0d0*xiRequired,xiMinimum)
+ self%xiTabulatedMaximum=max(1.1d0*xiRequired,xiMaximum)
+ write (labelXiMinimum,'(f5.2)') self%xiTabulatedMinimum
+ write (labelXiMaximum,'(f5.2)') self%xiTabulatedMaximum
+ call displayIndent ('tabulating isothermal SIDM density profile solutions' ,verbosityLevelWorking)
+ call displayMessage('range: '//trim(adjustl(labelXiMinimum))//' < ξ < '//trim(adjustl(labelXiMaximum))//'',verbosityLevelWorking)
+ ! Construct ranges of the parameter ξ to span.
+ countXi=int((self%xiTabulatedMaximum-self%xiTabulatedMinimum)*dble(countXiPerUnit))+1
+ allocate( xi ( countXi))
+ allocate( y0 ( countXi))
+ allocate( z0 ( countXi))
+ allocate(self%radiiDimensionless (countRadii ))
+ allocate(self%densityProfileDimensionless(countRadii,countXi))
+ allocate(self%massProfileDimensionless (countRadii,countXi))
+ xi =Make_Range(self%xiTabulatedMinimum,self%xiTabulatedMaximum,countXi ,rangeTypeLinear)
+ self%radiiDimensionless=Make_Range( 0.0d0 , 1.0d0 ,countRadii,rangeTypeLinear)
+ ! Set absolute property scales for ODE solving.
+ propertyScales=1.0d0
+ ! Start parallel region to solve for halo structure at each value of ξ.
+ count=0
+ call displayCounter(count,isNew=.true.,verbosity=verbosityLevelWorking)
+ !$omp parallel private(i,j,x,properties,locationMinimum,iteration,converged,minimizer_)
+ !! Allocate and construct objects needed by each thread.
+ allocate(odeSolver_)
+ allocate(minimizer_)
+ odeSolver_=odeSolver (propertyCount+1,sphericalSIDMIsothermalDimensionlessODEs ,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative,scale=propertyScales)
+ minimizer_=multiDMinimizer(propertyCount ,sphericalSIDMIsothermalDimensionlessFitMetric )
+ !$omp do schedule(dynamic)
+ do i=1,countXi
+ xi_=xi(i)
+ ! Seek the low-density solution.
+ call minimizer_%set(x=[0.0d0,1.0d0],stepSize=[0.01d0,0.01d0])
+ iteration=0
+ converged=.false.
+ do while (.not.converged .and. iteration < 100)
+ call minimizer_%iterate()
+ iteration=iteration+1
+ converged=minimizer_%testSize(toleranceAbsolute=1.0d-12)
+ end do
+ locationMinimum=minimizer_%x()
+ y0(i)=exp(locationMinimum(1))
+ z0(i)= locationMinimum(2)
+ ! Tabulate solutions for density and mass.
+ do j=1,countRadii
+ x =0.0d0
+ properties=0.0d0
+ call odeSolver_%solve(x,self%radiiDimensionless(j),properties)
+ self%densityProfileDimensionless(j,i)=+y0(i) &
+ & *exp( &
+ & -properties(1) &
+ & /z0(i) **2 &
+ & )
+ self%massProfileDimensionless (j,i)=+ properties(3)
+ end do
+ !$omp atomic
+ count=count+1
+ call displayCounter(int(100.0d0*dble(count)/dble(countXi)),isNew=.false.,verbosity=verbosityLevelWorking)
+ end do
+ !$omp end do
+ call displayCounterClear(verbosityLevelWorking)
+ deallocate(odeSolver_)
+ deallocate(minimizer_)
+ !$omp end parallel
+ ! Write the data to file.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
+ !$ call hdf5Access%set()
+ call file%openFile (char( fileName ) ,overWrite=.true.,readOnly=.false.)
+ call file%writeDataset( xi ,'xi' )
+ call file%writeDataset( self%radiiDimensionless ,'radii' )
+ call file%writeDataset( y0 ,'y0' )
+ call file%writeDataset( z0 ,'z0' )
+ call file%writeDataset( self%densityProfileDimensionless ,'densityProfileDimensionless' )
+ call file%writeDataset( self%massProfileDimensionless ,'massProfileDimensionless' )
+ call file%close ( )
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ call displayUnindent('done',verbosityLevelWorking)
+ end if
+ ! Build the interpolators.
+ allocate(self%interpolatorXi )
+ allocate(self%interpolatorRadiiDimensionless )
+ allocate(self%densityCentralDimensionless )
+ allocate(self%velocityDispersionCentralDimensionless)
+ self%densityCentralDimensionless =interpolator( xi ,y0)
+ self%velocityDispersionCentralDimensionless=interpolator( xi ,z0)
+ self%interpolatorXi =interpolator( xi )
+ self%interpolatorRadiiDimensionless =interpolator(self%radiiDimensionless )
+ return
+ end subroutine sphericalSIDMIsothermalTabulateSolutions
+
+ double precision function sphericalSIDMIsothermalDimensionlessFitMetric(propertiesCentral)
+ !!{
+ Evaluate the fit metric.
+ !!}
+ implicit none
+ double precision, intent(in ), dimension(:) :: propertiesCentral
+ double precision, parameter :: x1 =1.0d0
+ double precision , dimension(propertyCount+1) :: properties
+ double precision :: x , y1, &
+ & m1
+
+ ! Extract current parameters to submodule-scope.
+ y0_=exp(propertiesCentral(1))
+ z0_= propertiesCentral(2)
+ ! Solve the ODE to x₁.
+ x =0.0d0
+ properties=0.0d0
+ call odeSolver_%solve(x,x1,properties)
+ ! Extract density and mass at x₁.
+ y1 =+y0_ &
+ & *exp( &
+ & -properties(1) &
+ & /z0_ **2 &
+ & )
+ m1 =+ properties(3)
+ ! Evaluate the fit metric.
+ sphericalSIDMIsothermalDimensionlessFitMetric=+(y1-1.0d0)**2 &
+ & +(m1-1.0d0)**2
+ return
+ end function sphericalSIDMIsothermalDimensionlessFitMetric
+
+ integer function sphericalSIDMIsothermalDimensionlessODEs(x,properties,propertiesRateOfChange)
+ !!{
+ Define the dimensionless ODE system to solve for isothermal self-interacting dark matter cores.
+ !!}
+ use :: Interface_GSL, only : GSL_Success
+ implicit none
+ double precision, intent(in ) :: x
+ double precision, intent(in ), dimension(:) :: properties
+ double precision, intent( out), dimension(:) :: propertiesRateOfChange
+ double precision :: y
+
+ ! Compute the dimensionless density.
+ y =+y0_ &
+ & *exp( &
+ & -max(properties(1),0.0d0) &
+ & /z0_**2 &
+ & )
+ ! Evaluate the ODE.
+ propertiesRateOfChange (1)=+properties(2)
+ propertiesRateOfChange (2)=+3.0d0 &
+ & /xi_ &
+ & *y
+ if (x > 0.0d0) &
+ & propertiesRateOfChange(2)=+propertiesRateOfChange(2) &
+ & -2.0d0 &
+ & *properties (2) &
+ & /x
+ propertiesRateOfChange (3)=+3.0d0 &
+ & /xi_ &
+ & *x**2 &
+ & *y
+ sphericalSIDMIsothermalDimensionlessODEs=GSL_Success
+ return
+ end function sphericalSIDMIsothermalDimensionlessODEs
+
+ subroutine sphericalSIDMIsothermalComputeSolution(self)
+ !!{
+ Compute a solution for the isothermal core of an SIDM halo.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalSIDMIsothermal), intent(inout) :: self
+ integer , parameter :: countTable =1000
+ double precision , parameter :: odeToleranceAbsolute =1.0d-3, odeToleranceRelative =1.0d-3
+ double precision :: densityCentral , velocityDispersionCentral , &
+ & densityInteraction , massInteraction , &
+ & radiusInteraction , xi , &
+ & velocityDispersionInteraction
+ type (coordinateSpherical ) :: coordinatesInteraction
+
+ ! Find the interaction radius.
+ radiusInteraction =self%radiusInteraction ( )
+ coordinatesInteraction=[radiusInteraction,0.0d0,0.0d0]
+ ! Properties of the original density profile at the interaction radius.
+ densityInteraction =self%massDistribution_%density (coordinatesInteraction)
+ massInteraction =self%massDistribution_%massEnclosedBySphere(radiusInteraction )
+ ! Find the velocity dispersion scale to be applied to the dimensionless solutions.
+ velocityDispersionInteraction=sqrt(gravitationalConstantGalacticus*massInteraction/radiusInteraction)
+ ! Compute the ξ parameter.
+ xi =+massInteraction &
+ & *3.0d0 &
+ & /4.0d0 &
+ & /Pi &
+ & /densityInteraction &
+ & /radiusInteraction **3
+ ! Ensure dimensionless solutions have been tabulated.
+ call self%tabulateSolutions(xi)
+ ! Find the properties at the halo center.
+ densityCentral =self%densityCentralDimensionless %interpolate(xi)*densityInteraction
+ velocityDispersionCentral =self%velocityDispersionCentralDimensionless%interpolate(xi)*velocityDispersionInteraction
+ ! Store properties of current profile.
+ self%radiusInteraction_ =radiusInteraction
+ self%densityInteraction =densityInteraction
+ self%massInteraction =massInteraction
+ self%velocityDispersionInteraction=velocityDispersionInteraction
+ self%velocityDispersionCentral =velocityDispersionCentral
+ ! Compute interpolating factors in ξ.
+ call self%interpolatorXi%linearFactors(xi,self%indexXi,self%factorsXi)
+ return
+ end subroutine sphericalSIDMIsothermalComputeSolution
+
+ double precision function sphericalSIDMIsothermalDensity(self,coordinates) result(density)
+ !!{
+ Compute the density at the specified {\normalfont \ttfamily coordinates} for the {\normalfont \ttfamily sphericalSIDMIsothermal}
+ mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMIsothermal), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ integer (c_size_t ) :: i , j, &
+ & indexRadius
+ double precision , dimension(0:1) :: factorsRadius
+
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ density=self%massDistribution_%density(coordinates)
+ else
+ call self%interpolatorRadiiDimensionless%linearFactors(coordinates%rSpherical()/self%radiusInteraction_,indexRadius,factorsRadius)
+ density=0.0d0
+ do i=0,1
+ do j=0,1
+ density=+ density &
+ & +self%densityProfileDimensionless(indexRadius+i,self%indexXi+j) &
+ & * factorsRadius ( i ) &
+ & *self%factorsXi ( j)
+ end do
+ end do
+ density=+ density &
+ & *self%densityInteraction
+ end if
+ return
+ end function sphericalSIDMIsothermalDensity
+
+ double precision function sphericalSIDMIsothermalDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a truncated spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMIsothermal), intent(inout) , target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ) , optional :: logarithmic
+ integer (c_size_t ) :: indexRadius
+ double precision , dimension(0:1) :: factorsRadius
+ !![
+
+ !!]
+
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ densityGradient=self%massDistribution_%densityGradientRadial(coordinates,logarithmic=logarithmic)
+ else
+ call self%interpolatorRadiiDimensionless%linearFactors(coordinates%rSpherical()/self%radiusInteraction_,indexRadius,factorsRadius)
+ if (indexRadius > 1) then
+ densityGradient=+log(self%densityProfileDimensionless(indexRadius+1,self%indexXi+0)/self%densityProfileDimensionless(indexRadius+0,self%indexXi+0)) &
+ & /log(self%radiiDimensionless (indexRadius+1 )/self%radiiDimensionless (indexRadius+0 ))
+ if (.not.logarithmic_) &
+ densityGradient=+ densityGradient &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ else
+ densityGradient=+0.0d0
+ end if
+ end if
+ return
+ end function sphericalSIDMIsothermalDensityGradientRadial
+
+ double precision function sphericalSIDMIsothermalMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for the {\normalfont \ttfamily sphericalSIDMIsothermal}
+ mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMIsothermal), intent(inout) , target :: self
+ double precision , intent(in ) :: radius
+ integer (c_size_t ) :: i , j, &
+ & indexRadius
+ double precision , dimension(0:1) :: factorsRadius
+
+ if (radius > self%radiusInteraction()) then
+ mass=self%massDistribution_%massEnclosedBySphere(radius)
+ else
+ call self%interpolatorRadiiDimensionless%linearFactors(radius/self%radiusInteraction_,indexRadius,factorsRadius)
+ mass=0.0d0
+ do i=0,1
+ do j=0,1
+ mass =+ mass &
+ & +self%massProfileDimensionless(indexRadius+i,self%indexXi+j) &
+ & * factorsRadius ( i ) &
+ & *self%factorsXi ( j)
+ end do
+ end do
+ mass =+ mass &
+ & *self%massInteraction
+ end if
+ return
+ end function sphericalSIDMIsothermalMassEnclosedBySphere
+
+ logical function sphericalSIDMIsothermalPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSphericalSIDMIsothermal), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function sphericalSIDMIsothermalPotentialIsAnalytic
+
+ double precision function sphericalSIDMIsothermalPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an burkert mass distribution.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
+ implicit none
+ class(massDistributionSphericalSIDMIsothermal), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
+ type (coordinateSpherical ) :: coordinatesInteraction
+
+ if (present(status)) status=structureErrorCodeSuccess
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ potential =+ self%massDistribution_%potential (coordinates )
+ else
+ coordinatesInteraction=[self%radiusInteraction_,0.0d0,0.0d0]
+ potential =+ self%massDistribution_%potential (coordinatesInteraction) &
+ & - self %velocityDispersionCentral **2 &
+ & *log( &
+ & +self %density (coordinates ) &
+ & /self %density (coordinatesInteraction) &
+ & )
+ end if
+ return
+ end function sphericalSIDMIsothermalPotential
diff --git a/source/mass_distributions.spherical.SIDM.isothermal.baryons.F90 b/source/mass_distributions.spherical.SIDM.isothermal.baryons.F90
new file mode 100644
index 0000000000..7f8b6af1e0
--- /dev/null
+++ b/source/mass_distributions.spherical.SIDM.isothermal.baryons.F90
@@ -0,0 +1,484 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Provides a mass distribution implementing the ``isothermal'' approximation to the effects of SIDM, including the baryonic
+ potential, based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+
+ use, intrinsic :: ISO_C_Binding , only : c_size_t
+ use :: Numerical_Interpolation, only : interpolator
+ use :: Numerical_ODE_Solvers , only : odeSolver
+
+ public :: sphericalSIDMIsothermalBaryonsInitializor
+
+ !![
+
+
+ Mass distributions for self-interacting dark matter following the ``isothermal'' model of \cite{jiang_semi-analytic_2023}. This
+ model assumes that the dark matter within the interaction radius, $r_1$, has thermalized and can therefore be described by a
+ constant velocity dispersion, $\sigma_0$. Under this assumption the spherical Jeans equation has a solution of the form:
+ \begin{equation}
+ \rho(r) = \rho_0 \exp\left[-\frac{\phi(r)}{\sigma_0^2}\right],
+ \end{equation}
+ where $\rho(r)$ is the density $\rho_0$ is the density at $r=0$, and the gravitational potential satisfies \citep{jiang_semi-analytic_2023}:
+ \begin{equation}
+ \nabla^2 \phi(r) = 4 \pi \mathrm{G} \left[ \rho_0 \exp \left( - \frac{\phi(r)}{\sigma_0^2} \right) + \rho_\mathrm{b}(r) \right],
+ \end{equation}
+ where $\rho_\mathrm{b}(r)$ is the density of the baryonic component. This second-order differential equation is solved using the boundary conditions $\phi(r=0)=0$ and
+ $\mathrm{d}\phi/\mathrm{d}r(r=0)=0$. The values of $\rho_0$ and $\sigma_0$ are then found by minimizing a function
+ \begin{equation}
+ \delta^2(\rho_0,\sigma_0) = \left[ \frac{\rho(r_1)}{\rho^\prime(r_1)} - 1 \right]^2 + \left[ \frac{M(r_1)}{M^\prime(r_1)} - 1 \right]^2,
+ \end{equation}
+ where $M(r)$ is the mass contained within radius $r$, and primes indicate the profile prior to SIDM thermalization.
+
+
+ !!]
+ type, extends(massDistributionSphericalSIDM) :: massDistributionSphericalSIDMIsothermalBaryons
+ !!{
+ A mass distribution implementing the ``isothermal'' approximation to the effects of SIDM, including the baryonic potential,
+ based on the model of \cite{jiang_semi-analytic_2023}.
+ !!}
+ private
+ double precision :: velocityDispersionCentral
+ class (massDistributionClass ), pointer :: massDistributionBaryonic => null()
+ type (interpolator ), allocatable :: densityProfile , massProfile
+ ! Call-back function and arguments used for as-needed initialization of the baryonic component.
+ logical :: initialized
+ procedure (sphericalSIDMIsothermalBaryonsInitializor), pointer , nopass :: initializationFunction
+ class (* ), pointer :: initializationSelf => null(), initializationArgument => null()
+ contains
+ !![
+
+
+
+
+ !!]
+ final :: sphericalSIDMIsothermalBaryonsDestructor
+ procedure :: setBaryonicComponent => sphericalSIDMIsothermalBaryonsSetBaryonicComponent
+ procedure :: computeSolution => sphericalSIDMIsothermalBaryonsComputeSolution
+ procedure :: density => sphericalSIDMIsothermalBaryonsDensity
+ procedure :: densityGradientRadial => sphericalSIDMIsothermalBaryonsDensityGradientRadial
+ procedure :: massEnclosedBySphere => sphericalSIDMIsothermalBaryonsMassEnclosedBySphere
+ procedure :: potentialIsAnalytic => sphericalSIDMIsothermalBaryonsPotentialIsAnalytic
+ procedure :: potential => sphericalSIDMIsothermalBaryonsPotential
+ end type massDistributionSphericalSIDMIsothermalBaryons
+
+ interface massDistributionSphericalSIDMIsothermalBaryons
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalSIDMIsothermalBaryons} mass distribution class.
+ !!}
+ module procedure sphericalSIDMIsothermalBaryonsConstructorParameters
+ module procedure sphericalSIDMIsothermalBaryonsConstructorInternal
+ end interface massDistributionSphericalSIDMIsothermalBaryons
+
+ abstract interface
+ subroutine sphericalSIDMIsothermalBaryonsInitializor(initializationSelf,initializationArgument,massDistributionBaryonic)
+ !!{
+ Interface for call-back functions for as-needed initialization of the baryonic component.
+ !!}
+ import massDistributionClass
+ class(* ), intent(inout), target :: initializationSelf , initializationArgument
+ class(massDistributionClass), intent( out), pointer :: massDistributionBaryonic
+ end subroutine sphericalSIDMIsothermalBaryonsInitializor
+ end interface
+
+contains
+
+ function sphericalSIDMIsothermalBaryonsConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sidmIsothermal} mass distribution class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalSIDMIsothermalBaryons) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionBaryonic
+ class (darkMatterParticleClass ), pointer :: darkMatterParticle_
+ procedure (sphericalSIDMIsothermalBaryonsInitializor ), pointer :: initializationFunction
+ class (* ), pointer :: initializationSelf , initializationArgument
+ type (varying_string ) :: componentType , massType , &
+ & nonAnalyticSolver
+ double precision :: timeAge
+
+ !![
+
+ timeAge
+
+ The age of the halo (in Gyr).
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ initializationFunction => null()
+ initializationSelf => null()
+ initializationArgument => null()
+ self=massDistributionSphericalSIDMIsothermalBaryons(timeAge,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,massDistributionBaryonic,darkMatterParticle_,initializationFunction,initializationSelf,initializationArgument,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalSIDMIsothermalBaryonsConstructorParameters
+
+ function sphericalSIDMIsothermalBaryonsConstructorInternal(timeAge,nonAnalyticSolver,massDistribution_,massDistributionBaryonic,darkMatterParticle_,initializationFunction,initializationSelf,initializationArgument,componentType,massType) result(self)
+ !!{
+ Internal constructor for the {\normalfont \ttfamily sidmIsothermal} mass distribution class.
+ !!}
+ use :: Dark_Matter_Particles, only : darkMatterParticleSelfInteractingDarkMatter
+ implicit none
+ type (massDistributionSphericalSIDMIsothermalBaryons) :: self
+ double precision , intent(in ) :: timeAge
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ class (massDistributionClass ), intent(in ), target :: massDistributionBaryonic
+ class (darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ procedure (sphericalSIDMIsothermalBaryonsInitializor ), intent(in ), pointer :: initializationFunction
+ class (* ), intent(in ), pointer :: initializationSelf , initializationArgument
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Validate the dark matter particle type.
+ select type (darkMatterParticle__ => self%darkMatterParticle_)
+ class is (darkMatterParticleSelfInteractingDarkMatter)
+ ! This is as expected.
+ class default
+ call Error_Report('this class expects a self-interacting dark matter particle'//{introspection:location})
+ end select
+ ! Initialize state.
+ self%dimensionless=.false.
+ self%initialized =.not.associated(initializationFunction)
+ return
+ end function sphericalSIDMIsothermalBaryonsConstructorInternal
+
+ subroutine sphericalSIDMIsothermalBaryonsDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalSIDMIsothermalBaryons} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalSIDMIsothermalBaryons), intent(inout) :: self
+
+ !![
+
+
+
+ !!]
+ return
+ end subroutine sphericalSIDMIsothermalBaryonsDestructor
+
+ subroutine sphericalSIDMIsothermalBaryonsSetBaryonicComponent(self)
+ !!{
+ Set the baryonic component properties in an adiabatically-contracted spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalSIDMIsothermalBaryons), intent(inout) :: self
+ class(massDistributionClass ), pointer :: massDistributionBaryonic
+
+ if (.not.self%initialized) then
+ call self%initializationFunction(self%initializationSelf,self%initializationArgument,massDistributionBaryonic)
+ self%massDistributionBaryonic => massDistributionBaryonic
+ self%initialized = .true.
+ call self%computeSolution()
+ end if
+ return
+ end subroutine sphericalSIDMIsothermalBaryonsSetBaryonicComponent
+
+ subroutine sphericalSIDMIsothermalBaryonsComputeSolution(self)
+ !!{
+ Compute a solution for the isothermal core of an SIDM halo.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_ODE_Solvers , only : odeSolver
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLinear
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Multidimensional_Minimizer , only : multiDMinimizer
+ implicit none
+ class (massDistributionSphericalSIDMIsothermalBaryons), intent(inout) :: self
+ integer (c_size_t ), parameter :: propertyCount = 2
+ integer , parameter :: countTable =1000
+ double precision , parameter :: odeToleranceAbsolute =1.0d-3, odeToleranceRelative =1.0d-3
+ double precision , parameter :: fractionRadiusInitial =1.0d-6
+ double precision , dimension(propertyCount+1) :: properties , propertyScales
+ double precision , dimension(countTable ) :: radiusTable , densityTable , &
+ & massTable
+ double precision , dimension(propertyCount ) :: locationMinimum
+ type (odeSolver ) :: odeSolver_
+ type (multiDMinimizer ) :: minimizer_
+ integer :: i , iteration
+ logical :: converged
+ double precision :: densityCentral , velocityDispersionCentral , &
+ & densityInteraction , massInteraction , &
+ & radiusInteraction , radius , &
+ & velocityDispersionInteraction , mass , &
+ & density
+ type (coordinateSpherical ) :: coordinatesInteraction
+
+ ! Find the interaction radius.
+ radiusInteraction =self%radiusInteraction ( )
+ coordinatesInteraction =[radiusInteraction,0.0d0,0.0d0]
+ ! Properties of the original density profile at the interaction radius.
+ densityInteraction =self%massDistribution_%density (coordinatesInteraction)
+ massInteraction =self%massDistribution_%massEnclosedBySphere(radiusInteraction )
+ ! Find the velocity dispersion scale.
+ velocityDispersionInteraction=sqrt(gravitationalConstantGalacticus*massInteraction/radiusInteraction)
+ ! Set ODE solver scales.
+ propertyScales =[velocityDispersionInteraction**2,velocityDispersionInteraction**2/radiusInteraction,massInteraction]
+ ! Construct an ODE solver.
+ odeSolver_ =odeSolver (propertyCount+1,sidmIsothermalODEs ,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative,scale=propertyScales)
+ ! Construct a minimizer.
+ minimizer_ =multiDMinimizer(propertyCount ,sidmIsothermalFitMetric )
+ ! Seek the solution.
+ call minimizer_%set(x=[0.0d0,1.0d0],stepSize=[1.0d0,1.0d0])
+ iteration=0
+ converged=.false.
+ do while (.not.converged .and. iteration < 100)
+ call minimizer_%iterate()
+ iteration=iteration+1
+ converged=minimizer_%testSize(toleranceAbsolute=1.0d-3)
+ end do
+ locationMinimum =minimizer_%x()
+ densityCentral =exp(locationMinimum(1))*densityInteraction
+ velocityDispersionCentral= locationMinimum(2) *velocityDispersionInteraction
+ ! Tabulate solutions for density and mass.
+ radiusTable =Make_Range(rangeMinimum=0.0d0,rangeMaximum=radiusInteraction,rangeNumber=countTable,rangeType=rangeTypeLinear)
+ densityTable(1)=densityCentral
+ massTable (1)=0.0d0
+ do i=2,countTable
+ radius =fractionRadiusInitial*radiusInteraction
+ properties=0.0d0
+ call odeSolver_%solve(radius,radiusTable(i),properties)
+ densityTable(i)=+densityCentral &
+ & *exp( &
+ & -properties(1) &
+ & /velocityDispersionCentral**2 &
+ & )
+ massTable (i)=+ properties(3)
+ end do
+ allocate(self%densityProfile)
+ allocate(self% massProfile)
+ self% densityProfile=interpolator(radiusTable, densityTable)
+ self% massProfile=interpolator(radiusTable, massTable)
+ self%velocityDispersionCentral= velocityDispersionCentral
+ return
+
+ contains
+
+ double precision function sidmIsothermalFitMetric(propertiesCentral)
+ !!{
+ Evaluate the fit metric.
+ !!}
+ implicit none
+ double precision, intent(in ), dimension(:) :: propertiesCentral
+ double precision , dimension(propertyCount+1) :: properties
+ double precision :: radius
+
+ ! Extract current parameters.
+ densityCentral =exp(propertiesCentral(1))*densityInteraction
+ velocityDispersionCentral= propertiesCentral(2) *velocityDispersionInteraction
+ ! Solve the ODE to r₁.
+ radius =fractionRadiusInitial*radiusInteraction
+ properties=0.0d0
+ call odeSolver_%solve(radius,radiusInteraction,properties)
+ ! Extract density and mass at r₁.
+ density=+densityCentral &
+ & *exp( &
+ & -properties(1) &
+ & /velocityDispersionCentral**2 &
+ & )
+ mass =+ properties(3)
+ ! Evaluate the fit metric.
+ sidmIsothermalFitMetric=+(density/densityInteraction-1.0d0)**2 &
+ & +( mass/ massInteraction-1.0d0)**2
+ return
+ end function sidmIsothermalFitMetric
+
+ integer function sidmIsothermalODEs(radius,properties,propertiesRateOfChange)
+ !!{
+ Define the ODE system to solve for isothermal self-interacting dark matter cores.
+ !!}
+ use :: Interface_GSL , only : GSL_Success
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ double precision , intent(in ) :: radius
+ double precision , intent(in ), dimension(:) :: properties
+ double precision , intent( out), dimension(:) :: propertiesRateOfChange
+ double precision :: densityDarkMatter , densityBaryons
+ type (coordinateSpherical) :: coordinates
+
+ coordinates =[radius,0.0d0,0.0d0]
+ densityDarkMatter =+densityCentral &
+ & *exp( &
+ & -max(properties(1),0.0d0) &
+ & /velocityDispersionCentral**2 &
+ & )
+ densityBaryons =+self%massDistributionBaryonic%density(coordinates)
+ propertiesRateOfChange (1)=+properties(2)
+ propertiesRateOfChange (2)=+4.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +densityDarkMatter &
+ & +densityBaryons &
+ & )
+ if (radius > 0.0d0) &
+ & propertiesRateOfChange(2)=+propertiesRateOfChange(2) &
+ & -2.0d0 &
+ & *properties (2) &
+ & /radius
+ propertiesRateOfChange (3)=+4.0d0 &
+ & *Pi &
+ & *radius**2 &
+ & *densityDarkMatter
+ sidmIsothermalODEs = GSL_Success
+ return
+ end function sidmIsothermalODEs
+
+ end subroutine sphericalSIDMIsothermalBaryonsComputeSolution
+
+ double precision function sphericalSIDMIsothermalBaryonsDensity(self,coordinates) result(density)
+ !!{
+ Compute the density at the specified {\normalfont \ttfamily coordinates} for the {\normalfont \ttfamily sphericalSIDMIsothermalBaryons}
+ mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalSIDMIsothermalBaryons), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ call self%setBaryonicComponent()
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ density=self%massDistribution_%density (coordinates )
+ else
+ density=self%densityProfile %interpolate(coordinates%rSpherical())
+ end if
+ return
+ end function sphericalSIDMIsothermalBaryonsDensity
+
+ double precision function sphericalSIDMIsothermalBaryonsDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a truncated spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMIsothermalBaryons), intent(inout) , target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ) , optional :: logarithmic
+ !![
+
+ !!]
+
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ densityGradient=self%massDistribution_%densityGradientRadial(coordinates,logarithmic)
+ else
+ densityGradient=self%densityProfile%derivative(coordinates%rSpherical())
+ if (logarithmic_) &
+ & densityGradient=+ densityGradient &
+ & *coordinates%rSpherical ( ) &
+ & /self %densityProfile%interpolate(coordinates%rSpherical())
+ end if
+ return
+ end function sphericalSIDMIsothermalBaryonsDensityGradientRadial
+
+ double precision function sphericalSIDMIsothermalBaryonsMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for the {\normalfont \ttfamily sphericalSIDMIsothermalBaryons}
+ mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalSIDMIsothermalBaryons), intent(inout) , target :: self
+ double precision , intent(in ) :: radius
+
+ call self%setBaryonicComponent()
+ if (radius > self%radiusInteraction()) then
+ mass=self%massDistribution_%massEnclosedBySphere(radius)
+ else
+ mass=self%massProfile %interpolate (radius)
+ end if
+ return
+ end function sphericalSIDMIsothermalBaryonsMassEnclosedBySphere
+
+ logical function sphericalSIDMIsothermalBaryonsPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return if the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSphericalSIDMIsothermalBaryons), intent(inout) :: self
+
+ isAnalytic=self%massDistribution_%potentialIsAnalytic()
+ return
+ end function sphericalSIDMIsothermalBaryonsPotentialIsAnalytic
+
+ double precision function sphericalSIDMIsothermalBaryonsPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an burkert mass distribution.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
+ implicit none
+ class (massDistributionSphericalSIDMIsothermalBaryons), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
+ type (coordinateSpherical ) :: coordinatesInteraction
+ double precision :: radiusInteraction
+
+ call self%setBaryonicComponent()
+ if (present(status)) status=structureErrorCodeSuccess
+ if (coordinates%rSpherical() > self%radiusInteraction()) then
+ potential =+self%massDistribution_%potential(coordinates,status=status)
+ else
+ radiusInteraction =+self%radiusInteraction()
+ coordinatesInteraction=[radiusInteraction,0.0d0,0.0d0]
+ potential =+self%massDistribution_%potential(coordinatesInteraction) &
+ & -self%velocityDispersionCentral**2 &
+ & *log( &
+ & +self%densityProfile%interpolate(coordinates%rSpherical ()) &
+ & /self%densityProfile%interpolate( radiusInteraction ) &
+ & )
+ end if
+ return
+ end function sphericalSIDMIsothermalBaryonsPotential
diff --git a/source/mass_distributions.spherical.Sersic.F90 b/source/mass_distributions.spherical.Sersic.F90
index 7489d468ab..3f2f1f32f5 100644
--- a/source/mass_distributions.spherical.Sersic.F90
+++ b/source/mass_distributions.spherical.Sersic.F90
@@ -33,17 +33,18 @@
!!{
The S\'ersic density profile.
!!}
- double precision :: densityNormalization , mass , &
- & radiusHalfMass_ , index_
+ double precision :: densityNormalization , mass , &
+ & radiusHalfMass_ , index_
! Tabulation of the Sérsic profile.
- double precision :: coefficient , radiusStart
- logical :: tableInitialized =.false.
+ double precision :: coefficient , radiusStart
+ logical :: tableInitialized =.false.
integer :: tableCount
- double precision :: tableRadiusMaximum , tableRadiusMinimum
+ double precision :: tableRadiusMaximum , tableRadiusMinimum
double precision :: table3dRadiusHalfMass
double precision :: table2dRadiusHalfMass
- double precision , allocatable, dimension(:) :: tableDensity , tableEnclosedMass , &
- & tablePotential , tableRadius
+ double precision :: gradientLogarithmicMassCentral
+ double precision , allocatable, dimension(:) :: tableDensity , tableEnclosedMass , &
+ & tablePotential , tableRadius
type (interpolator ) :: tableInterpolator
!$ integer (omp_lock_kind ) :: tableLock
contains
@@ -58,6 +59,7 @@
procedure :: densityRadialMoment => sersicDensityRadialMoment
procedure :: massEnclosedBySphere => sersicMassEnclosedBySphere
procedure :: massTotal => sersicMassTotal
+ procedure :: potentialIsAnalytic => sersicPotentialIsAnalytic
procedure :: potential => sersicPotential
procedure :: radiusHalfMass => sersicRadiusHalfMass
procedure :: radiusHalfMassProjected => sersicRadiusHalfMassProjected
@@ -199,23 +201,17 @@ function sersicConstructorInternal(index,radiusHalfMass,mass,dimensionless,compo
return
end function sersicConstructorInternal
- double precision function sersicDensity(self,coordinates,componentType,massType)
+ double precision function sersicDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a S\'ersic mass distribution.
!!}
use :: Coordinates , only : assignment(=) , coordinateSpherical
implicit none
- class (massDistributionSersic ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision :: r
+ class (massDistributionSersic), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: position
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- sersicDensity=0.0d0
- return
- end if
! Get position in spherical coordinate system.
position= coordinates
! Compute the density at this position.
@@ -230,26 +226,20 @@ double precision function sersicDensity(self,coordinates,componentType,massType)
return
end function sersicDensity
- double precision function sersicDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function sersicDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Returns a radial density moment for the S\'ersic mass distribution.
!!}
implicit none
- class (massDistributionSersic ), intent(inout) :: self
- double precision , intent(in ) :: moment
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- integer :: iRadius
- double precision :: deltaRadius , integrand , &
- & previousIntegrand , fractionalRadiusMinimum, &
- & fractionalRadiusMaximum
-
- if (.not.self%matches(componentType,massType)) then
- sersicDensityRadialMoment=0.0d0
- return
- end if
+ class (massDistributionSersic), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ integer :: iRadius
+ double precision :: deltaRadius , integrand , &
+ & previousIntegrand , fractionalRadiusMinimum, &
+ & fractionalRadiusMaximum
+
isInfinite =.false.
sersicDensityRadialMoment=0.0d0
!$ call OMP_Set_Lock(self%tableLock)
@@ -291,38 +281,26 @@ double precision function sersicDensityRadialMoment(self,moment,radiusMinimum,ra
return
end function sersicDensityRadialMoment
- double precision function sersicMassTotal(self,componentType,massType)
+ double precision function sersicMassTotal(self)
!!{
Computes the total mass for S\'ersic mass distributions.
!!}
implicit none
- class(massDistributionSersic ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionSersic), intent(inout) :: self
- if (.not.self%matches(componentType,massType)) then
- sersicMassTotal=0.0d0
- return
- end if
sersicMassTotal=self%mass
return
end function sersicMassTotal
- double precision function sersicMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function sersicMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for S\'ersic mass distributions.
!!}
implicit none
- class (massDistributionSersic ), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: fractionalRadius
+ class (massDistributionSersic), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: fractionalRadius
- if (.not.self%matches(componentType,massType)) then
- sersicMassEnclosedBySphere=0.0d0
- return
- end if
if (radius <= 0.0d0) then
sersicMassEnclosedBySphere=0.0d0
else
@@ -341,37 +319,59 @@ double precision function sersicMassEnclosedBySphere(self,radius,componentType,m
return
end function sersicMassEnclosedBySphere
- double precision function sersicPotential(self,coordinates,componentType,massType)
+ logical function sersicPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSersic), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function sersicPotentialIsAnalytic
+
+ double precision function sersicPotential(self,coordinates,status)
!!{
Return the potential at the specified {\normalfont \ttfamily coordinates} in a S\'ersic mass distribution.
!!}
use :: Coordinates , only : assignment(=) , coordinateSpherical
+ use :: Error , only : Error_Report
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionSersic ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision :: r
+ class (massDistributionSersic ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateSpherical ) :: position
+ double precision :: r
- if (.not.self%matches(componentType,massType)) then
- sersicPotential=0.0d0
- return
- end if
+ if (present(status)) status=structureErrorCodeSuccess
! Get position in spherical coordinate system.
position=coordinates
- ! Compute the potential at this position.
!$ call OMP_Set_Lock(self%tableLock)
- r =+position%r () &
- & /self %radiusHalfMass_
- call self%tabulate(r)
- if (r < self%tableRadius(self%tableCount)) then
- sersicPotential=+self%mass &
- & /self%radiusHalfMass_ &
- & *self%tableInterpolator%interpolate(r,self%tablePotential)
+ ! For small radii, use a simple power-law extrapolation.
+ if (position%r() < self%tableRadiusMinimum) then
+ if (position%r() <= 0.0d0 .and. self%gradientLogarithmicMassCentral <= 1.0d0) call Error_Report('potential is divergent at r=0'//{introspection:location})
+ sersicPotential=+self%tablePotential (1) &
+ & -self%tableEnclosedMass(1) &
+ & /self%tableRadius (1) &
+ & *( &
+ & +1.0d0 &
+ & -(position%r()/self%tableRadius(1))**(self%gradientLogarithmicMassCentral-1.0d0) &
+ & ) &
+ & / (self%gradientLogarithmicMassCentral-1.0d0)
else
- sersicPotential=0.0d0
+ ! Compute the potential at this position.
+ r =+position%r () &
+ & /self %radiusHalfMass_
+ call self%tabulate(r)
+ if (r < self%tableRadius(self%tableCount)) then
+ sersicPotential=+self%mass &
+ & /self%radiusHalfMass_ &
+ & *self%tableInterpolator%interpolate(r,self%tablePotential)
+ else
+ sersicPotential=0.0d0
+ end if
end if
!$ call OMP_Unset_Lock(self%tableLock)
if (.not.self%isDimensionless()) sersicPotential=+gravitationalConstantGalacticus &
@@ -379,38 +379,26 @@ double precision function sersicPotential(self,coordinates,componentType,massTyp
return
end function sersicPotential
- double precision function sersicRadiusHalfMass(self,componentType,massType)
+ double precision function sersicRadiusHalfMass(self)
!!{
Return the half-mass radius of a S\'ersic mass distribution.
!!}
implicit none
- class(massDistributionSersic ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionSersic), intent(inout) :: self
- if (.not.self%matches(componentType,massType)) then
- sersicRadiusHalfMass=0.0d0
- return
- end if
!$ call OMP_Set_Lock(self%tableLock)
sersicRadiusHalfMass=+self%radiusHalfMass_
!$ call OMP_Unset_Lock(self%tableLock)
return
end function sersicRadiusHalfMass
- double precision function sersicRadiusHalfMassProjected(self,componentType,massType)
+ double precision function sersicRadiusHalfMassProjected(self)
!!{
Return the half-mass radius in projection of a S\'ersic mass distribution.
!!}
implicit none
- class(massDistributionSersic ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionSersic), intent(inout) :: self
- if (.not.self%matches(componentType,massType)) then
- sersicRadiusHalfMassProjected=0.0d0
- return
- end if
sersicRadiusHalfMassProjected=+self%radiusHalfMass_ &
& *self%table2dRadiusHalfMass
return
@@ -418,7 +406,7 @@ end function sersicRadiusHalfMassProjected
subroutine sersicTabulate(self,radius)
!!{
- Tabulate the density and enclosed mass in a dimensionless S\'ersic profile.
+ Tabulate the density enclosed mass, and potential in a dimensionless S\'ersic profile.
!!}
use :: Numerical_Constants_Math, only : Pi
use :: Numerical_Integration , only : integrator
@@ -554,6 +542,9 @@ subroutine sersicTabulate(self,radius)
& .and. &
& (radiusActual <= self%tableRadiusMaximum)
end do
+ ! Determine the central slope of the mass profile.
+ self%gradientLogarithmicMassCentral=+log(self%tableEnclosedMass(2)/self%tableEnclosedMass(1)) &
+ & /log(self%tableRadius (2)/self%tableRadius (1))
! Build the interpolator.
self%tableInterpolator=interpolator(self%tableRadius,extrapolationType=extrapolationTypeExtrapolate)
! Flag that the table is initialized.
diff --git a/source/mass_distributions.spherical.Zhao1996.F90 b/source/mass_distributions.spherical.Zhao1996.F90
new file mode 100644
index 0000000000..7972b988c8
--- /dev/null
+++ b/source/mass_distributions.spherical.Zhao1996.F90
@@ -0,0 +1,1558 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of the \cite{zhao_analytical_1996} mass distribution class.
+ !!}
+
+ use :: Numerical_Interpolation , only : interpolator
+
+ !![
+
+ specialCase
+ Special cases for {\normalfont \ttfamily zhao1996} dark matter halo profile class.
+
+
+
+
+
+
+ !!]
+
+ !![
+
+
+ A mass distribution class which implements the \citep{zhao_analytical_1996} density profile:
+ \begin{equation}
+ \rho_\mathrm{dark matter}(r) = \rho_0 \left({r\over r_\mathrm{s}}\right)^{-\gamma} \left(1+[{r\over r_\mathrm{s}}]^\alpha\right)^{-(\beta-\gamma)/\alpha}.
+ \end{equation}
+ The mass enclosed within radius $r$ is given by
+ \begin{equation}
+ M(<r) = \frac{4 \pi}{3-\gamma} \rho_0 r_\mathrm{s}^{3-\gamma} {}_2F_1\left[\left(\frac{3-\gamma}{\alpha}\right),\left(\frac{-\beta+\gamma}{\alpha},1+\frac{3-\gamma}{\alpha}\right),-r^\alpha\right]
+ \end{equation}
+ where $R=r/r_\mathrm{s}$. The associated gravitational potential is
+ \begin{equation}
+ \Phi(r) = - \frac{4 \pi \mathrm{G}}{-3+\gamma} \rho_0 r^{2-\gamma} \frac{\Gamma[(3+\alpha-\gamma)/\alpha]}{\Gamma[(3-\gamma)/\alpha]} \left( \Gamma\left[\frac{2-\gamma}{\alpha}\right] {}_p\tilde{F}F_q\left[\left\{\frac{2-\gamma}{\alpha},\frac{\beta-\gamma}{\alpha}\right\},\left\{\frac{2+\alpha-\gamma}{\alpha}\right\},-r\alpha\right] - \Gamma\left[\frac{3-\gamma}{\alpha}\right] {}_p\tilde{F}F_q\left[\left\{\frac{3-\gamma}{\alpha},\frac{\beta-\gamma}{\alpha}\right\},\left\{\frac{3+\alpha-\gamma}{\alpha}\right\},-r\alpha\right] \right).
+ \end{equation}
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionZhao1996
+ !!{
+ The \citep{zhao_analytical_1996} mass distribution.
+ !!}
+ private
+ type (enumerationSpecialCaseType) :: specialCase
+ double precision :: densityNormalization , scaleLength , &
+ & alpha , beta , &
+ & gamma
+ double precision :: densityScaleFreeRadiusMinimum , densityScaleFreeRadiusMaximum
+ double precision :: densityScaleFreeMinimum , densityScaleFreeMaximum
+ type (interpolator ), allocatable :: densityScaleFree_
+ double precision :: massScaleFreeRadiusMinimum , massScaleFreeRadiusMaximum
+ double precision :: massScaleFreeMinimum , massScaleFreeMaximum
+ type (interpolator ), allocatable :: massScaleFree_
+ double precision :: angularMomentumSpecificScaleFreeRadiusMinimum, angularMomentumSpecificScaleFreeRadiusMaximum
+ double precision :: angularMomentumSpecificScaleFreeMinimum , angularMomentumSpecificScaleFreeMaximum
+ type (interpolator ), allocatable :: angularMomentumSpecificScaleFree_
+ double precision :: timeFreefallScaleFreeRadiusMinimum , timeFreefallScaleFreeRadiusMaximum
+ double precision :: timeFreefallScaleFreeMinimum , timeFreefallScaleFreeMaximum
+ type (interpolator ), allocatable :: timeFreefallScaleFree_
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: massTotal => zhao1996MassTotal
+ procedure :: density => zhao1996Density
+ procedure :: densityGradientRadial => zhao1996DensityGradientRadial
+ procedure :: densityRadialMoment => zhao1996DensityRadialMoment
+ procedure :: massEnclosedBySphere => zhao1996MassEnclosedBySphere
+ procedure :: velocityRotationCurveMaximum => zhao1996VelocityRotationCurveMaximum
+ procedure :: radiusRotationCurveMaximum => zhao1996RadiusRotationCurveMaximum
+ procedure :: radiusEnclosingMass => zhao1996RadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => zhao1996RadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => zhao1996RadiusFromSpecificAngularMomentum
+ procedure :: radiusFreefall => zhao1996RadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => zhao1996RadiusFreefallIncreaseRate
+ procedure :: timeFreefallTabulate => zhao1996TimeFreefallTabulate
+ procedure :: potentialIsAnalytic => zhao1996PotentialIsAnalytic
+ procedure :: potential => zhao1996Potential
+ procedure :: fourierTransform => zhao1996FourierTransform
+ procedure :: energyPotential => zhao1996EnergyPotential
+ procedure :: energyKinetic => zhao1996EnergyKinetic
+ procedure :: descriptor => zhao1996Descriptor
+ end type massDistributionZhao1996
+
+ interface massDistributionZhao1996
+ !!{
+ Constructors for the {\normalfont \ttfamily zhao1996} mass distribution class.
+ !!}
+ module procedure massDistributionZhao1996ConstructorParameters
+ module procedure massDistributionZhao1996ConstructorInternal
+ end interface massDistributionZhao1996
+
+ class(massDistributionZhao1996), pointer :: self_
+ !$omp threadprivate(self_)
+
+ ! The minimum (scale-free) freefall timescale in a cored NFW profile.
+ double precision , parameter :: timeFreefallScaleFreeMinimumCoredNFW=sqrt(3.0d0*Pi)/4.0d0
+
+contains
+
+ function massDistributionZhao1996ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily zhao1996} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Hypergeometric_Functions , only : Hypergeometric_2F1
+ implicit none
+ type (massDistributionZhao1996) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: mass , scaleLength, &
+ & densityNormalization, radiusOuter, &
+ & alpha , beta , &
+ & gamma
+ logical :: dimensionless
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ alpha
+ The parameter $\alpha$ of the Zhao1996 profile.
+
+
+
+ beta
+ The parameter $\beta$ of the Zhao1996 profile.
+
+
+
+ gamma
+ The parameter $\gamma$ of the Zhao1996 profile.
+
+
+
+ densityNormalization
+ (3.0d0-gamma)/4.0d0/Pi/Hypergeometric_2F1([(3.0d0-gamma)/alpha,(beta-gamma)/alpha],[1.0d0+(3.0d0-gamma)/alpha],-1.0d0)
+ The density normalization of the Zhao1996 profile.
+
+
+
+ scaleLength
+ 1.0d0
+ The scale radius of the Zhao1996 profile.
+
+
+
+ mass
+ 1.0d0
+ The mass of the Zhao1996 profile.
+
+
+
+ radiusOuter
+ The outer radius of the Zhao1996 profile.
+
+
+
+ dimensionless
+ .true.
+ If true the Zhao1996 profile is considered to be dimensionless.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ self=massDistributionZhao1996(alpha=alpha,beta=beta,gamma=gamma,scaleLength=scaleLength,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+
+
+ !!]
+ return
+ end function massDistributionZhao1996ConstructorParameters
+
+ function massDistributionZhao1996ConstructorInternal(alpha,beta,gamma,scaleLength,densityNormalization,mass,radiusOuter,dimensionless,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``zhao1996'' mass distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ use :: Numerical_Comparison , only : Values_Agree
+ implicit none
+ type (massDistributionZhao1996 ) :: self
+ double precision , intent(in ) :: alpha , beta , &
+ & gamma , scaleLength
+ double precision , intent(in ), optional :: densityNormalization, radiusOuter, &
+ & mass
+ logical , intent(in ), optional :: dimensionless
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ ! Determine density normalization.
+ if ( &
+ & present(densityNormalization) &
+ & ) then
+ self%densityNormalization=densityNormalization
+ else if ( &
+ & present(mass ).and. &
+ & present(radiusOuter ) &
+ & ) then
+ radiusScaleFree =+radiusOuter/self%scaleLength
+ self%densityNormalization=+mass/self%scaleLength**3*(3.0d0-gamma)/4.0d0/Pi/radiusScaleFree**(3.0d0-gamma)/Hypergeometric_2F1([(3.0d0-gamma)/alpha,(beta-gamma)/alpha],[1.0d0+(3.0d0-gamma)/alpha],-radiusScaleFree**alpha)
+ else
+ call Error_Report('either "densityNormalization", or "mass" and "radiusOuter" must be specified'//{introspection:location})
+ end if
+ ! Determine if profile is dimensionless.
+ if (present(dimensionless )) then
+ self%dimensionless=dimensionless
+ else
+ self%dimensionless=.false.
+ end if
+ ! Validate parameters.
+ if (gamma >= 3.0d0) call Error_Report('γ ≥ 3 gives divergent mass as r → 0'//{introspection:location})
+ ! Detect special cases.
+ if ( &
+ & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(gamma,1.0d0,absTol=1.0d-6) &
+ & ) then
+ ! The "NFW" profile.
+ self%specialCase=specialCaseNFW
+ else if ( &
+ & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(gamma,0.0d0,absTol=1.0d-6) &
+ & ) then
+ ! The "cored NFW" profile.
+ self%specialCase=specialCaseCoredNFW
+ else if ( &
+ & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(gamma,0.5d0,absTol=1.0d-6) &
+ & ) then
+ ! The "γ=1/2 NFW" profile.
+ self%specialCase=specialCaseGamma0_5NFW
+ else if ( &
+ & Values_Agree(alpha,1.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(beta ,3.0d0,absTol=1.0d-6) &
+ & .and. &
+ & Values_Agree(gamma,1.5d0,absTol=1.0d-6) &
+ & ) then
+ ! The "γ=3/2 NFW" profile.
+ self%specialCase=specialCaseGamma1_5NFW
+ else
+ ! Use general solutions.
+ self%specialCase=specialCaseGeneral
+ end if
+ ! Initialize memoized results.
+ self%densityScaleFreeMinimum =+huge(0.0d0)
+ self%densityScaleFreeMaximum =-huge(0.0d0)
+ self%densityScaleFreeRadiusMinimum =+1.0d0
+ self%densityScaleFreeRadiusMaximum =+1.0d0
+ self%massScaleFreeMinimum =+huge(0.0d0)
+ self%massScaleFreeMaximum =-huge(0.0d0)
+ self%massScaleFreeRadiusMinimum =+1.0d0
+ self%massScaleFreeRadiusMaximum =+1.0d0
+ self%angularMomentumSpecificScaleFreeMinimum =+huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeMaximum =-huge(0.0d0)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=+1.0d0
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=+1.0d0
+ self%timeFreefallScaleFreeMinimum =+huge(0.0d0)
+ self%timeFreefallScaleFreeMaximum =-huge(0.0d0)
+ self%timeFreefallScaleFreeRadiusMinimum =+1.0d0
+ self%timeFreefallScaleFreeRadiusMaximum =+1.0d0
+ return
+ end function massDistributionZhao1996ConstructorInternal
+
+ double precision function zhao1996MassTotal(self) result(massTotal)
+ !!{
+ Return the total mass in an Zhao1996 mass distribution.
+ !!}
+ use :: Gamma_Functions, only : Gamma_Function
+ implicit none
+ class(massDistributionZhao1996), intent(inout) :: self
+
+ if (self%beta <= 3.0d0) then
+ massTotal=+huge(0.0d0)
+ else
+ massTotal=+ 4.0d0 &
+ & /(3.0d0-self%gamma) &
+ & *Gamma_Function((-3.0d0 +self%beta )/self%alpha) &
+ & *Gamma_Function((+3.0d0+self%alpha -self%gamma)/self%alpha) &
+ & /Gamma_Function(( -self%alpha+self%beta )/self%alpha) &
+ & *self%densityNormalization &
+ & *self%scaleLength **3
+ end if
+ return
+ end function zhao1996MassTotal
+
+ double precision function zhao1996Density(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a Zhao1996 mass distribution.
+ !!}
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radiusScaleFree
+
+ ! Compute the density at this position.
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %scaleLength
+ select case (self%specialCase%ID)
+ case (specialCaseGeneral%ID)
+ density=+self%densityNormalization &
+ & / radiusScaleFree**self%gamma &
+ & /( &
+ & +1.0d0 &
+ & +radiusScaleFree**self%alpha &
+ & )**((self%beta-self%gamma)/self%alpha)
+ case (specialCaseNFW%ID)
+ density=+self%densityNormalization &
+ & / radiusScaleFree &
+ & /( &
+ & +1.0d0 &
+ & +radiusScaleFree &
+ & )**2
+ case (specialCaseCoredNFW%ID)
+ density=+self%densityNormalization &
+ & /( &
+ & +1.0d0 &
+ & +radiusScaleFree &
+ & )**3
+ case (specialCaseGamma0_5NFW%ID)
+ density=+self%densityNormalization &
+ & /sqrt(radiusScaleFree) &
+ & /( &
+ & +1.0d0 &
+ & +radiusScaleFree &
+ & )**2.5d0
+ case (specialCaseGamma1_5NFW%ID)
+ density=+self%densityNormalization &
+ & / radiusScaleFree**1.5d0 &
+ & /( &
+ & +1.0d0 &
+ & +radiusScaleFree &
+ & )**1.5d0
+ case default
+ density=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ return
+ end function zhao1996Density
+
+ double precision function zhao1996DensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an Zhao1996 \citep{zhao_analytical_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionZhao1996), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ radiusScaleFree =+coordinates%rSpherical() &
+ & /self %scaleLength
+ densityGradientRadial=-( &
+ & +self%beta *radiusScaleFree**self%alpha &
+ & +self%gamma &
+ & ) &
+ & /( &
+ & +1.0d0 &
+ & + radiusScaleFree**self%alpha &
+ & )
+ if (.not.logarithmic_) densityGradientRadial=+ densityGradientRadial &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ return
+ end function zhao1996DensityGradientRadial
+
+ double precision function zhao1996DensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Computes radial moments of the density in an Zhao1996 \citep{zhao_analytical_1996} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: radialMomentMinimum, radialMomentMaximum, &
+ & radiusScaleFree
+
+ densityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ if (present(radiusMinimum)) then
+ radiusScaleFree =+ radiusMinimum &
+ & /self%scaleLength
+ radialMomentMinimum=+radialMomentIndefinite(radiusScaleFree)
+ else
+ radialMomentMinimum=+0.0d0
+ if ( &
+ & 0.0d0 >= self%alpha &
+ & .or. &
+ & 1.0d0+moment <= self%gamma &
+ & ) call Error_Report('radial moment is undefined'//{introspection:location})
+ end if
+ if (present(radiusMaximum)) then
+ radiusScaleFree =+ radiusMaximum &
+ & /self%scaleLength
+ radialMomentMaximum=+radialMomentIndefinite(radiusScaleFree)
+ else
+ radialMomentMaximum=+0.0d0
+ if ( &
+ & self%alpha > 0.0d0 &
+ & .and. &
+ & 1.0d0+ moment > self%alpha+self%beta &
+ & .and. &
+ & 1.0d0+ moment > self%gamma &
+ & .and. &
+ & self%beta > self%gamma &
+ & ) call Error_Report('radial moment is undefined'//{introspection:location})
+ end if
+ densityRadialMoment=+( &
+ & +radialMomentMaximum &
+ & -radialMomentMinimum &
+ & ) &
+ & *self%densityNormalization &
+ & *self%scaleLength **(moment+1.0d0)
+ return
+
+ contains
+
+ double precision function radialMomentIndefinite(radiusScaleFree)
+ !!{
+ Compute the indefinite radial moment.
+ !!}
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ implicit none
+ double precision, intent(in ) :: radiusScaleFree
+
+ radialMomentIndefinite=+radiusScaleFree** (1.0d0+moment-self%gamma) &
+ & / (1.0d0+moment-self%gamma) &
+ & *Hypergeometric_2F1([(1.0d0+moment-self%gamma)/self%alpha,(self%beta-self%gamma)/self%alpha],[1.0d0+(1.0d0+moment-self%gamma)/self%alpha],-radiusScaleFree**self%alpha)
+ return
+ end function radialMomentIndefinite
+
+ end function zhao1996DensityRadialMoment
+
+ double precision function zhao1996MassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for zhao1996 mass distributions.
+ !!}
+ implicit none
+ class (massDistributionZhao1996), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radiusScaleFree
+
+ self_ => self
+ radiusScaleFree = + radius &
+ & /self%scaleLength
+ mass = +self%densityNormalization &
+ & *self%scaleLength **3 &
+ & *massEnclosedScaleFree(radiusScaleFree)
+ return
+ end function zhao1996MassEnclosedBySphere
+
+ double precision function zhao1996VelocityRotationCurveMaximum(self) result(velocity)
+ !!{
+ Return the peak velocity in the rotation curve for a Zhao1996 mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class(massDistributionZhao1996), intent(inout) :: self
+
+ select case (self%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ velocity=+self%rotationCurve(self%radiusRotationCurveMaximum())
+ return
+ case (specialCaseNFW %ID)
+ velocity=+1.6483500453640064578d0
+ case (specialCaseGamma0_5NFW%ID)
+ velocity=+1.4026527358517898624d0
+ case (specialCaseGamma1_5NFW%ID)
+ velocity=+2.0932014912026087087d0
+ case (specialCaseCoredNFW %ID)
+ velocity=+1.2414383571567440046d0
+ case default
+ velocity=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ velocity=+velocity &
+ & *sqrt( &
+ & +self%densityNormalization &
+ & ) &
+ & * self%scaleLength
+ if (.not.self%isDimensionless()) &
+ & velocity=+velocity &
+ & *sqrt(gravitationalConstantGalacticus)
+ return
+ end function zhao1996VelocityRotationCurveMaximum
+
+ double precision function zhao1996RadiusRotationCurveMaximum(self) result(radius)
+ !!{
+ Return the peak velocity in the rotation curve for a Zhao1996 mass distribution.
+ !!}
+ implicit none
+ class(massDistributionZhao1996), intent(inout), target :: self
+
+ select case (self%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ radius=+self%radiusRotationCurveMaximumNumerical()
+ return
+ case (specialCaseNFW %ID)
+ radius=+2.1625815870646098349d0
+ case (specialCaseGamma0_5NFW%ID)
+ radius=+3.2892765613841120232d0
+ case (specialCaseGamma1_5NFW%ID)
+ radius=+1.0549665718691230692d0
+ case (specialCaseCoredNFW %ID)
+ radius=+4.4247006468722702269d0
+ case default
+ radius=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ radius=+radius &
+ & *self%scaleLength
+ return
+ end function zhao1996RadiusRotationCurveMaximum
+
+ double precision function zhao1996RadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for zhao1996 mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionZhao1996), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision , allocatable , dimension(:) :: radii , masses
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: massScaleFree , mass_
+ integer :: countRadii
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_=mass
+ else if (present(massFractional)) then
+ call Error_Report('mass is unbounded, so mass fraction is undefined'//{introspection:location})
+ else
+ call Error_Report('either mass or massFractional must be supplied' //{introspection:location})
+ end if
+ massScaleFree=+ mass_ &
+ & /self%densityNormalization &
+ & /self%scaleLength **3
+ if ( &
+ & massScaleFree <= self%massScaleFreeMinimum &
+ & .or. &
+ & massScaleFree > self%massScaleFreeMaximum &
+ & ) then
+ self_ => self
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMinimum) >= massScaleFree)
+ self%massScaleFreeRadiusMinimum=0.5d0*self%massScaleFreeRadiusMinimum
+ end do
+ do while (massEnclosedScaleFree(self%massScaleFreeRadiusMaximum) < massScaleFree)
+ self%massScaleFreeRadiusMaximum=2.0d0*self%massScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%massScaleFreeRadiusMaximum/self%massScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%massScaleFree_)) deallocate(self%massScaleFree_)
+ allocate( radii (countRadii))
+ allocate( masses (countRadii))
+ allocate(self%massScaleFree_ )
+ radii = Make_Range(self%massScaleFreeRadiusMinimum,self%massScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ masses = massEnclosedScaleFree( radii)
+ self%massScaleFreeMinimum = masses ( 1 )
+ self%massScaleFreeMaximum = masses (countRadii )
+ self%massScaleFree_ = interpolator (masses ,radii)
+ end if
+ radius=+self%massScaleFree_%interpolate(massScaleFree) &
+ & *self%scaleLength
+ return
+ end function zhao1996RadiusEnclosingMass
+
+ double precision function zhao1996RadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for zhao1996 mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionZhao1996), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+ double precision , allocatable , dimension(:) :: radii , densities
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: densityScaleFree
+ integer :: countRadii
+
+ densityScaleFree=+density &
+ & /self%densityNormalization
+ if ( &
+ & densityScaleFree <= self%densityScaleFreeMinimum &
+ & .or. &
+ & densityScaleFree > self%densityScaleFreeMaximum &
+ & ) then
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMinimum) < densityScaleFree)
+ self%densityScaleFreeRadiusMinimum=0.5d0*self%densityScaleFreeRadiusMinimum
+ end do
+ do while (densityEnclosedScaleFree(self%densityScaleFreeRadiusMaximum) >= densityScaleFree)
+ self%densityScaleFreeRadiusMaximum=2.0d0*self%densityScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%densityScaleFreeRadiusMaximum/self%densityScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%densityScaleFree_)) deallocate(self%densityScaleFree_)
+ allocate( radii (countRadii))
+ allocate( densities (countRadii))
+ allocate(self%densityScaleFree_ )
+ self_ => self
+ radii = Make_Range(self%densityScaleFreeRadiusMinimum,self%densityScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ densities = -densityEnclosedScaleFree( radii)
+ self%densityScaleFreeMinimum = -densities (countRadii )
+ self%densityScaleFreeMaximum = -densities ( 1 )
+ self%densityScaleFree_ = interpolator (densities ,radii)
+ end if
+ radius=+self%densityScaleFree_%interpolate(-densityScaleFree) &
+ & *self%scaleLength
+ return
+ end function zhao1996RadiusEnclosingDensity
+
+ impure elemental double precision function massEnclosedScaleFree(radius) result(mass)
+ !!{
+ Evaluate the mass enclosed by a given radius in a scale-free Zhao1996 mass distribution.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision, parameter :: radiusTiny=1.0d-3
+
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral%ID)
+ mass =+4.0d0 &
+ & *Pi &
+ & *radius ** (3.0d0-self_%gamma) &
+ & *Hypergeometric_2F1([(3.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[1.0d0+(3.0d0-self_%gamma)/self_%alpha],-radius**self_%alpha) &
+ & / (3.0d0-self_%gamma)
+ case (specialCaseNFW%ID)
+ if (radius < radiusTiny) then
+ ! Use series solution for small radii.
+ mass =+ 2.0d0 *Pi*radius**2 &
+ & - 8.0d0/3.0d0*Pi*radius**3 &
+ & + 3.0d0 *Pi*radius**4 &
+ & -16.0d0/5.0d0*Pi*radius**5 &
+ & +10.0d0/3.0d0*Pi*radius**6 &
+ & -24.0d0/7.0d0*Pi*radius**7
+ else
+ ! Use full solution.
+ mass =+4.0d0 &
+ & *Pi &
+ & *( &
+ & +log(+1.0d0+radius) &
+ & - radius &
+ & / (+1.0d0+radius) &
+ & )
+ end if
+ case (specialCaseGamma0_5NFW%ID)
+ if (radius 0.0d0) then
+ angularMomentumSpecificScaleFree=+angularMomentumSpecific &
+ & /sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & / self%scaleLength **2
+ if ( &
+ & angularMomentumSpecificScaleFree <= self%angularMomentumSpecificScaleFreeMinimum &
+ & .or. &
+ & angularMomentumSpecificScaleFree > self%angularMomentumSpecificScaleFreeMaximum &
+ & ) then
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMinimum) >= angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMinimum=0.5d0*self%angularMomentumSpecificScaleFreeRadiusMinimum
+ end do
+ do while (angularMomentumSpecificEnclosedScaleFree(self%angularMomentumSpecificScaleFreeRadiusMaximum) < angularMomentumSpecificScaleFree)
+ self%angularMomentumSpecificScaleFreeRadiusMaximum=2.0d0*self%angularMomentumSpecificScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%angularMomentumSpecificScaleFreeRadiusMaximum/self%angularMomentumSpecificScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%angularMomentumSpecificScaleFree_)) deallocate(self%angularMomentumSpecificScaleFree_)
+ allocate( radii (countRadii))
+ allocate( angularMomentaSpecific (countRadii))
+ allocate(self%angularMomentumSpecificScaleFree_ )
+ self_ => self
+ radii = Make_Range(self%angularMomentumSpecificScaleFreeRadiusMinimum,self%angularMomentumSpecificScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ angularMomentaSpecific = angularMomentumSpecificEnclosedScaleFree( radii)
+ self%angularMomentumSpecificScaleFreeMinimum = angularMomentaSpecific ( 1 )
+ self%angularMomentumSpecificScaleFreeMaximum = angularMomentaSpecific ( countRadii )
+ self%angularMomentumSpecificScaleFree_ = interpolator (angularMomentaSpecific,radii)
+ end if
+ radius=+self%angularMomentumSpecificScaleFree_%interpolate(angularMomentumSpecificScaleFree) &
+ & *self%scaleLength
+ else
+ radius=+0.0d0
+ end if
+ return
+ end function zhao1996RadiusFromSpecificAngularMomentum
+
+ impure elemental double precision function angularMomentumSpecificEnclosedScaleFree(radius) result(angularMomentumSpecific)
+ !!{
+ Evaluate the specific angular momentum at a given radius in a scale-free Zhao1996 mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ angularMomentumSpecific=+sqrt( &
+ & +massEnclosedScaleFree(radius) &
+ & * radius &
+ & )
+ return
+ end function angularMomentumSpecificEnclosedScaleFree
+
+ logical function zhao1996PotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionZhao1996), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function zhao1996PotentialIsAnalytic
+
+ double precision function zhao1996Potential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an zhao1996 mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionZhao1996 ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision :: radiusScaleFree
+
+ if (present(status)) status=structureErrorCodeSuccess
+ self_ => self
+ radiusScaleFree = +coordinates%rSpherical () &
+ & /self %scaleLength
+ potential=+potentialScaleFree (radiusScaleFree) &
+ & *self%densityNormalization &
+ & *self%scaleLength **2
+ if (.not.self%isDimensionless()) potential=+gravitationalConstantGalacticus &
+ & *potential
+ return
+ end function zhao1996Potential
+
+ impure elemental double precision function potentialScaleFree(radius) result(potential)
+ !!{
+ Compute the potential in a scale-free Zhao1996 mass distribution.
+ !!}
+ use :: Gamma_Functions , only : Gamma_Function
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1_Regularized
+ implicit none
+ double precision, intent(in ) :: radius
+
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ potential=+4.0d0 &
+ & *Pi &
+ & *radius **(2.0d0 -self_%gamma) &
+ & / (3.0d0 -self_%gamma) &
+ & * Gamma_Function((3.0d0+self_%alpha-self_%gamma)/self_%alpha) &
+ & / Gamma_Function((3.0d0 -self_%gamma)/self_%alpha) &
+ & *( &
+ & +Gamma_Function((2.0d0 -self_%gamma)/self_%alpha)*Hypergeometric_2F1_Regularized([(2.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(2.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius**self_%alpha) &
+ & -Gamma_Function((3.0d0 -self_%gamma)/self_%alpha)*Hypergeometric_2F1_Regularized([(3.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(3.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius**self_%alpha) &
+ & )
+ case (specialCaseNFW %ID)
+ potential=+4.0d0 &
+ & *Pi &
+ & *( &
+ & + 1.0d0 &
+ & -log(+1.0d0+radius) &
+ & / radius &
+ & )
+ case (specialCaseGamma0_5NFW%ID)
+ potential=+8.0d0 &
+ & *Pi &
+ & *( &
+ & + (+3.0d0+radius) &
+ & / 3.0d0 &
+ & / sqrt(radius*(+1.0d0+radius)) &
+ & -asinh(sqrt(radius )) &
+ & / radius &
+ & )
+ case (specialCaseGamma1_5NFW%ID)
+ potential=+8.0d0 &
+ & *Pi &
+ & *( &
+ & + sqrt(radius*(1.0d0+radius)) &
+ & -asinh(sqrt(radius )) &
+ & ) &
+ & /radius
+ case (specialCaseCoredNFW %ID)
+ potential=+2.0d0 &
+ & *Pi &
+ & *( &
+ & + (+2.0d0+radius) &
+ & / (+1.0d0+radius) &
+ & - 2.0d0 &
+ & *log(+1.0d0+radius) &
+ & / radius &
+ & )
+ case default
+ potential=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ return
+ end function potentialScaleFree
+
+ double precision function potentialDifferenceScaleFree(radius1,radius2) result(potential)
+ !!{
+ Compute the potential difference in a scale-free Zhao1996 mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Comparison , only : Values_Agree
+ use :: Hypergeometric_Functions, only : Hypergeometric_2F1
+ implicit none
+ double precision, intent(in ) :: radius1 , radius2
+ double precision, parameter :: radiusSmall =1.0d-3
+ double precision, parameter :: toleranceRelative =1.0d-3
+ double precision :: potentialGradientLogarithmic , radiusDifferenceLogarithmic
+
+ if (Values_Agree(radius1,radius2,relTol=toleranceRelative) .or. max(radius1,radius2) < radiusSmall) then
+ if (radius1 < radiusSmall) then
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ potentialGradientLogarithmic=- 1.0d0 &
+ & /( &
+ & -1.0d0 &
+ & + (3.0d0-self_%gamma) &
+ & / (2.0d0-self_%gamma) &
+ & *Hypergeometric_2F1([(2.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(2.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius1**self_%alpha) &
+ & /Hypergeometric_2F1([(3.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(3.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius1**self_%alpha) &
+ & )
+ case (specialCaseNFW %ID)
+ potentialGradientLogarithmic=+1.0d0- 2.0d0*radius1/ 3.0d0+ 5.0d0*radius1**2/ 9.0d0- 67.0d0*radius1**3/ 135.0d0+ 371.0d0*radius1**4/ 810.0d0
+ case (specialCaseGamma0_5NFW%ID)
+ potentialGradientLogarithmic=+1.5d0-15.0d0*radius1/14.0d0+275.0d0*radius1**2/294.0d0-6525.0d0*radius1**3/7546.0d0+5067175.0d0*radius1**4/6180174.0d0
+ case (specialCaseGamma1_5NFW%ID)
+ potentialGradientLogarithmic=+0.5d0- 3.0d0*radius1/10.0d0+ 81.0d0*radius1**2/350.0d0- 341.0d0*radius1**3/1750.0d0+ 115477.0d0*radius1**4/ 673750.0d0
+ case (specialCaseCoredNFW %ID)
+ potentialGradientLogarithmic=+2.0d0- 3.0d0*radius1/ 2.0d0+ 27.0d0*radius1**2/ 20.0d0- 51.0d0*radius1**3/ 40.0d0+ 3441.0d0*radius1**4/ 2800.0d0
+ case default
+ potentialGradientLogarithmic=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ else
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ potentialGradientLogarithmic=+ 1.0d0 &
+ & /( &
+ & -1.0d0 &
+ & + (3.0d0-self_%gamma) &
+ & / (2.0d0-self_%gamma) &
+ & *Hypergeometric_2F1([(2.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(2.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius1**self_%alpha) &
+ & /Hypergeometric_2F1([(3.0d0-self_%gamma)/self_%alpha,(self_%beta-self_%gamma)/self_%alpha],[(3.0d0+self_%alpha-self_%gamma)/self_%alpha],-radius1**self_%alpha) &
+ & )
+ case (specialCaseNFW %ID)
+ potentialGradientLogarithmic=+ 1.0d0 &
+ & /( &
+ & -1.0d0 &
+ & + radius1 **2 &
+ & /( &
+ & - radius1 &
+ & + (1.0d0+radius1) &
+ & *log(1.0d0+radius1) &
+ & ) &
+ & )
+ case (specialCaseGamma0_5NFW%ID)
+ potentialGradientLogarithmic=+( &
+ & - (3.0d0+4.0d0*radius1) * sqrt(radius1) *sqrt(1.0d0+radius1) &
+ & + 3.0d0*(1.0d0+ radius1)**2*asinh(sqrt(radius1)) &
+ & ) &
+ & /( &
+ & + (1.0d0+ radius1) &
+ & *( &
+ & + (3.0d0+ radius1) * sqrt(radius1) *sqrt(1.0d0+radius1) &
+ & -3.0d0*(1.0d0+ radius1) *asinh(sqrt(radius1)) &
+ & ) &
+ & )
+ case (specialCaseGamma1_5NFW%ID)
+ potentialGradientLogarithmic=-1.0d0 &
+ & /( &
+ & +1.0d0 &
+ & +1.0d0 &
+ & /( &
+ & +1.0d0 &
+ & / radius1 &
+ & - sqrt(1.0d0+radius1 ) &
+ & *asinh(sqrt( radius1 )) &
+ & / radius1**1.5d0 &
+ & ) &
+ & )
+ case (specialCaseCoredNFW %ID)
+ potentialGradientLogarithmic=+1.0d0 &
+ & /( &
+ & - 1.0d0 &
+ & + radius1**3 &
+ & /( &
+ & -radius1* (2.0d0+3.0d0*radius1) &
+ & +2.0d0 * (1.0d0+ radius1)**2 &
+ & * log(1.0d0+ radius1) &
+ & ) &
+ & )
+ case default
+ potentialGradientLogarithmic=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ end if
+ radiusDifferenceLogarithmic=+1.0d0 &
+ & -radius2 &
+ & /radius1
+ potential =+potentialScaleFree (radius1) &
+ & *potentialGradientLogarithmic &
+ & *radiusDifferenceLogarithmic
+ else
+ potential=+potentialScaleFree(radius1) &
+ & -potentialScaleFree(radius2)
+ end if
+ return
+ end function potentialDifferenceScaleFree
+
+ double precision function zhao1996RadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in an Zhao1996 mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (self%specialCase == specialCaseCoredNFW .and. timeScaleFree <= timeFreefallScaleFreeMinimumCoredNFW) then
+ radius=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radius=+self%timeFreefallScaleFree_%interpolate(timeScaleFree) &
+ & *self%scaleLength
+ return
+ end function zhao1996RadiusFreefall
+
+ double precision function zhao1996RadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an zhao1996 mass
+ distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr, gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: time
+ double precision :: timeScaleFree, timeScale
+
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
+ timeScaleFree=+time &
+ & /timeScale
+ if (self%specialCase == specialCaseCoredNFW .and. timeScaleFree <= timeFreefallScaleFreeMinimumCoredNFW) then
+ radiusIncreaseRate=0.0d0
+ return
+ end if
+ call self%timeFreefallTabulate(timeScaleFree)
+ radiusIncreaseRate=+self%timeFreefallScaleFree_%derivative(timeScaleFree) &
+ & *self%scaleLength &
+ & / timeScale
+ return
+ end function zhao1996RadiusFreefallIncreaseRate
+
+ subroutine zhao1996TimeFreefallTabulate(self,timeScaleFree)
+ !!{
+ Tabulate the freefall radius at the given {\normalfont \ttfamily time} in an Zhao1996 mass distribution.
+ !!}
+ use :: Numerical_Integration, only : integrator
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionZhao1996), intent(inout), target :: self
+ double precision , intent(in ) :: timeScaleFree
+ double precision , allocatable , dimension(:) :: radii , timesFreefall
+ double precision , parameter :: countRadiiPerDecade=100.0d0
+ double precision :: radiusStart
+ integer :: countRadii , i
+ type (integrator ) :: integrator_
+
+ if ( &
+ & timeScaleFree <= self%timeFreefallScaleFreeMinimum &
+ & .or. &
+ & timeScaleFree > self%timeFreefallScaleFreeMaximum &
+ & ) then
+ self_ => self
+ integrator_ = integrator(timeFreeFallIntegrand,toleranceRelative=1.0d-6)
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMinimum) >= timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMinimum=0.5d0*self%timeFreefallScaleFreeRadiusMinimum
+ end do
+ do while (timeFreefallScaleFree(self%timeFreefallScaleFreeRadiusMaximum) < timeScaleFree)
+ self%timeFreefallScaleFreeRadiusMaximum=2.0d0*self%timeFreefallScaleFreeRadiusMaximum
+ end do
+ countRadii=int(log10(self%timeFreefallScaleFreeRadiusMaximum/self%timeFreefallScaleFreeRadiusMinimum)*countRadiiPerDecade)+1
+ if (allocated(self%timeFreefallScaleFree_)) deallocate(self%timeFreefallScaleFree_)
+ allocate( radii (countRadii))
+ allocate( timesFreefall (countRadii))
+ allocate(self%timeFreefallScaleFree_ )
+ radii=Make_Range(self%timeFreefallScaleFreeRadiusMinimum,self%timeFreefallScaleFreeRadiusMaximum,countRadii,rangeTypeLogarithmic)
+ do i=1,countRadii
+ timesFreefall(i)=timeFreefallScaleFree(radii(i))
+ end do
+ self%timeFreefallScaleFreeMinimum=timesFreefall( 1 )
+ self%timeFreefallScaleFreeMaximum=timesFreefall( countRadii )
+ self%timeFreefallScaleFree_ =interpolator (timesFreefall,radii)
+ end if
+ return
+
+ contains
+
+ double precision function timeFreefallScaleFree(radius)
+ !!{
+ Evaluate the freefall time from a given radius in a scale-free Zhao1996 mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ radiusStart = radius
+ timeFreefallScaleFree=integrator_%integrate(0.0d0,radius)
+ return
+ end function timeFreefallScaleFree
+
+ double precision function timeFreeFallIntegrand(radius)
+ !!{
+ Integrand used to find the freefall time in a scale-free Zhao1996 mass distribution.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+ double precision :: potentialDifference
+
+ if (radius == 0.0d0) then
+ timeFreeFallIntegrand=+0.0d0
+ else
+ potentialDifference=+potentialDifferenceScaleFree(radiusStart,radius)
+ if (potentialDifference > 0.0d0) then
+ timeFreeFallIntegrand=+1.0d0 &
+ & /sqrt( &
+ & +2.0d0 &
+ & *potentialDifference &
+ & )
+ else
+ timeFreeFallIntegrand=+0.0d0
+ end if
+ end if
+ return
+ end function timeFreeFallIntegrand
+
+ end subroutine zhao1996TimeFreefallTabulate
+
+ double precision function zhao1996FourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in an Zhao1996 mass
+ distribution.
+ !!}
+ use :: Exponential_Integrals , only : Exponential_Integral
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter , wavenumber
+ double precision :: wavenumberScaleFree, radiusOuterScaleFree
+
+ waveNumberScaleFree =+waveNumber *self%scaleLength
+ radiusOuterScaleFree=+radiusOuter/self%scaleLength
+ select case (self%specialCase%ID)
+ case ( &
+ & specialCaseGeneral %ID, &
+ & specialCaseGamma0_5NFW%ID, &
+ & specialCaseGamma1_5NFW%ID &
+ & )
+ fourierTransform=+self%fourierTransformNumerical(radiusOuter,wavenumber)
+ return
+ case (specialCaseNFW %ID)
+ fourierTransform=+dimag( &
+ & +4.0d0 &
+ & *Pi &
+ & *( &
+ & -exp(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree*(1.0d0+radiusOuterScaleFree)) &
+ & + (1.0d0+radiusOuterScaleFree) &
+ & *( &
+ & +exp(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree) &
+ & - dcmplx(0.0d0,1.0d0)*wavenumberScaleFree &
+ & *( &
+ & +Exponential_Integral(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree ) &
+ & -Exponential_Integral(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & ) &
+ & ) &
+ & /exp(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree) &
+ & /wavenumberScaleFree &
+ & /(1.0d0+radiusOuterScaleFree) &
+ & )
+ case (specialCaseCoredNFW %ID)
+ fourierTransform=+dimag( &
+ & +2.0d0 &
+ & *Pi &
+ & *( &
+ & + 1.0d0 &
+ & - dcmplx(0.0d0,1.0d0)* wavenumberScaleFree &
+ & +( &
+ & + dcmplx(0.0d0,1.0d0)*exp(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree*radiusOuterScaleFree) &
+ & *( &
+ & + dcmplx(0.0d0,1.0d0)+wavenumberScaleFree &
+ & +(dcmplx(0.0d0,2.0d0)+wavenumberScaleFree)*radiusOuterScaleFree &
+ & ) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree)**2 &
+ & -( &
+ & + wavenumberScaleFree &
+ & *(dcmplx(0.0d0,2.0d0)+wavenumberScaleFree) &
+ & *( &
+ & +Exponential_Integral(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree) &
+ & -Exponential_Integral(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & ) &
+ & /exp(dcmplx(0.0d0,1.0d0)*wavenumberScaleFree) &
+ & ) &
+ & /wavenumberScaleFree &
+ & )
+ case default
+ fourierTransform=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ fourierTransform=+fourierTransform &
+ & /massEnclosedScaleFree(radiusOuterScaleFree)
+ return
+ end function zhao1996FourierTransform
+
+ double precision function zhao1996EnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in a Zhao1996 mass distribution.
+ \begin{eqnarray}
+ \end{eqnarray}
+ where $x=r/r_\mathrm{s}$ and $\mathrm{G}$ is Catalan's constant.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ double precision :: radiusOuterScaleFree
+ logical :: analytic
+
+ analytic=.false.
+ radiusOuterScaleFree=+ radiusOuter &
+ & /self%scaleLength
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ analytic=.false.
+ case (specialCaseNFW %ID)
+ analytic=.true.
+ energy =+8.0d0 &
+ & *Pi**2 &
+ & *( &
+ & + radiusOuterScaleFree * (2.0d0+radiusOuterScaleFree) &
+ & -2.0d0*(1.0d0+radiusOuterScaleFree)*log(1.0d0+radiusOuterScaleFree) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree)**2
+ case (specialCaseGamma0_5NFW%ID)
+ analytic=.true.
+ energy =-16.0d0 &
+ & *Pi**2 &
+ & *( &
+ & -12.0d0 &
+ & *(1.0d0+radiusOuterScaleFree) &
+ & *( &
+ & -3.0d0*sqrt(radiusOuterScaleFree )*sqrt(1.0d0+radiusOuterScaleFree) &
+ & -4.0d0* radiusOuterScaleFree**1.5d0 *sqrt(1.0d0+radiusOuterScaleFree) &
+ & +3.0d0*sqrt(radiusOuterScaleFree * (1.0d0+radiusOuterScaleFree)) &
+ & + sqrt(radiusOuterScaleFree**3 * (1.0d0+radiusOuterScaleFree)) &
+ & +radiusOuterScaleFree &
+ & *( &
+ & +3.0d0*sqrt(radiusOuterScaleFree *(1.0d0+radiusOuterScaleFree)) &
+ & + sqrt(radiusOuterScaleFree**3*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & ) &
+ & *asinh(sqrt(radiusOuterScaleFree)) &
+ & +radiusOuterScaleFree &
+ & *( &
+ & +radiusOuterScaleFree &
+ & *( &
+ & -6.0d0 &
+ & +radiusOuterScaleFree*(-3.0d0+5.0d0*radiusOuterScaleFree) &
+ & ) &
+ & +6.0d0*(1.0d0+radiusOuterScaleFree)**3*log(1.0d0+radiusOuterScaleFree) &
+ & ) &
+ & ) &
+ & /9.0d0 &
+ & /radiusOuterScaleFree &
+ & /(1.0d0+radiusOuterScaleFree)**3
+ case (specialCaseGamma1_5NFW%ID)
+ analytic=.true.
+ energy =+32.0d0 &
+ & *Pi**2 &
+ & *( &
+ & -1.0d0 &
+ & +1.0d0 &
+ & /(1.0d0+radiusOuterScaleFree) &
+ & +( &
+ & +2.0d0 &
+ & *( &
+ & -radiusOuterScaleFree**1.5d0/sqrt(1.0d0+radiusOuterScaleFree) &
+ & +sqrt(radiusOuterScaleFree**3*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & *asinh(sqrt(radiusOuterScaleFree)) &
+ & ) &
+ & /radiusOuterScaleFree**2 &
+ & +( &
+ & +( &
+ & +radiusOuterScaleFree**2 &
+ & +radiusOuterScaleFree**3 &
+ & -sqrt(radiusOuterScaleFree *(1.0d0+radiusOuterScaleFree)) &
+ & *sqrt(radiusOuterScaleFree**3*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & *asinh(sqrt(radiusOuterScaleFree))**2 &
+ & ) &
+ & /radiusOuterScaleFree**3 &
+ & /(1.0d0+radiusOuterScaleFree) &
+ & -log(1.0d0+radiusOuterScaleFree) &
+ & )
+ case (specialCaseCoredNFW %ID)
+ analytic=.true.
+ energy =+2.0d0 &
+ & *Pi**2 &
+ & *( &
+ & +radiusOuterScaleFree*(12.0d0+radiusOuterScaleFree*(42.0d0+radiusOuterScaleFree*(40.0d0+7.0d0*radiusOuterScaleFree))) &
+ & -12.0d0*(1.0d0+radiusOuterScaleFree)**2*(1.0d0+2.0d0*radiusOuterScaleFree)*log(1.0d0+radiusOuterScaleFree) &
+ & ) &
+ & /(3.0d0*(1.0d0+radiusOuterScaleFree)**4)
+ case default
+ energy=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ if (analytic) then
+ energy =-energy &
+ & *gravitationalConstantGalacticus &
+ & *self%scaleLength **5 &
+ & *self%densityNormalization **2
+ else
+ energy =+self%energyPotentialNumerical(radiusOuter)
+ end if
+ return
+ end function zhao1996EnergyPotential
+
+ double precision function zhao1996EnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius} in a Zhao1996 mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Dilogarithms , only : Dilogarithm
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ logical :: analytic
+ double precision :: radiusOuterScaleFree
+
+ analytic=.false.
+ select type (massDistributionEmbedding)
+ class is (massDistributionZhao1996)
+ select type (kinematicsDistribution_ => massDistributionEmbedding%kinematicsDistribution_)
+ class is (kinematicsDistributionZhao1996)
+ radiusOuterScaleFree=+ radiusOuter &
+ & /self%scaleLength
+ select case (self_%specialCase%ID)
+ case (specialCaseGeneral %ID)
+ analytic=.false.
+ case (specialCaseNFW %ID)
+ analytic=.true.
+ energy =+4.0d0 &
+ & *Pi**2 &
+ & *( &
+ & +Pi**2 &
+ & *(+1.0d0+radiusOuterScaleFree ) &
+ & *(+2.0d0+radiusOuterScaleFree**3) &
+ & +radiusOuterScaleFree*(2.0d0-radiusOuterScaleFree*(2.0d0+7.0d0*radiusOuterScaleFree)) &
+ & +2.0d0*radiusOuterScaleFree**4*atanh(1.0d0/(1.0d0+2.0d0*radiusOuterScaleFree)) &
+ & -radiusOuterScaleFree**3*log( radiusOuterScaleFree) &
+ & -2.0d0 *log(1.0d0+radiusOuterScaleFree) &
+ & +( &
+ & + radiusOuterScaleFree &
+ & - 3.0d0* radiusOuterScaleFree**2 &
+ & - 5.0d0* radiusOuterScaleFree**3 &
+ & +12.0d0*(1.0d0+radiusOuterScaleFree )*log(radiusOuterScaleFree) &
+ & ) &
+ & *log(1.0d0+radiusOuterScaleFree) &
+ & + 3.0d0*(1.0d0+radiusOuterScaleFree)*(-2.0d0+radiusOuterScaleFree**3)* log( 1.0d0+radiusOuterScaleFree )**2 &
+ & + 6.0d0*(1.0d0+radiusOuterScaleFree)*(+2.0d0+radiusOuterScaleFree**3)*Dilogarithm( -radiusOuterScaleFree ) &
+ & -12.0d0*(1.0d0+radiusOuterScaleFree) *Dilogarithm(1.0d0/(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree)
+ case (specialCaseGamma0_5NFW%ID)
+ analytic=.true.
+ energy =+16.0d0 &
+ & /3.0d0 &
+ & *Pi**2 &
+ & *( &
+ & -4.0d0*radiusOuterScaleFree**1.5d0 &
+ & *(-1.0d0+4.0d0*radiusOuterScaleFree+8.0d0*radiusOuterScaleFree**2) &
+ & *asinh(sqrt( radiusOuterScaleFree)) &
+ & / sqrt(1.0d0+radiusOuterScaleFree) &
+ & +radiusOuterScaleFree &
+ & *( &
+ & + 2.0d0+ radiusOuterScaleFree &
+ & *( &
+ & -5.0d0+2.0d0*radiusOuterScaleFree &
+ & *( &
+ & -11.0d0 &
+ & +32.0d0*log(2.0d0) &
+ & + 8.0d0*radiusOuterScaleFree*(-1.0d0+radiusOuterScaleFree*log(16.0d0)+log(256.0d0)) &
+ & ) &
+ & ) &
+ & ) &
+ & / 2.0d0 &
+ & /(+1.0d0+ radiusOuterScaleFree )**2 &
+ & +(-1.0d0+16.0d0*radiusOuterScaleFree**3) &
+ & *log(1.0d0+radiusOuterScaleFree) &
+ & )
+ case (specialCaseGamma1_5NFW%ID)
+ analytic=.true.
+ energy =-16.0d0 &
+ & /5.0d0 &
+ & *Pi**2 &
+ & *( &
+ & -4.0d0 &
+ & *( &
+ & +3.0d0*sqrt(radiusOuterScaleFree *(1.0d0+radiusOuterScaleFree)) &
+ & -4.0d0*sqrt(radiusOuterScaleFree**3*(1.0d0+radiusOuterScaleFree)) &
+ & +8.0d0*sqrt(radiusOuterScaleFree**5*(1.0d0+radiusOuterScaleFree)) &
+ & ) &
+ & *asinh(sqrt(radiusOuterScaleFree)) &
+ & +radiusOuterScaleFree*(7.0d0+4.0d0*radiusOuterScaleFree*(-3.0d0+8.0d0*radiusOuterScaleFree*log(2.0d0))) &
+ & -4.0d0 *radiusOuterScaleFree**3 *log(radiusOuterScaleFree) &
+ & +5.0d0*(1.0d0+4.0d0*radiusOuterScaleFree**3) &
+ & *log(1.0d0+radiusOuterScaleFree) &
+ & )
+ case (specialCaseCoredNFW %ID)
+ analytic=.true.
+ energy =+Pi**2 &
+ & *( &
+ & +radiusOuterScaleFree &
+ & *( &
+ & +4.0d0+radiusOuterScaleFree &
+ & *( &
+ & +10.0d0+radiusOuterScaleFree &
+ & *( &
+ & +35.0d0-4.0d0*Pi**2*(1.0d0+radiusOuterScaleFree)**3 &
+ & + 6.0d0*radiusOuterScaleFree &
+ & *( &
+ & +9.0d0+4.0d0*radiusOuterScaleFree &
+ & ) &
+ & ) &
+ & ) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree)**3 &
+ & -4.0d0*log(1.0d0+radiusOuterScaleFree) &
+ & *( &
+ & +1.0d0+radiusOuterScaleFree &
+ & -3.0d0*radiusOuterScaleFree**2 &
+ & -6.0d0*radiusOuterScaleFree**3 &
+ & +3.0d0*radiusOuterScaleFree**3*(1.0d0+radiusOuterScaleFree)*log(1.0d0+radiusOuterScaleFree) &
+ & ) &
+ & /(1.0d0+radiusOuterScaleFree) &
+ & -24.0d0*radiusOuterScaleFree**3*Dilogarithm(-radiusOuterScaleFree) &
+ & )
+ case default
+ energy=+0.0d0
+ call Error_Report('unknown special case'//{introspection:location})
+ end select
+ end select
+ end select
+ if (analytic) then
+ energy=+energy &
+ & *gravitationalConstantGalacticus &
+ & *self%scaleLength **5 &
+ & *self%densityNormalization **2
+ else
+ energy=+self%energyKineticNumerical(radiusOuter,massDistributionEmbedding)
+ end if
+ return
+ end function zhao1996EnergyKinetic
+
+ subroutine zhao1996Descriptor(self,descriptor,includeClass,includeFileModificationTimes)
+ !!{
+ Return an input parameter list descriptor which could be used to recreate this object.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ class (massDistributionZhao1996), intent(inout) :: self
+ type (inputParameters ), intent(inout) :: descriptor
+ logical , intent(in ), optional :: includeClass , includeFileModificationTimes
+ character(len=18 ) :: parameterLabel
+ type (inputParameters ) :: parameters
+
+ if (.not.present(includeClass).or.includeClass) call descriptor%addParameter('massDistribution','Zhao1996')
+ parameters=descriptor%subparameters('massDistribution')
+ write (parameterLabel,'(e17.10)') self%densityNormalization
+ call parameters%addParameter('densityNormalization',trim(adjustl(parameterLabel)))
+ write (parameterLabel,'(e17.10)') self%scaleLength
+ call parameters%addParameter('scaleLength' ,trim(adjustl(parameterLabel)))
+ return
+ end subroutine zhao1996Descriptor
+
diff --git a/source/mass_distributions.spherical.accelerator.F90 b/source/mass_distributions.spherical.accelerator.F90
new file mode 100644
index 0000000000..182ff4d49a
--- /dev/null
+++ b/source/mass_distributions.spherical.accelerator.F90
@@ -0,0 +1,221 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements an accelerator for spherical mass distributions.
+ !!}
+
+ use :: Binary_Search_Trees , only : binaryTree
+
+ !![
+
+
+ Accelerates spherical mass distribution classes by storing previous results for the enclosed mass and interpolating where
+ possible.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalAccelerator
+ !!{
+ Implementation of a finite resolution spherical mass distribution.
+ !!}
+ private
+ type (binaryTree) :: treeMassEnclosed
+ double precision :: toleranceRelative, factorRadiusMaximum, factorRadiusLogarithmicMaximum
+ contains
+ final :: sphericalAcceleratorDestructor
+ procedure :: density => sphericalAcceleratorDensity
+ procedure :: massEnclosedBySphere => sphericalAcceleratorMassEnclosedBySphere
+ procedure :: useUndecorated => sphericalAcceleratorUseUndecorated
+ end type massDistributionSphericalAccelerator
+
+ interface massDistributionSphericalAccelerator
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalAccelerator} mass distribution class.
+ !!}
+ module procedure sphericalAcceleratorConstructorParameters
+ module procedure sphericalAcceleratorConstructorInternal
+ end interface massDistributionSphericalAccelerator
+
+contains
+
+ function sphericalAcceleratorConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalAccelerator} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalAccelerator) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision :: toleranceRelative, factorRadiusMaximum
+ type (varying_string ) :: componentType , massType , &
+ & nonAnalyticSolver
+
+ !![
+
+ toleranceRelative
+ 1.0d-2
+
+ The tolerance with which to accept accelerated estimates.
+
+
+ factorRadiusMaximum
+ 3.0d0
+
+ The maximum factor by which to interpolate in radius.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalAccelerator(toleranceRelative,factorRadiusMaximum,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ return
+ end function sphericalAcceleratorConstructorParameters
+
+ function sphericalAcceleratorConstructorInternal(toleranceRelative,factorRadiusMaximum,nonAnalyticSolver,massDistribution_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalAccelerator'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalAccelerator) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ double precision , intent(in ) :: toleranceRelative, factorRadiusMaximum
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%factorRadiusLogarithmicMaximum=+log(sqrt(factorRadiusMaximum))
+ self%dimensionless =self%massDistribution_%isDimensionless()
+ return
+ end function sphericalAcceleratorConstructorInternal
+
+ subroutine sphericalAcceleratorDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalAccelerator} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalAccelerator), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine sphericalAcceleratorDestructor
+
+ logical function sphericalAcceleratorUseUndecorated(self) result(useUndecorated)
+ !!{
+ Determines whether to use the undecorated solution.
+ !!}
+ implicit none
+ class(massDistributionSphericalAccelerator), intent(inout) :: self
+
+ useUndecorated=.false.
+ return
+ end function sphericalAcceleratorUseUndecorated
+
+ double precision function sphericalAcceleratorDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an accelerated mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalAccelerator), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ density=self%massDistribution_%density(coordinates)
+ return
+ end function sphericalAcceleratorDensity
+
+ double precision function sphericalAcceleratorMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for accelerated mass distributions.
+ !!}
+ use :: Binary_Search_Trees , only : binaryTreeNode
+ use :: Numerical_Comparison, only : Values_Agree
+ implicit none
+ class (massDistributionSphericalAccelerator), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ type (binaryTreeNode ), pointer :: left1 , left2 , &
+ & right1 , right2
+ double precision :: massEnclosed1 , massEnclosed2, &
+ & radiusLogarithmic
+ logical :: found
+
+ found =.false.
+ radiusLogarithmic=log(radius)
+ call self%treeMassEnclosed%bracket(radiusLogarithmic,left1,right1)
+ if (associated(left1).and.associated(right1)) then
+ if (associated(left1,right1)) then
+ mass=exp(left1%value)
+ found =.true.
+ else
+ if ( &
+ & +radiusLogarithmic- left1%key < self%factorRadiusLogarithmicMaximum &
+ & .and. &
+ & -radiusLogarithmic+right1%key < self%factorRadiusLogarithmicMaximum &
+ & ) then
+ left2 => left1%predecessor()
+ right2 => right1% successor()
+ if (associated(left2).and.associated(right2)) then
+ massEnclosed1=(radiusLogarithmic-left1%key)*(right1%value-left1%value)/(right1%key-left1%key)+left1%value
+ massEnclosed2=(radiusLogarithmic-left2%key)*(right2%value-left2%value)/(right2%key-left2%key)+left2%value
+ if (Values_Agree(massEnclosed1,massEnclosed2,relTol=self%toleranceRelative)) then
+ mass=exp(massEnclosed1)
+ found =.true.
+ end if
+ end if
+ end if
+ end if
+ end if
+ if (.not.found) then
+ mass=self%massDistribution_%massEnclosedBySphere(radius)
+ call self%treeMassEnclosed%insert(radiusLogarithmic,log(mass))
+ end if
+ return
+ end function sphericalAcceleratorMassEnclosedBySphere
diff --git a/source/mass_distributions.spherical.accretion_flow.DiemerKravtsov2014.F90 b/source/mass_distributions.spherical.accretion_flow.DiemerKravtsov2014.F90
new file mode 100644
index 0000000000..1fc509bfdb
--- /dev/null
+++ b/source/mass_distributions.spherical.accretion_flow.DiemerKravtsov2014.F90
@@ -0,0 +1,186 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a mass distribution for accretion flow using the fitting function of \cite{diemer_dependence_2014}.
+ !!}
+
+ !![
+
+
+ A mass distribution class for accretion flows which models the accretion flow using the fitting function of
+ \cite{diemer_dependence_2014}. Specifically, the density profile of the accretion flow is modeled using their equation~(4).
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionDiemerKravtsov2014
+ !!{
+ A mass distribution for accretion flow using the fitting function of \cite{diemer_dependence_2014}.
+ !!}
+ private
+ double precision :: radius200Mean, densityMean, &
+ & b , s
+ logical :: includeMean
+ contains
+ procedure :: density => diemerKravtsov2014Density
+ procedure :: densityGradientRadial => diemerKravtsov2014DensityGradientRadial
+ end type massDistributionDiemerKravtsov2014
+
+ interface massDistributionDiemerKravtsov2014
+ !!{
+ Constructors for the {\normalfont \ttfamily diemerKravtsov2014} mass distribution class.
+ !!}
+ module procedure massDistributionDiemerKravtsov2014ConstructorParameters
+ module procedure massDistributionDiemerKravtsov2014ConstructorInternal
+ end interface massDistributionDiemerKravtsov2014
+
+contains
+
+ function massDistributionDiemerKravtsov2014ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily diemerKravtsov2014} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionDiemerKravtsov2014) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: radius200Mean, densityMean, &
+ & b , s
+ logical :: includeMean
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ densityMean
+ The mean density of the universe in the \cite{diemer_dependence_2014} accretion flow mass distribution.
+
+
+
+ radius200Mean
+ The radius enclosing a density of 200 times the mean density of the universe in the \cite{diemer_dependence_2014} accretion flow mass distribution.
+
+
+
+ includeMean
+ If true, include the mean density of the universe in the profile, otherwise, subtract off that mean density.
+
+ .true.
+
+
+ b
+ The coefficient $b$ in the \cite{diemer_dependence_2014} accretion flow mass distribution.
+
+
+
+ s
+ The exponent $s$ in the \cite{diemer_dependence_2014} accretion flow mass distribution.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+ !!]
+ self=massDistributionDiemerKravtsov2014(densityMean,radius200Mean,includeMean,b,s,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ !![
+
+ !!]
+ return
+ end function massDistributionDiemerKravtsov2014ConstructorParameters
+
+ function massDistributionDiemerKravtsov2014ConstructorInternal(densityMean,radius200Mean,includeMean,b,s,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``diemerKravtsov2014'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionDiemerKravtsov2014) :: self
+ double precision , intent(in ) :: densityMean , radius200Mean, &
+ & b , s
+ logical , intent(in ) :: includeMean
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%dimensionless=.false.
+ return
+ end function massDistributionDiemerKravtsov2014ConstructorInternal
+
+ double precision function diemerKravtsov2014Density(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a \cite{diemer_dependence_2014} mass distribution.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateSpherical
+ implicit none
+ class(massDistributionDiemerKravtsov2014), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ density=+self%densityMean &
+ & *self%b &
+ & /( &
+ & +coordinates%rSpherical () &
+ & /5.0d0 &
+ & /self %radius200Mean &
+ & )**self%s
+ if (self%includeMean) &
+ & density=+ density &
+ & +self%densityMean
+ return
+ end function diemerKravtsov2014Density
+
+ double precision function diemerKravtsov2014DensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density gradient at the specified {\normalfont \ttfamily coordinates} in a \cite{diemer_dependence_2014} mass distribution.
+ !!}
+ implicit none
+ class (massDistributionDiemerKravtsov2014), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ densityGradient=-self%s &
+ & /( &
+ & +1.0d0 &
+ & +( &
+ & +coordinates%rSpherical () &
+ & /5.0d0 &
+ & /self %radius200Mean &
+ & )**self%s &
+ & /self%b &
+ & )
+ if (.not.logarithmic_) &
+ & densityGradient=+ densityGradient &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ return
+ end function diemerKravtsov2014DensityGradientRadial
diff --git a/source/mass_distributions.spherical.accretion_flow.F90 b/source/mass_distributions.spherical.accretion_flow.F90
new file mode 100644
index 0000000000..227cc1f906
--- /dev/null
+++ b/source/mass_distributions.spherical.accretion_flow.F90
@@ -0,0 +1,269 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a accretionFlow spherical mass distribution.
+ !!}
+
+ !![
+
+
+ An implementation of a mass distribution which includes the accretion flow surrounding a halo. The density
+ profile is modeled as
+ \begin{equation}
+ \rho(r) = f_\mathrm{trans}(r) \rho_\mathrm{halo}(r) + \rho_\mathrm{accretion}(r),
+ \end{equation}
+ where $\rho_\mathrm{halo}(r)$ is the halo mass distribution, $\rho_\mathrm{accretion}(r)$ is the accretion flow mass distribution,
+ and
+ \begin{equation}
+ f_\mathrm{trans}(r) = \left( 1 + \left[\frac{r}{r_\mathrm{trans}}\right]^4 \right)^{-2}.
+ \end{equation}
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalAccretionFlow
+ !!{
+ Implementation of an accretion flow spherical mass distribution.
+ !!}
+ private
+ class (massDistributionClass), pointer :: massDistributionAccretionFlow_ => null()
+ double precision :: radiusTransition
+ contains
+ !![
+
+
+
+ !!]
+ final :: sphericalAccretionFlowDestructor
+ procedure :: density => sphericalAccretionFlowDensity
+ procedure :: densityGradientRadial => sphericalAccretionFlowDensityGradientRadial
+ procedure :: energyPotential => sphericalAccretionFlowEnergyPotential
+ procedure :: energyKinetic => sphericalAccretionFlowEnergyKinetic
+ procedure :: transitionFunction => sphericalAccretionFlowTransitionFunction
+ end type massDistributionSphericalAccretionFlow
+
+ interface massDistributionSphericalAccretionFlow
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalAccretionFlow} mass distribution class.
+ !!}
+ module procedure sphericalAccretionFlowConstructorParameters
+ module procedure sphericalAccretionFlowConstructorInternal
+ end interface massDistributionSphericalAccretionFlow
+
+contains
+
+ function sphericalAccretionFlowConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalAccretionFlow} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalAccretionFlow) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_, massDistributionAccretionFlow_
+ type (varying_string ) :: nonAnalyticSolver
+ double precision :: radiusTransition
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ radiusTransition
+
+ The transition radius.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ select type (massDistributionAccretionFlow_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalAccretionFlow(radiusTransition,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,massDistributionAccretionFlow_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric accretion flow mass distribution is required'//{introspection:location})
+ end select
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required' //{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ return
+ end function sphericalAccretionFlowConstructorParameters
+
+ function sphericalAccretionFlowConstructorInternal(radiusTransition,nonAnalyticSolver,massDistribution_,massDistributionAccretionFlow_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalAccretionFlow'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalAccretionFlow) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_, massDistributionAccretionFlow_
+ double precision , intent(in ) :: radiusTransition
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%dimensionless=.false.
+ return
+ end function sphericalAccretionFlowConstructorInternal
+
+ subroutine sphericalAccretionFlowDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalAccretionFlow} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalAccretionFlow), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalAccretionFlowDestructor
+
+ subroutine sphericalAccretionFlowTransitionFunction(self,radius,multiplier,multiplierGradient)
+ !!{
+ Return the scaled truncation radial coordinate, and the truncation multiplier.
+ !!}
+ implicit none
+ class (massDistributionSphericalAccretionFlow), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision , intent( out), optional :: multiplier, multiplierGradient
+ double precision :: x
+
+ x =+ radius &
+ & /self%radiusTransition
+ if (present(multiplier )) then
+ multiplier =+1.0d0 &
+ & /( &
+ & +1.0d0 &
+ & +x**4 &
+ & )**2
+ end if
+ if (present(multiplierGradient)) then
+ multiplierGradient=+8.0d0 &
+ & * x**3 &
+ & /( &
+ & +1.0d0 &
+ & +x**4 &
+ & )**3 &
+ & /self%radiusTransition
+ end if
+ return
+ end subroutine sphericalAccretionFlowTransitionFunction
+
+ double precision function sphericalAccretionFlowDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalAccretionFlow), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: multiplier
+
+ call self%transitionFunction(radius=coordinates%rSpherical(),multiplier=multiplier)
+ density=+self%massDistribution_ %density(coordinates) &
+ & * multiplier &
+ & +self%massDistributionAccretionFlow_%density(coordinates) &
+ & *(1.0d0-multiplier)
+ return
+ end function sphericalAccretionFlowDensity
+
+ double precision function sphericalAccretionFlowDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a accretionFlow spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalAccretionFlow), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: multiplier , multiplierGradient
+ !![
+
+ !!]
+
+ call self%transitionFunction(radius=coordinates%rSpherical(),multiplier=multiplier,multiplierGradient=multiplierGradient)
+ densityGradient=+ self%massDistribution_ %densityGradientRadial(coordinates,logarithmic=.false.) &
+ & * multiplier &
+ & + self%massDistributionAccretionFlow_%densityGradientRadial(coordinates,logarithmic=.false.) &
+ & *(1.0d0-multiplier ) &
+ & +( &
+ & +self%massDistribution_ %density (coordinates ) &
+ & -self%massDistributionAccretionFlow_%density (coordinates ) &
+ & ) &
+ & + multiplierGradient
+ if (logarithmic_) densityGradient=+ densityGradient &
+ & *coordinates%rSpherical ( ) &
+ & /self %density (coordinates)
+ return
+ end function sphericalAccretionFlowDensityGradientRadial
+
+ double precision function sphericalAccretionFlowEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in a spherical accretion flow mass
+ distribution. Note that this is defined to be the potential energy of the \emph{virialized} component of the mass
+ distribution---the accretion flow itself is excluded.
+ !!}
+ implicit none
+ class (massDistributionSphericalAccretionFlow), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ energy=self%massDistribution_%energyPotential(radiusOuter)
+ return
+ end function sphericalAccretionFlowEnergyPotential
+
+ double precision function sphericalAccretionFlowEnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius} in a spherical accretion flow mass distribution. Note
+ that this is defined to be the potential energy of the \emph{virialized} component of the mass distribution---the accretion
+ flow itself is excluded.
+ !!}
+ implicit none
+ class (massDistributionSphericalAccretionFlow), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ energy=self%massDistribution_%energyKinetic(radiusOuter,massDistributionEmbedding)
+ return
+ end function sphericalAccretionFlowEnergyKinetic
+
diff --git a/source/mass_distributions.spherical.accretion_flow.Shi2016.F90 b/source/mass_distributions.spherical.accretion_flow.Shi2016.F90
new file mode 100644
index 0000000000..680da88ce6
--- /dev/null
+++ b/source/mass_distributions.spherical.accretion_flow.Shi2016.F90
@@ -0,0 +1,760 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An accretion flow mass distribution using the framework of \cite{shi_outer_2016}.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Numerical_Interpolation, only : gsl_interp_cspline , interpolator
+
+ ! Note: Throughout this class the following acronyms are used:
+ ! * HRTA - "half radius turnaround" - i.e. half of the turnaround radius for a given shell (more precisely, we use the
+ ! ratio of the virial radius to turnaround radius determined by spherical collapse models - this is previously
+ ! 1/2 for an Einstein-de Sitter universe, but differs by a small amount for other cosmologies).
+
+ ! Note: Throughout this class different three separate unit systems are used, identified by the variable name suffix:
+ ! * Scaled - these correspond to the scaled, self-similar variables used in Appendix A of Shi (2016) - i.e. column 3 of Table A1, "y" for radius, etc.
+ ! * Original - these correspond to the original variables used in Appendix A of Shi (2016) - i.e. column 2 of Table A1, "R" for radius, etc.
+ ! * ScaleHRTA - these correspond to the HRTA unit system - that is, quantities are scaled to Rₕᵣₜₐ(a) and Mₕᵣₜₐ(a).
+ ! * Physical - these correspond to physical units (Mpc, M☉, km/s).
+
+ !![
+
+
+ A mass distribution for accretion flows using the framework of \cite{shi_outer_2016}.
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionShi2016
+ !!{
+ A mass distribution for accretion flows using the framework of \cite{shi_outer_2016}.
+ !!}
+ private
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ type (interpolator ), allocatable :: interpolatorDensityPhysical , interpolatorVelocityPhysical , &
+ & interpolatorScaleFactorHalfRadiusTurnaroundScaled , interpolatorRadiusScaled , &
+ & interpolatorRadiusTurnaroundOriginal , interpolatorRadiusHRTAOriginal , &
+ & interpolatorRadiusComovingInitialOriginal , interpolatorMassTurnaroundScaled , &
+ & interpolatorMassHRTAOriginal , interpolatorMassMultiStreamScaleHRTA
+ double precision , allocatable, dimension(:) :: radiusScaled , overdensityScaled , &
+ & expansionFactorHRTAScaled , radiusGrowthRateScaled , &
+ & radiusComovingInitialOriginal , massEnclosedInitialOriginal , &
+ & timeTurnaroundScaled , timeHRTAScaled , &
+ & radiusTurnaroundScaled , radiusOrderedOriginal , &
+ & massShellOrderedOriginal , densityOrderedOriginal , &
+ & massEnclosedOrderedOriginal
+ double precision :: radiusMaximumPhysical , scaleFactorVelocity , &
+ & expansionFactorScaled , radiusMultistreamMinimumScaledHRTA , &
+ & radiusMultistreamMaximumScaledHRTA , cosmologicalConstantScaled , &
+ & radiusTurnaroundNowOriginal , radiusHRTANowOriginal , &
+ & timeNowScaled , radiusSplashbackTurnaround , &
+ & ratioRadiusSplashbackHRTA , radiusSplashbackOriginal , &
+ & radiusSplashbackScaled , radiusMinimumPhysical , &
+ & radiusVirial , mass , &
+ & massAccretionRate , redshift , &
+ & time , ratioRadiusTurnaroundVirial
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: density => shi2016Density
+ procedure :: densityGradientRadial => shi2016DensityGradientRadial
+ procedure :: solve => shi2016Solve
+ end type massDistributionShi2016
+
+ interface massDistributionShi2016
+ !!{
+ Constructors for the {\normalfont \ttfamily shi2016} mass distribution class.
+ !!}
+ module procedure massDistributionShi2016ConstructorParameters
+ module procedure massDistributionShi2016ConstructorInternal
+ end interface massDistributionShi2016
+
+ ! Sub-module scope variables used in root finding.
+ double precision :: timeTurnaroundScaled__, radiusTurnaroundScaled__
+ !$omp threadprivate(timeTurnaroundScaled__, radiusTurnaroundScaled__)
+
+ ! Sub-module scope variables used in ODE solving.
+ double precision :: radiusComovingInitialOriginal , massEnclosedInitialOriginal
+ class (massDistributionShi2016), pointer :: self_
+ logical :: noShellCrossing =.false.
+ !$omp threadprivate(self_,massEnclosedInitialOriginal,radiusComovingInitialOriginal,noShellCrossing)
+
+contains
+
+ function massDistributionShi2016ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily shi2016} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionShi2016 ) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ double precision :: scaleFactorVelocity , mass , &
+ & massAccretionRate , ratioRadiusTurnaroundVirial, &
+ & radiusVirial , redshift
+
+ !![
+
+ mass
+ The mass of the halo.
+
+
+
+ massAccretionRate
+ The mass accretion rate of the halo.
+
+
+
+ radiusVirial
+ The virial radius of the halo.
+
+
+
+ ratioRadiusTurnaroundVirial
+ The ratio of the turnaround to virial radii of the halo.
+
+
+
+ redshift
+ The redshift of the halo.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ self=massDistributionShi2016(mass,massAccretionRate,radiusVirial,ratioRadiusTurnaroundVirial,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),scaleFactorVelocity,cosmologyFunctions_,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ !![
+
+
+ !!]
+ return
+ end function massDistributionShi2016ConstructorParameters
+
+ function massDistributionShi2016ConstructorInternal(mass,massAccretionRate,radiusVirial,ratioRadiusTurnaroundVirial,time,scaleFactorVelocity,cosmologyFunctions_,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``shi2016'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionShi2016 ) :: self
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ double precision , intent(in ) :: mass , massAccretionRate , &
+ & time , scaleFactorVelocity , &
+ & radiusVirial , ratioRadiusTurnaroundVirial
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%dimensionless=.false.
+ self%redshift =self%cosmologyFunctions_%redshiftFromExpansionFactor(self%cosmologyFunctions_%expansionFactor(time))
+ call self%solve()
+ return
+ end function massDistributionShi2016ConstructorInternal
+
+ subroutine shi2016Destructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily shi2016} accretion flow mass distribution class.
+ !!}
+ implicit none
+ type(massDistributionShi2016), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine shi2016Destructor
+
+ double precision function shi2016Density(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a accretion flow modeled on the 2-halo correlation function.
+ !!}
+ implicit none
+ class(massDistributionShi2016), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ if (coordinates%rSpherical() > self%radiusMaximumPhysical) then
+ ! Beyond the maximum radius for the flow just return the mean matter density.
+ density=self%cosmologyFunctions_ %matterDensityEpochal(self %time )
+ else if (coordinates%rSpherical() < self%radiusMinimumPhysical) then
+ density=0.0d0
+ call Error_Report('radius is less than minimum tabulated for accretion flow'//{introspection:location})
+ else
+ density=self%interpolatorDensityPhysical%interpolate (coordinates%rSpherical())
+ end if
+ return
+ end function shi2016Density
+
+ double precision function shi2016DensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the radial density gradient at the specified {\normalfont \ttfamily coordinates} in a accretion flow modeled on the 2-halo correlation function.
+ !!}
+ implicit none
+ class (massDistributionShi2016), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ if (coordinates%rSpherical() > self%radiusMaximumPhysical) then
+ ! Beyond the maximum radius for the flow just return the mean matter density.
+ densityGradientRadial=0.0d0
+ else if (coordinates%rSpherical() < self%radiusMinimumPhysical) then
+ densityGradientRadial=0.0d0
+ call Error_Report('radius is less than minimum tabulated for accretion flow'//{introspection:location})
+ else
+ densityGradientRadial=self%interpolatorDensityPhysical%derivative(coordinates%rSpherical())
+ end if
+ return
+ end function shi2016DensityGradientRadial
+
+ subroutine shi2016Solve(self)
+ !!{
+ Solve the accretion flow.
+ !!}
+ use :: Array_Utilities , only : Array_Reverse
+ use :: Display , only : displayCounter , displayCounterClear , displayIndent , displayUnindent, &
+ & verbosityLevelWorking
+ use :: Elliptic_Integrals , only : Elliptic_Integral_K , Elliptic_Integral_Pi
+ use :: Error , only : Error_Report
+ use :: ISO_Varying_String , only : var_str
+ use :: Numerical_Comparison , only : Values_Differ
+ use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ use :: Sorting , only : sortIndex
+ use :: String_Handling , only : operator(//)
+ implicit none
+ class (massDistributionShi2016), intent(inout), target :: self
+ double precision , allocatable , dimension(:) :: radiusOriginal , densityOriginal , &
+ & velocityOriginal , radiusSingleStreamAnalyticPhysical , &
+ & velocitySingleStreamAnalyticPhysical , densitySingleStreamAnalyticPhysical , &
+ & expansionFactorHRTAScaled , radiusPhysical , &
+ & velocityPhysical , densityPhysical
+ integer (c_size_t ), allocatable , dimension(:) :: order
+ type (interpolator ), allocatable :: interpolatorMassMultiStreamNewScaleHRTA
+ integer , parameter :: countRadii =1000
+ integer , parameter :: countCompare = 10
+ integer , parameter :: iterationMaximum = 30
+ double precision , parameter :: expansionFactorRelativeInitial =1.0d-6
+ double precision , parameter :: radiusMultiStreamFractionalSmall =1.0d-6
+ double precision , parameter :: multistreamToleranceRelative =5.0d-2
+ double precision :: expansionFactorOriginal , timeInitialScaled , &
+ & radiusInitialScaled , radiusGrowthRateInitialScaled , &
+ & overdensityMinimumScaled , overdensityMaximumScaled , &
+ & bigA , bigB , &
+ & bigC , expansionFactorScaledInitial , &
+ & massVirialOriginal , radiusVirialOriginal , &
+ & growthIndex , radiusScaleHRTA , &
+ & radiusMultistreamMinimumNewScaledHRTA , radiusMultistreamMaximumNewScaledHRTA, &
+ & radiusCompareMinimumScaledHRTA , radiusCompareMaximumScaledHRTA , &
+ & massMultiStream , massMultiStreamNew , &
+ & changeRelative , changeRelativeMaximum , &
+ & radiusSplashbackPhysical , h
+ integer :: i , j , &
+ & iTurnaround , iFirstZero , &
+ & iVirial , iteration
+ type (rootFinder ) :: finder
+ logical :: firstZeroFound , multistreamConverged
+ character (len=12 ) :: label
+
+ ! The growth index specifies the profile of the initial mass perturbation, δMᵢ/Mᵢ ∝ M^{-1/s}, or, equivalently, the growth rate,
+ ! M(t) ∝ aˢ.
+ growthIndex=+self %massAccretionRate &
+ & /self%cosmologyFunctions_%expansionRate (self%cosmologyFunctions_%expansionFactor(self%time)) &
+ & /self %mass
+ ! Compute the scale-free solution.
+ allocate(self%overdensityScaled (countRadii))
+ allocate(self%radiusScaled (countRadii))
+ allocate(self%radiusGrowthRateScaled (countRadii))
+ allocate(self%expansionFactorHRTAScaled (countRadii))
+ allocate(self%radiusComovingInitialOriginal(countRadii))
+ allocate(self%massEnclosedInitialOriginal (countRadii))
+ allocate(self%radiusTurnaroundScaled (countRadii))
+ allocate(self%timeTurnaroundScaled (countRadii))
+ allocate(self%radiusOrderedOriginal (countRadii))
+ allocate(self%massShellOrderedOriginal (countRadii))
+ allocate(self%massEnclosedOrderedOriginal (countRadii))
+ allocate(self%densityOrderedOriginal (countRadii))
+ allocate(self%timeHRTAScaled (countRadii))
+ allocate( order (countRadii))
+ allocate( expansionFactorHRTAScaled (countRadii))
+ allocate( radiusOriginal (countRadii))
+ ! Create a module-scope pointer to self for use in ODE solver functions.
+ self_ => self
+ ! Compute the scaled cosmological constant parameter ("w" in the notation of Shi 2016, Table A1).
+ self%cosmologicalConstantScaled=+1.0d0/self%cosmologyFunctions_ %OmegaMatterEpochal(self%time) &
+ & -1.0d0
+ ! Compute expansion factor, and scaled expansion factor ("y" in the notation of Shi 2016, Table A1).
+ expansionFactorOriginal =+self%cosmologyFunctions_ %expansionFactor (self%time)
+ self%expansionFactorScaled =+self%cosmologicalConstantScaled **(1.0d0/3.0d0) &
+ & *expansionFactorOriginal
+ ! Find the scaled time at the current epoch.
+ self%timeNowScaled =+sqrt(1.0d0-self %cosmologyFunctions_%OmegaMatterEpochal(self%time )) & ! Equation A5 from Shi (2016).
+ & * self %cosmologyFunctions_%expansionRate (expansionFactorOriginal) &
+ & * self%time
+ ! Choose an initial epoch and (scaled) radius for the ODE. This is chosen to be an expansion factor much smaller than the
+ ! present day such that the perturbations will be small.
+ expansionFactorScaledInitial =+ expansionFactorRelativeInitial &
+ & *self%expansionFactorScaled
+ radiusInitialScaled =+ expansionFactorScaledInitial
+ ! Choose a value of the scaled overdensity, "β" in the notation of Shi (2016), identifying a mass shell. We avoid β=1 because such a shell never collapses.
+ overdensityMinimumScaled =+1.001d0
+ ! Choose a maximum overdensity. We use β=10 here as it's more than sufficient to allow most of the accretion stream to be captured,
+ overdensityMaximumScaled =+1.000d1
+ ! Build array of overdensities.
+ self%overdensityScaled =Make_Range(overdensityMaximumScaled,overdensityMinimumScaled,countRadii,rangeTypeLogarithmic)
+ ! Build a root finder which will be used for finding the time at half the turnaround radius.
+ finder=rootFinder( &
+ & rootFunction =halfRadiusTurnAroundRoot, &
+ & toleranceAbsolute=0.0d+0 , &
+ & toleranceRelative=1.0d-3 &
+ & )
+ ! Find turnaround radius and mass as a function of time, along with epoch at which half the turnaround radius is
+ ! reached. This is all in scale-free units.
+ do i=1,countRadii
+ ! Find the epoch of turnaround.
+ self%radiusTurnaroundScaled(i)=+2.0d0**(2.0d0/3.0d0) & ! Equation A9 from Shi (2016).
+ & * sqrt( self%overdensityScaled(i) ) &
+ & *sin((1.0d0/3.0d0)*asin(1.0d0/self%overdensityScaled(i)**1.5d0))
+ bigA =+1.0d0 & ! Text after equation of A12 from Shi (2016).
+ & /self%radiusTurnaroundScaled(i)**3
+ bigB =+2.0d0 & ! Text after equation of A12 from Shi (2016).
+ & / ( +3.0d0+sqrt(1.0d0+4.0d0*bigA) )
+ bigC =+ sqrt(1.0d0+4.0d0*bigA) & ! Text after equation of A12 from Shi (2016).
+ & / (bigA-0.5d0+sqrt(1.0d0+4.0d0*bigA)/2.0d0)
+ self%timeTurnaroundScaled (i)=+ ( +1.0d0+sqrt(1.0d0+4.0d0*bigA) ) & ! Equation A12 of Shi (2016).
+ & /sqrt(bigA-0.5d0+sqrt(1.0d0+4.0d0*bigA)/2.0d0) &
+ & *( &
+ & +Elliptic_Integral_Pi(bigC,bigB) &
+ & -Elliptic_Integral_K (bigC ) &
+ & )
+ ! Find the epoch corresponding to reaching half of the turnaround radius.
+ radiusTurnaroundScaled__=self%radiusTurnaroundScaled(i)
+ timeTurnaroundScaled__ =self%timeTurnaroundScaled (i)
+ call finder%rangeExpand( &
+ & rangeExpandUpward =1.1d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
+ & rangeDownwardLimit =timeTurnaroundScaled__ &
+ & )
+ self%timeHRTAScaled (i)=finder%find(rootGuess=timeTurnaroundScaled__)
+ self%expansionFactorHRTAScaled(i)=expansionFactorFromTimeScaled(self%timeHRTAScaled(i))
+ end do
+ ! Find enclosed masses. Note that this is found using the self-similarity assumption that the mass of a shell should scale
+ ! as the expansion factor at "half-radius-turnaround" to the power of the growth index, i.e. M ∝ uₕᵣₜₐˢ. In regions
+ ! where the cosmological constant is negligible (high overdensities, which collapse at early times), this will also give
+ ! the expected scaling with overdensity, M ∝ β⁻ˢ. But, at lower overdensities, which collapse when the
+ ! cosmological constant is non-negligible, the scaling with overdensity will change. What we do here seems to be consistent
+ ! with what Shi (2016) assumes, and has the nice feature that it ensures the mass-epoch relation is the simple scale-free
+ ! expectation at all times.
+ expansionFactorHRTAScaled =expansionFactorFromTimeScaled(self%timeHRTAScaled)
+ self%massEnclosedInitialOriginal =(expansionFactorHRTAScaled/self%expansionFactorScaled )**growthIndex
+ self%radiusComovingInitialOriginal= self%massEnclosedInitialOriginal **(1.0d0/3.0d0)
+ ! Build a variety of interpolators for different radii and masses as functions of scaled time.
+ allocate(self%interpolatorRadiusTurnaroundOriginal )
+ allocate(self%interpolatorRadiusHRTAOriginal )
+ allocate(self%interpolatorRadiusComovingInitialOriginal )
+ allocate(self%interpolatorMassTurnaroundScaled )
+ allocate(self%interpolatorMassHRTAOriginal )
+ allocate(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled)
+ self%interpolatorRadiusTurnaroundOriginal =interpolator( self%timeTurnaroundScaled , self%radiusTurnaroundScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0) )
+ self%interpolatorRadiusComovingInitialOriginal =interpolator( self%timeTurnaroundScaled , self%radiusComovingInitialOriginal )
+ self%interpolatorMassTurnaroundScaled =interpolator( self%timeTurnaroundScaled , self%massEnclosedInitialOriginal )
+ self%interpolatorRadiusHRTAOriginal =interpolator( self%timeHRTAScaled , self%radiusTurnaroundScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)/self%ratioRadiusTurnaroundVirial )
+ self%interpolatorMassHRTAOriginal =interpolator( self%timeHRTAScaled , self%massEnclosedInitialOriginal )
+ self%interpolatorScaleFactorHalfRadiusTurnaroundScaled=interpolator(Array_Reverse(self%overdensityScaled ),Array_Reverse(self%expansionFactorHRTAScaled ))
+ ! Compute present epoch turnaround and half-turnaround radii.
+ self%radiusTurnaroundNowOriginal=self%interpolatorRadiusTurnaroundOriginal%interpolate(self%timeNowScaled)
+ self%radiusHRTANowOriginal =self%interpolatorRadiusHRTAOriginal %interpolate(self%timeNowScaled)
+ ! Make an estimate of the splashback radius (in units of the turnaround radius, using eqn. 2 of Shi 2016 which is for an
+ ! Einstein-de Sitter universe). An approximate value is acceptable here as this is used only on the first iteration. It's
+ ! not clear if this is precisely what Shi (2016) chose, but it should not matter.
+ if (growthIndex <= 1.5d0) then
+ self%radiusSplashbackTurnaround=+1.0d0/3.0d0**(2.0d0/3.0d0+2.0d0*growthIndex/9.0d0)
+ else
+ self%radiusSplashbackTurnaround=+1.0d0/(1.0d0+4.0d0*(4.0d0*growthIndex/9.0d0+1.0d0/3.0d0)/sqrt(Pi))
+ end if
+ ! Find the splashback radius in units of the present day half-turnaround radius.
+ self%ratioRadiusSplashbackHRTA=self%radiusSplashbackTurnaround*self%radiusTurnaroundNowOriginal/self%radiusHRTANowOriginal
+ ! For the first iteration, adopt a mass profile solution in the multi-stream region that has the form f(x)=x (Shi 2016,
+ ! section 2.1).
+ allocate(self%interpolatorMassMultiStreamScaleHRTA)
+ self%radiusMultistreamMinimumScaledHRTA=radiusMultiStreamFractionalSmall*self%ratioRadiusSplashbackHRTA
+ self%radiusMultistreamMaximumScaledHRTA= self%ratioRadiusSplashbackHRTA
+ self%interpolatorMassMultiStreamScaleHRTA=interpolator([self%radiusMultistreamMinimumScaledHRTA,self%radiusMultistreamMaximumScaledHRTA],[self%radiusMultistreamMinimumScaledHRTA,self%radiusMultistreamMaximumScaledHRTA])
+ ! Begin iterating to find a solution.
+ iteration =0
+ multistreamConverged=.false.
+ do while (iteration < iterationMaximum .and. .not.multistreamConverged)
+ iteration=iteration+1
+ call displayIndent(var_str('multistream mass profile iteration ')//iteration,verbosity=verbosityLevelWorking)
+ ! Iterate over all overdensity shells solving for their radial position and velocity at the present epoch.
+ do i=1,countRadii
+ call displayCounter(int(100.0d0*dble(i-1)/dble(countRadii)),isNew=i==1,verbosity=verbosityLevelWorking)
+ ! Solve the dynamical ODEs to get the scaled radius at the final time.
+ radiusComovingInitialOriginal=self%radiusComovingInitialOriginal(i)
+ massEnclosedInitialOriginal =self%massEnclosedInitialOriginal (i)
+ radiusGrowthRateInitialScaled=+sqrt( & ! Equation A8 from Shi (2016).
+ & +1.0d0/ radiusInitialScaled &
+ & + radiusInitialScaled **2 &
+ & -3.0d0*self%overdensityScaled (i) &
+ & /2.0d0 **(2.0d0/3.0d0) &
+ & )
+ timeInitialScaled =+2.0d0 & ! Equation A6 from Shi (2016).
+ & /3.0d0 &
+ & *asinh( expansionFactorScaledInitial **1.5d0)
+ call radiusScaledSolver(timeInitialScaled,self%timeNowScaled,radiusInitialScaled,radiusGrowthRateInitialScaled,self%radiusScaled(i),self%radiusGrowthRateScaled(i))
+ end do
+ call displayCounterClear(verbosity=verbosityLevelWorking)
+ ! Build an interpolator for the scaled radius as a function of overdensity.
+ if (allocated(self%interpolatorRadiusScaled)) deallocate(self%interpolatorRadiusScaled)
+ allocate(self%interpolatorRadiusScaled)
+ self%interpolatorRadiusScaled=interpolator(Array_Reverse(self%overdensityScaled),Array_Reverse(self%radiusScaled))
+ ! Construct the mass and density profile by ordering the shells in radius. Also find the splashback radius.
+ radiusOriginal =abs(self%radiusScaled)*self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
+ order =sortIndex(radiusOriginal)
+ self%radiusSplashBackOriginal=-huge(0.0d0)
+ firstZeroFound =.false.
+ do i=1,countRadii
+ ! Order radii.
+ self%radiusOrderedOriginal(i)=radiusOriginal(order(i))
+ ! Compute mass and density in this shell.
+ if (order(i) == 1) then
+ self%massShellOrderedOriginal(i)=self%massEnclosedInitialOriginal(order(i))
+ self%densityOrderedOriginal (i)=self%massShellOrderedOriginal(i)*3.0d0/4.0d0/Pi/ self%radiusOrderedOriginal(i)**3
+ else
+ self%massShellOrderedOriginal(i)=self%massEnclosedInitialOriginal(order(i))-self%massEnclosedInitialOriginal(order(i)-1)
+ self%densityOrderedOriginal (i)=self%massShellOrderedOriginal(i)*3.0d0/4.0d0/Pi/(self%radiusOrderedOriginal(i)**3-self%radiusOrderedOriginal(i-1)**3)
+ end if
+ ! Find the mass enclosed by this shell.
+ self%massEnclosedOrderedOriginal(i)=sum(self%massShellOrderedOriginal(1:i))
+ end do
+ ! Find the splashback radius.
+ do i=countRadii,1,-1
+ firstZeroFound=firstZeroFound.or.self%radiusScaled(i) <= 0.0d0
+ if (firstZeroFound .and. radiusOriginal(i) > self%radiusSplashBackOriginal) then
+ self%radiusSplashbackOriginal= radiusOriginal(i)
+ self%radiusSplashbackScaled =abs(self%radiusScaled (i))
+ end if
+ end do
+ deallocate(radiusOriginal)
+ ! Find the splashback radius in units of the turnaround radius, and the half-turnaround radius.
+ self%radiusSplashbackTurnaround=self%radiusSplashBackOriginal/self%radiusTurnaroundNowOriginal
+ self%ratioRadiusSplashbackHRTA =self%radiusSplashBackOriginal/self%radiusHRTANowOriginal
+ ! Build a new interpolator for the mass profile in the multistream region, and compare it to the previous one.
+ allocate(interpolatorMassMultiStreamNewScaleHRTA)
+ interpolatorMassMultiStreamNewScaleHRTA=interpolator(self%radiusOrderedOriginal/self%radiusHRTANowOriginal,self%massEnclosedOrderedOriginal)
+ radiusMultistreamMinimumNewScaledHRTA =self%radiusOrderedOriginal( 1)/self%radiusHRTANowOriginal
+ radiusMultistreamMaximumNewScaledHRTA =self%radiusOrderedOriginal(countRadii)/self%radiusHRTANowOriginal
+ radiusCompareMinimumScaledHRTA =max(radiusMultistreamMinimumNewScaledHRTA,self%radiusMultistreamMinimumScaledHRTA)
+ radiusCompareMaximumScaledHRTA =min(radiusMultistreamMaximumNewScaledHRTA,self%radiusMultistreamMaximumScaledHRTA)
+ changeRelativeMaximum =-huge(0.0d0)
+ do j=1,countCompare
+ radiusScaleHRTA =+ radiusCompareMinimumScaledHRTA &
+ & +(radiusCompareMaximumScaledHRTA-radiusCompareMinimumScaledHRTA) &
+ & *dble( j) &
+ & /dble(countCompare)
+ massMultiStream =self%interpolatorMassMultiStreamScaleHRTA %interpolate(radiusScaleHRTA)
+ massMultiStreamNew = interpolatorMassMultiStreamNewScaleHRTA%interpolate(radiusScaleHRTA)
+ changeRelative =+abs(massMultiStream-massMultiStreamNew) &
+ & / (massMultiStream+massMultiStreamNew) &
+ & /0.5d0
+ changeRelativeMaximum=max(changeRelative,changeRelativeMaximum)
+ end do
+ multistreamConverged=changeRelativeMaximum <= multistreamToleranceRelative
+ deallocate(interpolatorMassMultiStreamNewScaleHRTA)
+ ! Replace the interpolator for the mass profile in the multistream region with the updated one.
+ deallocate(self%interpolatorMassMultiStreamScaleHRTA)
+ allocate (self%interpolatorMassMultiStreamScaleHRTA)
+ self%interpolatorMassMultiStreamScaleHRTA=interpolator(self%radiusOrderedOriginal/self%radiusHRTANowOriginal,self%massEnclosedOrderedOriginal)
+ self%radiusMultistreamMinimumScaledHRTA =self%radiusOrderedOriginal( 1)/self%radiusHRTANowOriginal
+ self%radiusMultistreamMaximumScaledHRTA =self%radiusOrderedOriginal(countRadii)/self%radiusHRTANowOriginal
+ write (label,'(e8.2)') changeRelativeMaximum
+ call displayUnindent(var_str('done [fractional change = ')//trim(adjustl(label))//']',verbosity=verbosityLevelWorking)
+ end do
+ if (.not.multistreamConverged) call Error_Report('failed to reach convergence in the multistream region'//{introspection:location})
+ ! Compute properties along the stream.
+ allocate(radiusOriginal (countRadii))
+ allocate(densityOriginal (countRadii))
+ allocate(velocityOriginal(countRadii))
+ !! Find the radii and velocities in the stream.
+ radiusOriginal =self%radiusScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
+ velocityOriginal=self%radiusGrowthRateScaled*self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
+ !! Compute densities.
+ do i=1,countRadii
+ if (i == 1) then
+ densityOriginal(i)= self%massEnclosedInitialOriginal(i) *3.0d0/4.0d0/Pi/abs(radiusOriginal(i))**3
+ else
+ densityOriginal(i)=(self%massEnclosedInitialOriginal(i)-self%massEnclosedInitialOriginal(i-1))*3.0d0/4.0d0/Pi/abs(radiusOriginal(i) **3-radiusOriginal(i-1)**3)
+ end if
+ end do
+ ! Find the shells at their turnaround radius and "half" of their turnaround radius (this defines the virial mass/radius), and
+ ! also the shell which is about to make its first passage through zero radius.
+ iVirial =-1
+ iTurnaround=-1
+ iFirstZero =-1
+ do i=countRadii,1,-1
+ if (iVirial < 0 .and. self%radiusGrowthRateScaled(i) < 0.0d0 .and. self%radiusScaled(i) <= self%radiusTurnaroundScaled(i)/self%ratioRadiusTurnaroundVirial) &
+ & iVirial =i
+ if (iTurnaround < 0 .and. self%radiusGrowthRateScaled(i) <= 0.0d0 ) &
+ & iTurnaround=i
+ if (iFirstZero < 0 .and. self%radiusGrowthRateScaled(i) < 0.0d0 .and. self%radiusScaled(i) < 0.0d0 ) &
+ & iFirstZero =i+1
+ end do
+ ! Interpolate to get a more precise virial radius.
+ h =+(1.0d0 /self%ratioRadiusTurnaroundVirial -self%radiusScaled(iVirial)/self%radiusTurnaroundScaled(iVirial)) &
+ & /(self%radiusScaled(iVirial+1)/self%radiusTurnaroundScaled(iVirial+1)-self%radiusScaled(iVirial)/self%radiusTurnaroundScaled(iVirial))
+ radiusVirialOriginal=+( &
+ & +self%radiusScaled(iVirial )*self%radiusComovingInitialOriginal(iVirial )*(1.0d0-h) &
+ & +self%radiusScaled(iVirial+1)*self%radiusComovingInitialOriginal(iVirial+1)* h &
+ & ) &
+ & /self%cosmologicalConstantScaled**(1.0d0/3.0d0)
+ massVirialOriginal =+self%interpolatorMassHRTAOriginal %interpolate( self%timeNowScaled ) &
+ & *self%interpolatorMassMultiStreamScaleHRTA%interpolate(radiusVirialOriginal/self%radiusHRTANowOriginal)
+ ! Scale radii, velocities, and densities to the virial radius/mass.
+ allocate(radiusPhysical (countRadii))
+ allocate(velocityPhysical(countRadii))
+ allocate(densityPhysical (countRadii))
+ radiusPhysical =+radiusOriginal &
+ & * self_%radiusVirial/radiusVirialOriginal
+ velocityPhysical=+velocityOriginal &
+ & * self_%radiusVirial/radiusVirialOriginal &
+ & * self_%timeNowScaled /self%time &
+ & *megaParsec &
+ & /gigaYear &
+ & /kilo
+ densityPhysical =+densityOriginal &
+ & * self%mass /massVirialOriginal &
+ & /(self_%radiusVirial/radiusVirialOriginal)**3
+ ! Build interpolators into the infall stream.
+ if (allocated(self%interpolatorDensityPhysical )) deallocate(self%interpolatorDensityPhysical )
+ if (allocated(self%interpolatorVelocityPhysical)) deallocate(self%interpolatorVelocityPhysical)
+ allocate(self%interpolatorDensityPhysical )
+ allocate(self%interpolatorVelocityPhysical)
+ self%interpolatorDensityPhysical =interpolator(radiusPhysical(iFirstZero:countRadii),densityPhysical (iFirstZero:countRadii),interpolationType=gsl_interp_cspline)
+ self%interpolatorVelocityPhysical=interpolator(radiusPhysical(iFirstZero:countRadii),velocityPhysical(iFirstZero:countRadii),interpolationType=gsl_interp_cspline)
+ self%radiusMinimumPhysical =radiusPhysical(iFirstZero)
+ self%radiusMaximumPhysical =radiusPhysical(countRadii)
+ ! Compute the analytic solution in the single stream regime.
+ allocate(radiusSingleStreamAnalyticPhysical (countRadii-iVirial-1))
+ allocate(densitySingleStreamAnalyticPhysical (countRadii-iVirial-1))
+ allocate(velocitySingleStreamAnalyticPhysical(countRadii-iVirial-1))
+ do i=iVirial+1,countRadii-1
+ radiusSingleStreamAnalyticPhysical (i-iVirial)=+( &
+ & +3.0d0 &
+ & *self%mass &
+ & /4.0d0 &
+ & /Pi &
+ & /self%cosmologyFunctions_%matterDensityEpochal(self%time) &
+ & )**(1.0d0/3.0d0) &
+ & *( &
+ & +self%expansionFactorHRTAScaled(i) &
+ & /self%expansionFactorScaled &
+ & )**(growthIndex/3.0d0) &
+ & *self%radiusScaled(i) &
+ & /self%expansionFactorScaled
+ densitySingleStreamAnalyticPhysical (i-iVirial)=+self%cosmologyFunctions_%matterDensityEpochal(self%time) &
+ & *( &
+ & +self%expansionFactorScaled &
+ & /self%radiusScaled (i) &
+ & )**3 &
+ & /( &
+ & +1.0d0 &
+ & +3.0d0 &
+ & /growthIndex &
+ & /(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled%derivative(self%overdensityScaled(i))/self%expansionFactorHRTAScaled(i)) &
+ & *(self%interpolatorRadiusScaled %derivative(self%overdensityScaled(i))/self%radiusScaled (i)) &
+ & )
+ velocitySingleStreamAnalyticPhysical(i-iVirial)=+self%radiusGrowthRateScaled (i ) &
+ & * radiusSingleStreamAnalyticPhysical(i-iVirial) &
+ & /self%radiusScaled (i ) &
+ & *sqrt(1.0d0-self%cosmologyFunctions_%OmegaMatterEpochal (self%time)) &
+ & * self%cosmologyFunctions_%HubbleParameterEpochal(self%time)
+ end do
+ ! Check that the numerical solution matches the analytic solution in the single stream region.
+ radiusSplashbackPhysical=+self%radiusSplashBackOriginal &
+ & *self%radiusVirial &
+ & / radiusVirialOriginal
+ do i=iVirial+1,countRadii-1
+ if ( &
+ & radiusPhysical(i) > radiusSplashBackPhysical &
+ & .and. &
+ & Values_Differ( &
+ & radiusSingleStreamAnalyticPhysical(i-iVirial)/radiusSingleStreamAnalyticPhysical(iTurnaround-iVirial), &
+ & radiusPhysical (i )/radiusPhysical (iTurnaround ), &
+ & relTol=1.0d-6 &
+ & ) &
+ & ) &
+ & call Error_Report('numerical and analytic solutions disagree in single stream region'//{introspection:location})
+ end do
+ return
+ end subroutine shi2016Solve
+
+ double precision function halfRadiusTurnAroundRoot(timeFinalScaled)
+ !!{
+ Root function used in finding the epoch at which a shell reaches a radius equal to half of its turnaround radius, $y^*$.
+ !!}
+ implicit none
+ double precision, intent(in ) :: timeFinalScaled
+ double precision :: radiusScaled , radiusGrowthRateScaled
+
+ ! Integrate the dynamical equations governing the evolution of the shell radius starting from the turnaround time, I⋆, at
+ ! which the radius is y⋆, and the rate of change of radius is zero (by definition).
+ noShellCrossing=.true.
+ call radiusScaledSolver(timeTurnaroundScaled__,timeFinalScaled,radiusTurnaroundScaled__,0.0d0,radiusScaled,radiusGrowthRateScaled)
+ noShellCrossing=.false.
+ halfRadiusTurnAroundRoot=+ radiusScaled &
+ & - radiusTurnaroundScaled__ &
+ & /self_%ratioRadiusTurnaroundVirial
+ return
+ end function halfRadiusTurnAroundRoot
+
+ subroutine radiusScaledSolver(timeInitialScaled,timeFinalScaled,radiusInitialScaled,radiusGrowthRateInitialScaled,radiusScaled,radiusGrowthRateScaled)
+ !!{
+ Compute the scaled radius (and its growth rate) as a function of the initial state and final time.
+ !!}
+ use :: Interface_GSL , only : GSL_Success
+ use :: Numerical_ODE_Solvers, only : odeSolver
+ implicit none
+ double precision , intent(in ) :: timeInitialScaled , timeFinalScaled , &
+ & radiusInitialScaled , radiusGrowthRateInitialScaled
+ double precision , intent( out) :: radiusScaled , radiusGrowthRateScaled
+ double precision , dimension(2) :: odeVariables
+ double precision , parameter :: odeToleranceAbsolute=0.0d0, odeToleranceRelative =1.0d-9
+ type (odeSolver) :: solver
+ double precision :: timeScaled
+
+ solver =odeSolver(2_c_size_t,dynamicalODEs,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative)
+ odeVariables=[radiusInitialScaled,radiusGrowthRateInitialScaled]
+ timeScaled = timeInitialScaled
+ call solver%solve(timeScaled,timeFinalScaled,odeVariables)
+ radiusScaled =odeVariables(1)
+ radiusGrowthRateScaled=odeVariables(2)
+ return
+ end subroutine radiusScaledSolver
+
+ integer function dynamicalODES(timeScaled,odeVariables,odeVariablesGrowthRate)
+ !!{
+ The dynamical equation describing the motion of a shell of matter in scaled variables \citep[][eqn.~A7]{shi_outer_2016}.
+ !!}
+ use :: Interface_GSL, only : GSL_Success
+ implicit none
+ double precision , intent(in ) :: timeScaled
+ double precision, dimension(:), intent(in ) :: odeVariables
+ double precision, dimension(:), intent( out) :: odeVariablesGrowthRate
+ double precision :: radiusScaled , radiusGrowthRateScaled, &
+ & radiusHRTA , radiusSplashback , &
+ & radius , massHRTA , &
+ & massEnclosedRatio
+ !$GLC attributes unused :: timeScaled
+
+ ! Extract ODE variables into named variables for clarity.
+ radiusScaled =odeVariables(1)
+ radiusGrowthRateScaled=odeVariables(2)
+ ! Determine the ratio of the mass enclosed within the current radius to the mass enclosed within the shell at the initial
+ ! time.
+ if (noShellCrossing .or. timeScaled < self_%timeHRTAScaled(1)) then
+ ! If shell crossing is being ignored, or if the current time is less than the earliest time for which we have the
+ ! half-turnaround radius tabulated, simply assume a mass ratio of unity.
+ massEnclosedRatio=1.0d0
+ else
+ ! Our solution for the mass in the multistream regime is the self-similar solution expressed in units of the radius of the
+ ! shell at its half-turnaround radius, and the mass within that shell (assuming no shell crossing). Compute that radius and
+ ! mass at the present epoch.
+ radiusHRTA=self_%interpolatorRadiusHRTAOriginal%interpolate(timeScaled)
+ massHRTA =self_%interpolatorMassHRTAOriginal %interpolate(timeScaled)
+ ! Find the current splashback radius by scaling the ratio of splashback to half-turnaround radius to the current epoch.
+ radiusSplashback=+self_%ratioRadiusSplashbackHRTA &
+ & * radiusHRTA
+ ! Find the radius of the current shell in unscaled units.
+ radius=abs(radiusScaled)*radiusComovingInitialOriginal/self_%cosmologicalConstantScaled**(1.0d0/3.0d0)
+ ! Determine where in the stream our shell is.
+ if (radius < radiusSplashback) then
+ ! The shell is within the splashback radius.
+ if (radius > self_%radiusMultistreamMinimumScaledHRTA*radiusHRTA) then
+ ! Shell is outside the minimum radius that we have tabulated for the multistream region. Therefore, simply
+ ! interpolate to get the mass ratio.
+ massEnclosedRatio=+massHRTA &
+ & /massEnclosedInitialOriginal &
+ & *self_%interpolatorMassMultiStreamScaleHRTA%interpolate(radius/radiusHRTA)
+ else
+ ! Shell is inside the minimum radius that we have tabulated for the multistream region. In this region we assume that
+ ! the mass enclosed by the innermost tabulated point is distributed with uniform density. The mass enclosed therefore
+ ! grows as the cube of radius.
+ massEnclosedRatio=+massHRTA &
+ & /massEnclosedInitialOriginal &
+ & *self_%interpolatorMassMultiStreamScaleHRTA%interpolate(self_%radiusMultistreamMinimumScaledHRTA) &
+ & *(radius/radiusHRTA/self_%radiusMultistreamMinimumScaledHRTA)**3
+ end if
+ else
+ ! Mass is outside the splashback radius, so no shell crossing has occurred. The mass ratio is therefore unity.
+ massEnclosedRatio=1.0d0
+ end if
+ end if
+ ! Set ODE rates of change.
+ !! Radius rate of change is just the velocity.
+ odeVariablesGrowthRate(1)=+radiusGrowthRateScaled
+ !! Velocity rate of change is given by equation (A7) of Shi (2016).
+ if (radiusScaled == 0.0d0) then
+ odeVariablesGrowthRate(2)=+0.0d0
+ else
+ odeVariablesGrowthRate(2)=-0.5d0*massEnclosedRatio*sign(1.0d0,radiusScaled)/radiusScaled**2 &
+ & + radiusScaled
+ end if
+ dynamicalODES=GSL_Success
+ return
+ end function dynamicalODES
+
+ elemental double precision function expansionFactorFromTimeScaled(timeScaled) result(expansionFactor)
+ !!{
+ Compute the scaled expansion factor from the scaled time using equation~(A6) of \cite{shi_outer_2016}.
+ !!}
+ implicit none
+ double precision, intent(in ) :: timeScaled
+
+ expansionFactor=sinh(1.5d0*timeScaled)**(2.0d0/3.0d0)
+ return
+ end function expansionFactorFromTimeScaled
+
diff --git a/source/mass_distributions.spherical.accretion_flow.correlation_function.F90 b/source/mass_distributions.spherical.accretion_flow.correlation_function.F90
new file mode 100644
index 0000000000..1f3e0abcf7
--- /dev/null
+++ b/source/mass_distributions.spherical.accretion_flow.correlation_function.F90
@@ -0,0 +1,184 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a mass distribution for accretion flow using the 2-halo correlation function.
+ !!}
+
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+
+ An accretion flow class which models the accretion flow using the 2-halo correlation function.
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionCorrelationFunction
+ !!{
+ A mass distribution for accretion flow using the 2-halo correlation function.
+ !!}
+ private
+ class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
+ type (interpolator ) :: correlationFunction_
+ double precision :: mass , time , &
+ & redshift
+ double precision , allocatable, dimension(:) :: radius , correlationFunction
+ contains
+ procedure :: density => correlationFunctionDensity
+ procedure :: densityGradientRadial => correlationFunctionDensityGradientRadial
+ end type massDistributionCorrelationFunction
+
+ interface massDistributionCorrelationFunction
+ !!{
+ Constructors for the {\normalfont \ttfamily correlationFunction} mass distribution class.
+ !!}
+ module procedure massDistributionCorrelationFunctionConstructorParameters
+ module procedure massDistributionCorrelationFunctionConstructorInternal
+ end interface massDistributionCorrelationFunction
+
+contains
+
+ function massDistributionCorrelationFunctionConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily correlationFunction} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ use :: Numerical_Interpolation , only : interpolator
+ implicit none
+ type (massDistributionCorrelationFunction) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ double precision , allocatable, dimension(:) :: radius , correlationFunction
+ double precision :: mass , redshift
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ mass
+ The mass of the halo.
+
+
+
+ redshift
+ The redshift of the halo.
+
+
+
+ radius
+ The radius in the tabulated correlation function.
+
+
+
+ correlationFunction
+ The correlation in the tabulated correlation function.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ self=massDistributionCorrelationFunction(mass,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),radius,correlationFunction,cosmologyFunctions_,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ !![
+
+
+ !!]
+ return
+ end function massDistributionCorrelationFunctionConstructorParameters
+
+ function massDistributionCorrelationFunctionConstructorInternal(mass,time,radius,correlationFunction,cosmologyFunctions_,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``correlationFunction'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionCorrelationFunction) :: self
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ double precision , intent(in ) :: mass , time
+ double precision , intent(in ), dimension(:) :: radius , correlationFunction
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%dimensionless =.false.
+ self%redshift =self%cosmologyFunctions_%redshiftFromExpansionFactor(self%cosmologyFunctions_%expansionFactor(time))
+ self%correlationFunction_=interpolator(radius,correlationFunction)
+ return
+ end function massDistributionCorrelationFunctionConstructorInternal
+
+ subroutine correlationFunctionDestructor(self)
+ !!{
+ Destructor for the {\normalfont \ttfamily correlationFunction} accretion flow mass distribution class.
+ !!}
+ implicit none
+ type(massDistributionCorrelationFunction), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine correlationFunctionDestructor
+
+ double precision function correlationFunctionDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a accretion flow modeled on the 2-halo correlation function.
+ !!}
+ implicit none
+ class(massDistributionCorrelationFunction), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ density=+( &
+ & +1.0d0 &
+ & +self%correlationFunction_%interpolate (coordinates%rSpherical()) &
+ & ) &
+ & * self%cosmologyFunctions_ %matterDensityEpochal(self %time )
+ return
+ end function correlationFunctionDensity
+
+ double precision function correlationFunctionDensityGradientRadial(self,coordinates,logarithmic) result(densityGradientRadial)
+ !!{
+ Return the radial density gradient at the specified {\normalfont \ttfamily coordinates} in a accretion flow modeled on the 2-halo correlation function.
+ !!}
+ implicit none
+ class (massDistributionCorrelationFunction), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ densityGradientRadial=+self%correlationFunction_%derivative (coordinates%rSpherical()) &
+ & *self%cosmologyFunctions_ %matterDensityEpochal(self %time )
+ return
+ end function correlationFunctionDensityGradientRadial
diff --git a/source/mass_distributions.spherical.adiabatic_Gnedin2004.F90 b/source/mass_distributions.spherical.adiabatic_Gnedin2004.F90
new file mode 100644
index 0000000000..84b4ee5443
--- /dev/null
+++ b/source/mass_distributions.spherical.adiabatic_Gnedin2004.F90
@@ -0,0 +1,698 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements an adiabatically-contracted spherical mass distribution.
+ !!}
+
+ use :: Math_Exponentiation, only : fastExponentiator
+ use :: Root_Finder , only : rootFinder
+
+ public :: sphericalAdiabaticGnedin2004Initializor
+
+ ! Number of previous radius solutions to store.
+ !![
+
+
+
+ !!]
+ integer, parameter :: sphericalAdiabaticGnedin2004StoreCount=10
+
+ !![
+
+
+ A dark matter profile class which applies adiabatic contraction to the halo as it responds to the presence of
+ baryons. Adiabatic contraction follows the algorithm of \cite{gnedin_response_2004}. The parameters $A$ and $\omega$ of
+ that model are specified via input parameters {\normalfont \ttfamily A} and {\normalfont \ttfamily omega} respectively.
+
+ Given the final radius, $r_\mathrm{f}$, the corresponding initial radius, $r_\mathrm{i}$, is found by solving:
+ \begin{equation}
+ f_\mathrm{i} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) r_\mathrm{i} = f_\mathrm{f} M_\mathrm{total,0}(\bar{r}_\mathrm{i})
+ r_\mathrm{f} + V^2_\mathrm{b}(\bar{r}_\mathrm{f}) \bar{r}_\mathrm{f} r_\mathrm{f}/ \mathrm{G},
+ \label{eq:adiabaticContractionGnedinSolution}
+ \end{equation}
+ where $M_\mathrm{total,0}(r)$ is the initial total matter profile, $V_\mathrm{b}(r)$ is the baryonic contribution to the
+ rotation curve, $f_\mathrm{i}$, is the fraction of mass within the virial radius compared to the node mass\footnote{In
+ \protect\glc\ the ``node mass'' refers to the total mass of the node, assuming it has the universal complement of
+ baryons. Since some halos may contain less than the complete complement of baryons it is possible that $f_\mathrm{i}<1$.},
+ $f_\mathrm{f}=(\Omega_\mathrm{M}-\Omega_\mathrm{b})/\Omega_\mathrm{M}+M_\mathrm{satellite, baryonic}/M_\mathrm{total}$,
+ $M_\mathrm{satellite, baryonic}$ is the baryonic mass in any satellite halos, $M_\mathrm{total}$ is the node mass, and
+ \begin{equation}
+ {\bar{r} \over r_0} = A \left({r \over r_0}\right)^\omega,
+ \label{eq:adiabaticContractionGnedinPowerLaw}
+ \end{equation}
+ where the pivot radius $r_0$ is set to $f_0 r_\mathrm{vir}$ where $f_0=${\normalfont \ttfamily [radiusFractionalPivot]}, and
+ $r_\mathrm{vir}$ is the virial radius. The original \cite{gnedin_response_2004} assumed $f_0=1$, but the revised model of
+ \cite{gnedin_halo_2011} found that $f_0=0.03$ lead to an improved model (less scatter in the best fit values of $(A,\omega)$
+ when comparing to N-body simulations).
+
+ Note that we explicitly assume that the initial, uncontracted total density profile has the same shape as the initial dark
+ matter density profile, that contraction of the halo occurs with no shell crossing, and that satellite halos trace the dark
+ matter profile of their host halo. The derivative, $\mathrm{d} r_\mathrm{f}/\mathrm{d}d_\mathrm{i}\equiv r^\prime_\mathrm{i}$
+ is found by taking the derivative of eqn.~(\ref{eq:adiabaticContractionGnedinSolution}) to give:
+ \begin{eqnarray}
+ & & f_\mathrm{i} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) r^\prime_\mathrm{i} + f_\mathrm{i} 4 \pi
+ \bar{r}_\mathrm{i}^2 \rho_\mathrm{total,0}(\bar{r}_\mathrm{i}) {\mathrm{d} \bar{r}_\mathrm{i}\over\mathrm{d} r_\mathrm{i}}
+ r_\mathrm{i} r^\prime_\mathrm{i} \nonumber \\
+ & = & f_\mathrm{f} M_\mathrm{total,0}(\bar{r}_\mathrm{i}) + f_\mathrm{i} 4 \pi \bar{r}_\mathrm{i}^2
+ \rho_\mathrm{total,0}(\bar{r}_\mathrm{i}) {\mathrm{d} \bar{r}_\mathrm{i}\over\mathrm{d} r_\mathrm{i}} r_\mathrm{f}
+ r^\prime_\mathrm{i} \nonumber \\
+ & + & V^2_\mathrm{b}(\bar{r}_\mathrm{f}) \bar{r}_\mathrm{f} / \mathrm{G} + V^2_\mathrm{b}(\bar{r}_\mathrm{f})
+ {\mathrm{d}\bar{r}_\mathrm{f}\over \mathrm{d} r_\mathrm{f}} r_\mathrm{f}/ \mathrm{G} +
+ {\mathrm{d}V^2_\mathrm{b}\over\mathrm{d} \bar{r}_\mathrm{f}}(\bar{r}_\mathrm{f}) {\mathrm{d}\bar{r}_\mathrm{f}\over
+ \mathrm{d} r_\mathrm{f}} \bar{r}_\mathrm{f} r_\mathrm{f}/ \mathrm{G},
+ \end{eqnarray}
+ where
+ \begin{equation}
+ {\mathrm{d}\bar{r} \over \mathrm{d} r} = A \left({r \over r_0}\right)^{\omega-1},
+ \end{equation}
+ and which can then be solved numerically for $r^\prime_\mathrm{i}$.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalAdiabaticGnedin2004
+ !!{
+ An adiabatically-contracted spherical mass distribution.
+ !!}
+ private
+ class (massDistributionClass ), pointer :: massDistributionBaryonic => null()
+ type (rootFinder ) :: finder
+ ! Parameters of the adiabatic contraction algorithm.
+ double precision :: A , omega , &
+ & radiusFractionalPivot
+ ! Stored solutions for reuse.
+ integer :: radiusPreviousIndex , radiusPreviousIndexMaximum
+ double precision , dimension(sphericalAdiabaticGnedin2004StoreCount) :: radiusPrevious , radiusInitialPrevious
+ ! Quantities used in solving the initial radius root function.
+ double precision :: baryonicFinalTerm , baryonicFinalTermDerivative , &
+ & darkMatterDistributedFraction , massFractionInitial , &
+ & radiusFinal , radiusFinalMean , &
+ & darkMatterFraction , radiusVirial , &
+ & toleranceRelative , massTotal_
+ ! Call-back function and arguments used for as-needed initialization of the baryonic component.
+ logical :: initialized
+ procedure (sphericalAdiabaticGnedin2004Initializor), pointer , nopass :: initializationFunction
+ class (* ), pointer :: initializationSelf => null(), initializationArgument => null()
+ contains
+ !![
+
+
+
+
+
+
+
+
+ !!]
+ final :: sphericalAdiabaticGnedin2004Destructor
+ procedure :: setBaryonicComponent => sphericalAdiabaticGnedin2004SetBaryonicComponent
+ procedure :: density => sphericalAdiabaticGnedin2004Density
+ procedure :: massEnclosedBySphere => sphericalAdiabaticGnedin2004MassEnclosedBySphere
+ procedure :: radiusInitial => sphericalAdiabaticGnedin2004RadiusInitial
+ procedure :: radiusInitialDerivative => sphericalAdiabaticGnedin2004RadiusInitialDerivative
+ procedure :: computeFactors => sphericalAdiabaticGnedin2004ComputeFactors
+ procedure :: radiusOrbitalMean => sphericalAdiabaticGnedin2004RadiusOrbitalMean
+ procedure :: radiusOrbitalMeanDerivative => sphericalAdiabaticGnedin2004RadiusOrbitalMeanDerivative
+ end type massDistributionSphericalAdiabaticGnedin2004
+
+ interface massDistributionSphericalAdiabaticGnedin2004
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalAdiabaticGnedin2004} mass distribution class.
+ !!}
+ module procedure sphericalAdiabaticGnedin2004ConstructorParameters
+ module procedure sphericalAdiabaticGnedin2004ConstructorInternal
+ end interface massDistributionSphericalAdiabaticGnedin2004
+
+ ! Module-scope quantities used in solving the initial radius root function.
+ double precision , parameter :: toleranceAbsolute =0.0d0
+ class (massDistributionSphericalAdiabaticGnedin2004), pointer :: self_
+ !$omp threadprivate(self_)
+
+ ! Module-scope shared fast exponentiator.
+ type (fastExponentiator ), allocatable :: radiusExponentiator
+ double precision :: omegaPrevious =-huge(0.0d0)
+ !$omp threadprivate(radiusExponentiator,omegaPrevious)
+
+ abstract interface
+ subroutine sphericalAdiabaticGnedin2004Initializor(initializationSelf,initializationArgument,massDistributionBaryonic,darkMatterDistributedFraction,massFractionInitial)
+ !!{
+ Interface for call-back functions for as-needed initialization of the baryonic component.
+ !!}
+ import massDistributionClass
+ class (* ), intent(inout), target :: initializationSelf , initializationArgument
+ class (massDistributionClass), intent( out), pointer :: massDistributionBaryonic
+ double precision , intent( out) :: darkMatterDistributedFraction, massFractionInitial
+ end subroutine sphericalAdiabaticGnedin2004Initializor
+ end interface
+
+contains
+
+ function sphericalAdiabaticGnedin2004ConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalAdiabaticGnedin2004} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalAdiabaticGnedin2004) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionBaryonic
+ procedure (sphericalAdiabaticGnedin2004Initializor ), pointer :: initializationFunction
+ class (* ), pointer :: initializationSelf , initializationArgument
+ double precision :: A , omega , &
+ & radiusFractionalPivot , toleranceRelative , &
+ & radiusVirial , darkMatterFraction , &
+ & darkMatterDistributedFraction, massFractionInitial
+ type (varying_string ) :: componentType , massType , &
+ & nonAnalyticSolver
+
+ !![
+
+ A
+ (\citealt{gustafsson_baryonic_2006}; from their Fig. 9, strong feedback case)
+ 0.80d0
+ The parameter $A$ appearing in the \cite{gnedin_response_2004} adiabatic contraction algorithm.
+
+
+
+ omega
+ (\citealt{gustafsson_baryonic_2006}; from their Fig. 9, strong feedback case)
+ 0.77d0
+ The parameter $\omega$ appearing in the \cite{gnedin_response_2004} adiabatic contraction algorithm.
+
+
+
+ radiusFractionalPivot
+ \citep{gnedin_response_2004}
+ 1.0d0
+ The pivot radius (in units of the virial radius), $r_0$, appearing in equation~(\ref{eq:adiabaticContractionGnedinPowerLaw}).
+
+
+
+ radiusVirial
+ The virial radius, $r_\mathrm{v}$, appearing in equation~(\ref{eq:adiabaticContractionGnedinPowerLaw}).
+
+
+
+ darkMatterFraction
+ The universal dark matter fraction.
+
+
+
+ darkMatterDistributedFraction
+ The fraction of matter assumed to be distributed as the dark matter.
+
+
+
+ massFractionInitial
+ The fraction of matter assumed to be initially distributed as the dark matter.
+
+
+
+ toleranceRelative
+ 1.0d-2
+
+ The relative tolerance to use in solving for the initial radius in the adiabatically-contracted dark matter profile.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ initializationFunction => null()
+ initializationSelf => null()
+ initializationArgument => null()
+ self=massDistributionSphericalAdiabaticGnedin2004(A,omega,radiusVirial,radiusFractionalPivot,darkMatterFraction,darkMatterDistributedFraction,massFractionInitial,toleranceRelative,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,massDistributionBaryonic,initializationFunction,initializationSelf,initializationArgument,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalAdiabaticGnedin2004ConstructorParameters
+
+ function sphericalAdiabaticGnedin2004ConstructorInternal(A,omega,radiusVirial,radiusFractionalPivot,darkMatterFraction,darkMatterDistributedFraction,massFractionInitial,toleranceRelative,nonAnalyticSolver,massDistribution_,massDistributionBaryonic,initializationFunction,initializationSelf,initializationArgument,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalAdiabaticGnedin2004'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalAdiabaticGnedin2004) :: self
+ double precision , intent(in ) :: A , omega , &
+ & radiusVirial , radiusFractionalPivot , &
+ & darkMatterFraction , darkMatterDistributedFraction, &
+ & massFractionInitial , toleranceRelative
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ class (massDistributionClass ), intent(in ), target :: massDistributionBaryonic
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ procedure (sphericalAdiabaticGnedin2004Initializor ), intent(in ), pointer :: initializationFunction
+ class (* ), intent(in ), pointer :: initializationSelf , initializationArgument
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Validate.
+ if (.not.enumerationNonAnalyticSolversIsValid(nonAnalyticSolver)) call Error_Report('invalid non-analytic solver type'//{introspection:location})
+ ! Evaluate the original total mass.
+ self%massTotal_=self%massDistribution_%massEnclosedBySphere(radiusVirial)
+ ! Construct a root finder.
+ self%finder=rootFinder( &
+ & rootFunction =sphericalAdiabaticGnedin2004Solver, &
+ & toleranceAbsolute=toleranceAbsolute , &
+ & toleranceRelative=toleranceRelative &
+ & )
+ self%dimensionless=self%massDistribution_%isDimensionless()
+ ! Initialize state.
+ self%radiusPreviousIndex = 0
+ self%radiusPreviousIndexMaximum= 0
+ self%radiusPrevious =-1.0d0
+ self%initialized =.not.associated(initializationFunction)
+ return
+ end function sphericalAdiabaticGnedin2004ConstructorInternal
+
+ subroutine sphericalAdiabaticGnedin2004Destructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalAdiabaticGnedin2004} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalAdiabaticGnedin2004Destructor
+
+ subroutine sphericalAdiabaticGnedin2004SetBaryonicComponent(self)
+ !!{
+ Set the baryonic component properties in an adiabatically-contracted spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ class (massDistributionClass ), pointer :: massDistributionBaryonic
+ double precision :: darkMatterDistributedFraction, massFractionInitial
+
+ if (.not.self%initialized) then
+ call self%initializationFunction(self%initializationSelf,self%initializationArgument,massDistributionBaryonic,darkMatterDistributedFraction,massFractionInitial)
+ self%massDistributionBaryonic => massDistributionBaryonic
+ self%darkMatterDistributedFraction = darkMatterDistributedFraction
+ self%massFractionInitial = massFractionInitial
+ self%initialized = .true.
+ end if
+ return
+ end subroutine sphericalAdiabaticGnedin2004SetBaryonicComponent
+
+ double precision function sphericalAdiabaticGnedin2004Density(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an adiabatically-contracted spherical mass distribution.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: coordinatesInitial
+ double precision :: radiusInitial , radiusInitialDerivative, &
+ & densityInitial
+
+ radiusInitial =self %radiusInitial(coordinates %rSpherical())
+ coordinatesInitial=[radiusInitial,0.0d0,0.0d0]
+ densityInitial =self%massDistribution_%density (coordinatesInitial )
+ if (coordinates%rSpherical() == radiusInitial) then
+ density=+self%darkMatterFraction &
+ & * densityInitial
+ else
+ radiusInitialDerivative=+self%radiusInitialDerivative(coordinates%rSpherical())
+ density =+self%darkMatterFraction &
+ & *densityInitial &
+ & *( &
+ & + radiusInitial &
+ & /coordinates%rSpherical () &
+ & )**2 &
+ & *radiusInitialDerivative
+ end if
+ return
+ end function sphericalAdiabaticGnedin2004Density
+
+ double precision function sphericalAdiabaticGnedin2004MassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for adiabatically-contracted mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ mass =+self %darkMatterFraction &
+ & *self%massDistribution_%massEnclosedBySphere(self%radiusInitial(radius))
+ return
+ end function sphericalAdiabaticGnedin2004MassEnclosedBySphere
+
+ double precision function sphericalAdiabaticGnedin2004RadiusInitial(self,radius)
+ !!{
+ Compute the initial radius in the dark matter halo using the adiabatic contraction algorithm of
+ \cite{gnedin_response_2004}.
+ !!}
+ use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ integer :: i , j , &
+ & iMod
+ double precision :: radiusUpperBound, massEnclosed
+
+ call self%setBaryonicComponent()
+ ! Handle non-positive radii.
+ if (radius <= 0.0d0) then
+ sphericalAdiabaticGnedin2004RadiusInitial=0.0d0
+ return
+ end if
+ ! Check for a previously computed solution.
+ if (self%radiusPreviousIndexMaximum > 0 .and. any(self%radiusPrevious(1:self%radiusPreviousIndexMaximum) == radius)) then
+ sphericalAdiabaticGnedin2004RadiusInitial=0.0d0
+ do i=1,self%radiusPreviousIndexMaximum
+ if (self%radiusPrevious(i) == radius) then
+ sphericalAdiabaticGnedin2004RadiusInitial=self%radiusInitialPrevious(i)
+ exit
+ end if
+ end do
+ return
+ end if
+ ! Return radius unchanged if larger than the virial radius.
+ if (radius >= self%radiusVirial) then
+ sphericalAdiabaticGnedin2004RadiusInitial=radius
+ return
+ end if
+ ! Compute the various factors needed by this calculation.
+ call self%computeFactors(radius,computeGradientFactors=.false.)
+ !! Note that even if no baryons are present at this radius we can not assume that the initial radius is unchanged because it
+ !! is possible that the initial fraction of baryons and the fraction of mass distributed as the dark matter are not equal, fᵢ
+ !! ≠ fᵪ.
+ ! Check that solution is within bounds.
+ if (sphericalAdiabaticGnedin2004Solver(self%radiusVirial) < 0.0d0) then
+ sphericalAdiabaticGnedin2004RadiusInitial=self%radiusVirial
+ return
+ end if
+ j=-1
+ if (self%radiusPreviousIndexMaximum > 0) then
+ ! No exact match exists, look for approximate matches.
+ do i=1,self%radiusPreviousIndexMaximum
+ iMod=modulo(self%radiusPreviousIndex-i,sphericalAdiabaticGnedin2004StoreCount)+1
+ if (abs(radius-self%radiusPrevious(iMod))/self%radiusPrevious(iMod) < self%toleranceRelative) then
+ j=iMod
+ exit
+ end if
+ end do
+ end if
+ ! Find the solution for initial radius.
+ if (j == -1) then
+ ! No previous solution to use as an initial guess. Instead, we make an estimate of the initial radius under the
+ ! assumption that the mass of dark matter (in the initial profile) enclosed within the mean initial radius is the
+ ! same as enclosed within the mean final radius. Since the initial and final radii are typically not too
+ ! different, and since the mean radius is a weak (ѡ<1) function of the radius this is a useful
+ ! approximation. Furthermore, since it will underestimate the actual mass within the initial mean radius it gives
+ ! an overestimate of the initial radius. This means that we have a bracketing of the initial radius which we can
+ ! use in the solver.
+ massEnclosed=+self%massDistribution_%massEnclosedBySphere(self%radiusOrbitalMean(self%radiusFinal))
+ if (massEnclosed > 0.0d0) then
+ radiusUpperBound=+( &
+ & +self%baryonicFinalTerm &
+ & / massEnclosed &
+ & +self%darkMatterDistributedFraction &
+ & *self%radiusFinal &
+ & ) &
+ & / self%massFractionInitial
+ if (radiusUpperBound < radius) radiusUpperBound=radius
+ else
+ radiusUpperBound=radius
+ end if
+ call self%finder%rangeExpand( &
+ & rangeExpandUpward =1.1d0 , &
+ & rangeExpandDownward =0.9d0 , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ sphericalAdiabaticGnedin2004RadiusInitial=self%finder%find(rootRange=[radius,radiusUpperBound])
+ else
+ ! Use previous solution as an initial guess.
+ call self%finder%rangeExpand( &
+ & rangeExpandDownward =1.0d0/sqrt(1.0d0+self%toleranceRelative), &
+ & rangeExpandUpward =1.0d0*sqrt(1.0d0+self%toleranceRelative), &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive , &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ sphericalAdiabaticGnedin2004RadiusInitial=self%finder%find( &
+ & rootRange=[ &
+ & self%radiusInitialPrevious(j)/sqrt(1.0d0+self%toleranceRelative), &
+ & self%radiusInitialPrevious(j)*sqrt(1.0d0+self%toleranceRelative) &
+ & ] &
+ & )
+ end if
+ ! Store this solution.
+ self%radiusPreviousIndex =modulo(self%radiusPreviousIndex ,sphericalAdiabaticGnedin2004StoreCount)+1
+ self%radiusPreviousIndexMaximum =min (self%radiusPreviousIndexMaximum+1,sphericalAdiabaticGnedin2004StoreCount)
+ self%radiusPrevious (self%radiusPreviousIndex)=radius
+ self%radiusInitialPrevious (self%radiusPreviousIndex)=sphericalAdiabaticGnedin2004RadiusInitial
+ return
+ end function sphericalAdiabaticGnedin2004RadiusInitial
+
+ double precision function sphericalAdiabaticGnedin2004RadiusInitialDerivative(self,radius) result(radiusInitialDerivative)
+ !!{
+ Compute the derivative of the initial radius in the dark matter halo using the adiabatic contraction algorithm of
+ \cite{gnedin_response_2004}.
+ !!}
+ use :: Display , only : displayMessage , displayIndent, displayUnindent
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Coordinates , only : coordinateSpherical, assignment(=)
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ type (varying_string ), save :: message
+ !$omp threadprivate(message)
+ character (len=12 ) :: label
+ double precision :: radiusInitial , radiusInitialMean , &
+ & massDarkMatterInitial , densityDarkMatterInitial , &
+ & radiusInitialMeanSelfDerivative, radiusFinalMeanSelfDerivative, &
+ & numerator , denominator
+ type (coordinateSpherical ) :: coordinatesInitialMean
+
+ call self%setBaryonicComponent()
+ ! Compute the various factors needed by this calculation.
+ call self%computeFactors(radius,computeGradientFactors=.true.)
+ ! Return unit derivative if radius is larger than the virial radius.
+ if (radius >= self%radiusVirial) then
+ radiusInitialDerivative=1.0d0
+ return
+ end if
+ ! Validate.
+ if (radius <= 0.0d0) call Error_Report('non-positive radius')
+ ! Compute initial radius, and derivatives of initial and final mean radii.
+ radiusInitial =self %radiusInitial (radius )
+ radiusInitialMeanSelfDerivative=self %radiusOrbitalMeanDerivative(radiusInitial )
+ radiusFinalMeanSelfDerivative =self %radiusOrbitalMeanDerivative(radius )
+ ! Find the initial mean orbital radius.
+ radiusInitialMean =self %radiusOrbitalMean (radiusInitial )
+ coordinatesInitialMean =[radiusInitialMean,0.0d0,0.0d0]
+ ! Get the mass of dark matter inside the initial radius.
+ massDarkMatterInitial =self%massDistribution_%massEnclosedBySphere (radiusInitialMean )
+ ! Get the mass of dark matter inside the initial radius.
+ densityDarkMatterInitial =self%massDistribution_%density (coordinatesInitialMean)
+ ! Find the solution for the derivative of the initial radius.
+ numerator =+( &
+ & +massDarkMatterInitial &
+ & *self%darkMatterDistributedFraction &
+ & +self%baryonicFinalTerm &
+ & *( &
+ & +1.0d0 / radius &
+ & +radiusFinalMeanSelfDerivative/self%radiusFinalMean &
+ & ) &
+ & +self%baryonicFinalTermDerivative &
+ & )
+ denominator =+( &
+ & +massDarkMatterInitial*self%massFractionInitial &
+ & +( &
+ & +self%massFractionInitial *radiusInitial &
+ & -self%darkMatterDistributedFraction*radius &
+ & ) &
+ & *4.0d0 &
+ & *Pi &
+ & *radiusInitialMean**2 &
+ & *densityDarkMatterInitial &
+ & *radiusInitialMeanSelfDerivative &
+ & )
+ if (exponent(numerator)-exponent(denominator) > maxExponent(0.0d0)) then
+ call displayIndent ('Radius derivative calculation')
+ write (label,'(e12.6)') radius
+ message='r_final = '//label//' Mpc'
+ call displayMessage (message)
+ write (label,'(e12.6)') radiusInitial
+ message='r_initial = '//label//' Mpc'
+ call displayMessage (message)
+ write (label,'(e12.6)') self%radiusFinalMean
+ message='⟨r_final⟩ = '//label//' Mpc'
+ call displayMessage (message)
+ write (label,'(e12.6)') radiusInitialMean
+ message='⟨r_initial⟩ = '//label//' Mpc'
+ call displayMessage (message)
+ write (label,'(e12.6)') radiusInitialMeanSelfDerivative
+ message='d⟨r_initial⟩/dr_initial = '//label
+ call displayMessage (message)
+ write (label,'(e12.6)') radiusFinalMeanSelfDerivative
+ message='d⟨r_final⟩/dr_final = '//label
+ call displayMessage (message)
+ write (label,'(e12.6)') massDarkMatterInitial
+ message='M_dark,initial = '//label//' M☉'
+ call displayMessage (message)
+ write (label,'(e12.6)') self%baryonicFinalTerm
+ message='M_baryonic,final = '//label//' M☉'
+ call displayMessage (message)
+ write (label,'(e12.6)') self%baryonicFinalTermDerivative
+ message='dM_braryonic,final/dr_initial = '//label//' M☉/Mpc'
+ call displayMessage (message)
+ call displayUnindent('' )
+ call Error_Report('Overflow in initial radius derivative calculation'//{introspection:location})
+ end if
+ radiusInitialDerivative=+numerator &
+ & /denominator
+ return
+ end function sphericalAdiabaticGnedin2004RadiusInitialDerivative
+
+ subroutine sphericalAdiabaticGnedin2004ComputeFactors(self,radius,computeGradientFactors)
+ !!{
+ Compute various factors needed when solving for the initial radius in the dark matter halo using the adiabatic contraction
+ algorithm of \cite{gnedin_response_2004}.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ logical , intent(in ) :: computeGradientFactors
+ double precision :: velocityCircularSquaredGradient, velocityCircularSquared
+
+ ! Set module-scope pointer to self.
+ self_ => self
+ ! Store the final radius and its orbit-averaged mean.
+ self%radiusFinal = radius
+ self%radiusFinalMean=self%radiusOrbitalMean(radius)
+ ! Compute the baryonic contribution to the rotation curve.
+ velocityCircularSquared=self%massDistributionBaryonic%rotationCurve(self%radiusFinalMean)**2
+ self%baryonicFinalTerm=velocityCircularSquared*self%radiusFinalMean*self%radiusFinal/gravitationalConstantGalacticus
+ ! Compute the baryonic contribution to the rotation curve.
+ if (computeGradientFactors) then
+ velocityCircularSquaredGradient =+self%massDistributionBaryonic%rotationCurveGradient(self%radiusFinalMean)
+ self%baryonicFinalTermDerivative=+ velocityCircularSquaredGradient &
+ & *self%radiusOrbitalMeanDerivative(self%radiusFinal) &
+ & *self%radiusFinalMean &
+ & *self%radiusFinal &
+ & / gravitationalConstantGalacticus
+ end if
+ return
+ end subroutine sphericalAdiabaticGnedin2004ComputeFactors
+
+ double precision function sphericalAdiabaticGnedin2004RadiusOrbitalMean(self,radius)
+ !!{
+ Returns the orbit averaged radius for dark matter corresponding the given {\normalfont \ttfamily radius} using the model of
+ \cite{gnedin_response_2004}.
+ !!}
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ if (self%omega /= omegaPrevious) then
+ if (allocated(radiusExponentiator)) deallocate(radiusExponentiator)
+ allocate(radiusExponentiator)
+ radiusExponentiator=fastExponentiator(1.0d-3,1.0d0,self%omega,1.0d4,.false.)
+ omegaPrevious = self%omega
+ end if
+ sphericalAdiabaticGnedin2004RadiusOrbitalMean=+self %A &
+ & *self %radiusFractionalPivot &
+ & *self %radiusVirial &
+ & *radiusExponentiator%exponentiate ( &
+ & + radius &
+ & /self%radiusFractionalPivot &
+ & /self%radiusVirial &
+ & )
+ return
+ end function sphericalAdiabaticGnedin2004RadiusOrbitalMean
+
+ double precision function sphericalAdiabaticGnedin2004RadiusOrbitalMeanDerivative(self,radius)
+ !!{
+ Returns the derivative of the orbit averaged radius for dark matter corresponding the given {\normalfont \ttfamily radius} using the model of
+ \cite{gnedin_response_2004}.
+ !!}
+ implicit none
+ class (massDistributionSphericalAdiabaticGnedin2004), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ sphericalAdiabaticGnedin2004RadiusOrbitalMeanDerivative=+self%A &
+ & *self%omega &
+ & *( &
+ & + radius &
+ & /self%radiusFractionalPivot &
+ & /self%radiusVirial &
+ & )**(self%omega-1.0d0)
+ return
+ end function sphericalAdiabaticGnedin2004RadiusOrbitalMeanDerivative
+
+ double precision function sphericalAdiabaticGnedin2004Solver(radiusInitial)
+ !!{
+ Root function used in finding the initial radius in the dark matter halo when solving for adiabatic contraction.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radiusInitial
+ double precision :: massDarkMatterInitial, radiusInitialMean
+
+ ! Find the initial mean orbital radius.
+ radiusInitialMean =self_ %radiusOrbitalMean(radiusInitial )
+ ! Get the mass of dark matter inside the initial radius.
+ massDarkMatterInitial=self_%massDistribution_%massEnclosedBySphere(radiusInitialMean)
+ ! Compute the root function.
+ sphericalAdiabaticGnedin2004Solver=+massDarkMatterInitial &
+ & *( &
+ & +self_%massFractionInitial* radiusInitial &
+ & -self_%darkMatterDistributedFraction *self_%radiusFinal &
+ & ) &
+ & -self_%baryonicFinalTerm
+ return
+ end function sphericalAdiabaticGnedin2004Solver
diff --git a/source/mass_distributions.spherical.beta_profile.F90 b/source/mass_distributions.spherical.beta_profile.F90
index f0bb7909d0..a45ec6a921 100644
--- a/source/mass_distributions.spherical.beta_profile.F90
+++ b/source/mass_distributions.spherical.beta_profile.F90
@@ -32,14 +32,15 @@
!!}
double precision :: beta , coreRadius , densityNormalization , &
& momentRadial2Previous , momentRadial3Previous, momentRadial2XPrevious, &
- & momentRadial3XPrevious
- logical :: betaIsTwoThirds
+ & momentRadial3XPrevious, outerRadius
+ logical :: betaIsTwoThirds , truncateAtOuterRadius
contains
procedure :: density => betaProfileDensity
procedure :: densityGradientRadial => betaProfileDensityGradientRadial
procedure :: densityRadialMoment => betaProfileDensityRadialMoment
procedure :: densitySquareIntegral => betaProfileDensitySquareIntegral
procedure :: massEnclosedBySphere => betaProfileMassEnclosedBySphere
+ procedure :: potentialIsAnalytic => betaProfilePotentialIsAnalytic
procedure :: potential => betaProfilePotential
procedure :: descriptor => betaProfileDescriptor
end type massDistributionBetaProfile
@@ -64,10 +65,10 @@ function betaProfileConstructorParameters(parameters) result(self)
implicit none
type (massDistributionBetaProfile) :: self
type (inputParameters ), intent(inout) :: parameters
- double precision :: beta , densityNormalization, &
- & mass , outerRadius , &
+ double precision :: beta , densityNormalization , &
+ & mass , outerRadius , &
& coreRadius
- logical :: dimensionless
+ logical :: dimensionless, truncateAtOuterRadius
type (varying_string ) :: componentType
type (varying_string ) :: massType
@@ -108,6 +109,12 @@ function betaProfileConstructorParameters(parameters) result(self)
If true then the $\beta$-model mass distribution is considered to be in dimensionless units.
+
+ truncateAtOuterRadius
+ .false.
+ If true then the $\beta$-model mass distribution is truncated beyond the outer radius.
+
+
componentType
var_str('unknown')
@@ -122,18 +129,19 @@ function betaProfileConstructorParameters(parameters) result(self)
self=massDistributionBetaProfile(beta,componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
-
-
-
-
-
+
+
+
+
+
+
!!]
return
end function betaProfileConstructorParameters
- function betaProfileConstructorInternal(beta,densityNormalization,mass,outerRadius,coreRadius,dimensionless,componentType,massType) result(self)
+ function betaProfileConstructorInternal(beta,densityNormalization,mass,outerRadius,coreRadius,dimensionless,truncateAtOuterRadius,componentType,massType) result(self)
!!{
Constructor for ``betaProfile'' convergence class.
!!}
@@ -148,7 +156,7 @@ function betaProfileConstructorInternal(beta,densityNormalization,mass,outerRadi
double precision , intent(in ) :: beta
double precision , intent(in ), optional :: densityNormalization , mass , &
& outerRadius , coreRadius
- logical , intent(in ), optional :: dimensionless
+ logical , intent(in ), optional :: dimensionless , truncateAtOuterRadius
type (enumerationComponentTypeType), intent(in ), optional :: componentType
type (enumerationMassTypeType ), intent(in ), optional :: massType
double precision , parameter :: radiusTiny =1.0d-6
@@ -186,7 +194,7 @@ function betaProfileConstructorInternal(beta,densityNormalization,mass,outerRadi
& present(densityNormalization) &
& ) then
self%densityNormalization=densityNormalization
- else if (&
+ else if ( &
& present(mass ).and. &
& present(outerRadius ) &
& ) then
@@ -230,82 +238,84 @@ function betaProfileConstructorInternal(beta,densityNormalization,mass,outerRadi
end if
end if
end if
+ ! Check for truncation.
+ if (present(truncateAtOuterRadius)) then
+ self%truncateAtOuterRadius=truncateAtOuterRadius
+ else
+ self%truncateAtOuterRadius=.false.
+ end if
+ if (self%truncateAtOuterRadius) then
+ if (.not.present(outerRadius)) call Error_Report('can not truncate profile without an outer radius'//{introspection:location})
+ self%outerRadius=outerRadius
+ end if
! Initialize stored results.
self%momentRadial2XPrevious=-1.0d0
self%momentRadial3XPrevious=-1.0d0
return
end function betaProfileConstructorInternal
- double precision function betaProfileDensity(self,coordinates,componentType,massType)
+ double precision function betaProfileDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a $\beta$-profile mass distribution.
!!}
- use :: Coordinates, only : assignment(=), coordinateSpherical
implicit none
- class (massDistributionBetaProfile ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision :: r
+ class (massDistributionBetaProfile ), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radius
- if (.not.self%matches(componentType,massType)) then
+ ! Compute density.
+ radius=coordinates%rSpherical()
+ if (self%truncateAtOuterRadius .and. radius > self%outerRadius) then
betaProfileDensity=0.0d0
- return
+ else
+ betaProfileDensity=self%densityNormalization/(1.0d0+(radius/self%coreRadius)**2)**(1.5d0*self%beta)
end if
- ! Get position in spherical coordinate system.
- position =coordinates
- ! Compute density.
- r =position%r()/self%coreRadius
- betaProfileDensity=self%densityNormalization/(1.0d0+r**2)**(1.5d0*self%beta)
return
end function betaProfileDensity
- double precision function betaProfileDensityGradientRadial(self,coordinates,logarithmic,componentType,massType)
+ double precision function betaProfileDensityGradientRadial(self,coordinates,logarithmic)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a $\beta$-profile mass distribution.
!!}
- use :: Coordinates, only : assignment(=), coordinateSpherical
implicit none
- class (massDistributionBetaProfile ), intent(inout) :: self
+ class (massDistributionBetaProfile ), intent(inout), target :: self
class (coordinate ), intent(in ) :: coordinates
logical , intent(in ), optional :: logarithmic
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision :: r
+ double precision :: radius
logical :: logarithmicActual
- if (.not.self%matches(componentType,massType)) then
- betaProfileDensityGradientRadial=0.0d0
- return
- end if
! Set default options.
logarithmicActual=.false.
if (present(logarithmic)) logarithmicActual=logarithmic
! Get position in spherical coordinate system.
- position=coordinates
- r =position%r()/self%coreRadius
+ radius=coordinates%rSpherical()
+ ! Apply truncation.
+ if (self%truncateAtOuterRadius .and. radius > self%outerRadius) then
+ betaProfileDensityGradientRadial=0.0d0
+ return
+ end if
+ ! Convert to dimensionless radius.
+ radius=radius/self%coreRadius
! Compute density gradient.
if (logarithmicActual) then
- betaProfileDensityGradientRadial= &
- & -3.0d0 &
- & *self%beta &
- & * r**2 &
- & /(r**2+1.0d0)
+ betaProfileDensityGradientRadial= &
+ & -3.0d0 &
+ & *self%beta &
+ & * radius**2 &
+ & /(radius**2+1.0d0)
else
- betaProfileDensityGradientRadial= &
- & -3.0d0 &
- & *self%beta &
- & *self%densityNormalization &
- & /self%coreRadius &
- & * r**2 &
- & /(r**2+1.0d0)**(1.5d0*self%beta+1.0d0)
+ betaProfileDensityGradientRadial= &
+ & -3.0d0 &
+ & *self%beta &
+ & *self%densityNormalization &
+ & /self%coreRadius &
+ & * radius &
+ & /(radius**2+1.0d0)**(1.5d0*self%beta+1.0d0)
end if
return
end function betaProfileDensityGradientRadial
- double precision function betaProfileMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function betaProfileMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for $\beta$-profile mass distributions. Result computed
using \href{http://www.wolframalpha.com/input/?i=integrate+4*pi*r^2*rho\%2F\%281\%2Br^2\%29^\%283*beta\%2F2\%29}{Wolfram Alpha}.
@@ -313,53 +323,52 @@ double precision function betaProfileMassEnclosedBySphere(self,radius,componentT
use :: Hypergeometric_Functions, only : Hypergeometric_2F1
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionBetaProfile ), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision , parameter :: radiusTiny =1.0d-6
- double precision :: fractionalRadius
+ class (massDistributionBetaProfile ), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision , parameter :: radiusTiny =1.0d-6
+ double precision :: fractionalRadius , radius_
- if (.not.self%matches(componentType,massType)) then
- betaProfileMassEnclosedBySphere=0.0d0
- return
+ if (self%truncateAtOuterRadius .and. radius > self%outerRadius) then
+ radius_=self%outerRadius
+ else
+ radius_=radius
end if
- fractionalRadius=radius/self%coreRadius
+ fractionalRadius=radius_/self%coreRadius
if (self%betaIsTwoThirds) then
! Solution for special case of β=2/3.
if (fractionalRadius < radiusTiny) then
! Use a series solution.
- betaProfileMassEnclosedBySphere= &
- & +4.0d0 &
- & *Pi &
- & *self%densityNormalization &
- & *self%coreRadius **3 &
- & * fractionalRadius**3 &
- & *( +1.0d0/3.0d0+fractionalRadius**2 &
- & * ( -1.0d0/5.0d0+fractionalRadius**2 &
- & * (+1.0d0/7.0d0 &
- & ) &
- & ) &
+ betaProfileMassEnclosedBySphere= &
+ & +4.0d0 &
+ & *Pi &
+ & *self%densityNormalization &
+ & *self%coreRadius **3 &
+ & * fractionalRadius**3 &
+ & *( +1.0d0/3.0d0+fractionalRadius**2 &
+ & * ( -1.0d0/5.0d0+fractionalRadius**2 &
+ & * (+1.0d0/7.0d0 &
+ & ) &
+ & ) &
& )
else
- betaProfileMassEnclosedBySphere= &
- & +4.0d0 &
- & *Pi &
- & *self%densityNormalization &
- & *( &
- & + fractionalRadius &
- & -atan(fractionalRadius) &
- & ) &
+ betaProfileMassEnclosedBySphere= &
+ & +4.0d0 &
+ & *Pi &
+ & *self%densityNormalization &
+ & *( &
+ & + fractionalRadius &
+ & -atan(fractionalRadius) &
+ & ) &
& *self%coreRadius**3
end if
else
! General solution.
- betaProfileMassEnclosedBySphere= &
+ betaProfileMassEnclosedBySphere= &
& +4.0d0 &
& /3.0d0 &
& *Pi &
& *self%densityNormalization &
- & *radius**3 &
+ & *radius_**3 &
& *Hypergeometric_2F1( &
& [1.5d0,1.5d0*self%beta], &
& [2.5d0 ], &
@@ -369,37 +378,46 @@ double precision function betaProfileMassEnclosedBySphere(self,radius,componentT
return
end function betaProfileMassEnclosedBySphere
- double precision function betaProfilePotential(self,coordinates,componentType,massType)
+ logical function betaProfilePotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionBetaProfile), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function betaProfilePotentialIsAnalytic
+
+ double precision function betaProfilePotential(self,coordinates,status)
!!{
Return the potential at the specified {\normalfont \ttfamily coordinates} in a $\beta$-profile mass distribution. Calculated using
\href{http://www.wolframalpha.com/input/?i=integrate+4\%2F3+\%CF\%80+r+\%CF\%81+2F1\%283\%2F2\%2C+\%283+\%CE\%B2\%29\%2F2\%2C+5\%2F2\%2C+-r^2\%29}{Wolfram
Alpha}.
!!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Hypergeometric_Functions , only : Hypergeometric_2F1
use :: Numerical_Comparison , only : Values_Agree
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
implicit none
- class (massDistributionBetaProfile ), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- type (coordinateSpherical ) :: position
- double precision , parameter :: fractionalRadiusMinimum=1.0d-3
- double precision :: fractionalRadius
+ class (massDistributionBetaProfile ), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ double precision , parameter :: fractionalRadiusMinimum=1.0d-3
+ double precision :: fractionalRadius , radius
- if (.not.self%matches(componentType,massType)) then
- betaProfilePotential=0.0d0
- return
- end if
- ! Get position in spherical coordinate system.
- position=coordinates
+ if (present(status)) status=structureErrorCodeSuccess
! Compute the potential at this position.
- fractionalRadius=position%r()/self%coreRadius
+ radius=coordinates%rSpherical()
+ if (self%truncateAtOuterRadius .and. radius > self%outerRadius) then
+ fractionalRadius=self%outerRadius/self%coreRadius
+ else
+ fractionalRadius= radius/self%coreRadius
+ end if
if (Values_Agree(self%beta,2.0d0/3.0d0,absTol=1.0d-6)) then
if (fractionalRadius < fractionalRadiusMinimum) then
- betaProfilePotential= &
+ betaProfilePotential= &
& Pi &
& *self%densityNormalization &
& *( &
@@ -411,7 +429,7 @@ double precision function betaProfilePotential(self,coordinates,componentType,ma
& /5.0d0 &
& )
else
- betaProfilePotential= &
+ betaProfilePotential= &
& 2.0d0 &
& *Pi &
& *self%densityNormalization &
@@ -427,7 +445,7 @@ double precision function betaProfilePotential(self,coordinates,componentType,ma
end if
else
if (fractionalRadius < fractionalRadiusMinimum) then
- betaProfilePotential= &
+ betaProfilePotential= &
& Pi &
& *self%densityNormalization &
& *fractionalRadius**3 &
@@ -439,7 +457,7 @@ double precision function betaProfilePotential(self,coordinates,componentType,ma
& *fractionalRadius**2 &
& )
else
- betaProfilePotential= &
+ betaProfilePotential= &
& 2.0d0 &
& /3.0d0 &
& *Pi &
@@ -463,14 +481,14 @@ double precision function betaProfilePotential(self,coordinates,componentType,ma
& )
end if
end if
- if (.not.self%isDimensionless()) &
- & betaProfilePotential= &
- & betaProfilePotential &
+ if (.not.self%isDimensionless()) &
+ & betaProfilePotential= &
+ & betaProfilePotential &
& *gravitationalConstantGalacticus
return
end function betaProfilePotential
- double precision function betaProfileDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function betaProfileDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Computes radial moments of the density in a $\beta$-profile mass distribution.
!!}
@@ -483,14 +501,27 @@ double precision function betaProfileDensityRadialMoment(self,moment,radiusMinim
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- double precision :: fractionalRadiusMinimum, fractionalRadiusMaximum
+ logical :: haveRadiusMinimum , haveRadiusMaximum
+ double precision :: radiusMinimum_ , radiusMaximum_ , &
+ & fractionalRadiusMinimum, fractionalRadiusMaximum
integer :: specialCaseMoment
- if (.not.self%matches(componentType,massType)) then
- betaProfileDensityRadialMoment=0.0d0
- return
+ ! Determine effective radii.
+ haveRadiusMinimum=present(radiusMinimum)
+ haveRadiusMaximum=present(radiusMaximum)
+ radiusMinimum_ = 0.0d0
+ radiusMaximum_ =huge ( 0.0d0)
+ if (haveRadiusMinimum) then
+ radiusMinimum_ = radiusMinimum
+ end if
+ if (haveRadiusMaximum) then
+ radiusMaximum_ = radiusMaximum
+ end if
+ if (self%truncateAtOuterRadius) then
+ radiusMinimum_ =min(radiusMinimum_,self%outerRadius)
+ radiusMaximum_ =min(radiusMaximum_,self%outerRadius)
+ haveRadiusMinimum=.true.
+ haveRadiusMaximum=.true.
end if
! Determine if special case solutions can be used.
specialCaseMoment=-huge(0)
@@ -506,8 +537,8 @@ double precision function betaProfileDensityRadialMoment(self,moment,radiusMinim
end if
end if
if (present(isInfinite)) isInfinite=.false.
- if (present(radiusMaximum)) then
- fractionalRadiusMaximum=radiusMaximum/self%coreRadius
+ if (haveRadiusMaximum) then
+ fractionalRadiusMaximum=radiusMaximum_/self%coreRadius
if (specialCaseMoment /= -huge(0)) then
! Special case for 0ᵗʰ, 1ˢᵗ, 2ⁿᵈ, and 3ʳᵈ moments of a β=2/3 distribution.
betaProfileDensityRadialMoment= &
@@ -539,10 +570,25 @@ double precision function betaProfileDensityRadialMoment(self,moment,radiusMinim
& / (+1.0d0+moment )
end if
end if
- if (present(radiusMinimum)) then
- fractionalRadiusMinimum=radiusMinimum/self%coreRadius
+ if (haveRadiusMinimum) then
+ fractionalRadiusMinimum=radiusMinimum_/self%coreRadius
+ if (specialCaseMoment /= -huge(0)) then
+ ! Special case for 0ᵗʰ, 1ˢᵗ, 2ⁿᵈ, and 3ʳᵈ moments of a β=2/3 distribution.
+ betaProfileDensityRadialMoment= &
+ & +betaProfileDensityRadialMoment &
+ & -radialMomentTwoThirds(specialCaseMoment,fractionalRadiusMinimum)
+ else
+ betaProfileDensityRadialMoment= &
+ & +betaProfileDensityRadialMoment &
+ & -fractionalRadiusMinimum**(moment+1.0d0) &
+ & *Hypergeometric_2F1 ( &
+ & [(moment+1.0d0)/2.0d0,1.5d0*self%beta], &
+ & [(moment+3.0d0)/2.0d0 ], &
+ & -fractionalRadiusMinimum**2 &
+ & ) &
+ & / (moment+1.0d0)
+ end if
else
- fractionalRadiusMinimum=0.0d0
if (moment <= -1.0d0) then
betaProfileDensityRadialMoment=0.0d0
if (present(isInfinite)) then
@@ -553,22 +599,6 @@ double precision function betaProfileDensityRadialMoment(self,moment,radiusMinim
end if
end if
end if
- if (specialCaseMoment /= -huge(0)) then
- ! Special case for 0ᵗʰ, 1ˢᵗ, 2ⁿᵈ, and 3ʳᵈ moments of a β=2/3 distribution.
- betaProfileDensityRadialMoment= &
- & +betaProfileDensityRadialMoment &
- & -radialMomentTwoThirds(specialCaseMoment,fractionalRadiusMinimum)
- else
- betaProfileDensityRadialMoment= &
- & +betaProfileDensityRadialMoment &
- & -fractionalRadiusMinimum**(moment+1.0d0) &
- & *Hypergeometric_2F1 ( &
- & [(moment+1.0d0)/2.0d0,1.5d0*self%beta], &
- & [(moment+3.0d0)/2.0d0 ], &
- & -fractionalRadiusMinimum**2 &
- & ) &
- & / (moment+1.0d0)
- end if
! Convert to dimensionful units.
betaProfileDensityRadialMoment &
& =betaProfileDensityRadialMoment &
@@ -651,25 +681,44 @@ double precision function betaProfileDensitySquareIntegral(self,radiusMinimum,ra
use :: Hypergeometric_Functions, only : Hypergeometric_2F1
use :: Numerical_Constants_Math, only : Pi
implicit none
- class (massDistributionBetaProfile), intent(inout) :: self
- double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
- logical , intent( out), optional :: isInfinite
- double precision :: fractionalRadiusMinimum, fractionalRadiusMaximum
+ class (massDistributionBetaProfile ), intent(inout) :: self
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ logical :: haveRadiusMinimum , haveRadiusMaximum
+ double precision :: radiusMinimum_ , radiusMaximum_ , &
+ & fractionalRadiusMinimum, fractionalRadiusMaximum
if (present(isInfinite)) isInfinite=.false.
betaProfileDensitySquareIntegral=0.0d0
+ ! Determine effective radii.
+ haveRadiusMinimum=present(radiusMinimum)
+ haveRadiusMaximum=present(radiusMaximum)
+ radiusMinimum_ = 0.0d0
+ radiusMaximum_ =huge ( 0.0d0)
+ if (haveRadiusMinimum) then
+ radiusMinimum_ = radiusMinimum
+ end if
+ if (haveRadiusMaximum) then
+ radiusMaximum_ = radiusMaximum
+ end if
+ if (self%truncateAtOuterRadius) then
+ radiusMinimum_ =min(radiusMinimum_,self%outerRadius)
+ radiusMaximum_ =min(radiusMaximum_,self%outerRadius)
+ haveRadiusMinimum=.true.
+ haveRadiusMaximum=.true.
+ end if
! Determine if the special case solution can be used.
if (self%betaIsTwoThirds) then
! Compute the integral for the case β=2/3.
- if (present(radiusMinimum)) then
- fractionalRadiusMinimum=+ radiusMinimum &
+ if (haveRadiusMinimum) then
+ fractionalRadiusMinimum=+ radiusMinimum_ &
& /self%coreRadius
betaProfileDensitySquareIntegral=+betaProfileDensitySquareIntegral-4.0d0*Pi*(atan(fractionalRadiusMinimum)/2.0d0-fractionalRadiusMinimum/2.0d0/(1.0d0+fractionalRadiusMinimum**2))
else
betaProfileDensitySquareIntegral=+betaProfileDensitySquareIntegral+0.0d0
end if
- if (present(radiusMaximum)) then
- fractionalRadiusMaximum=+ radiusMaximum &
+ if (haveRadiusMaximum) then
+ fractionalRadiusMaximum=+ radiusMaximum_ &
& /self%coreRadius
betaProfileDensitySquareIntegral=betaProfileDensitySquareIntegral+4.0d0*Pi*(atan(fractionalRadiusMaximum)/2.0d0-fractionalRadiusMaximum/2.0d0/(1.0d0+fractionalRadiusMaximum**2))
else
@@ -677,15 +726,15 @@ double precision function betaProfileDensitySquareIntegral(self,radiusMinimum,ra
end if
else
! Compute the integral for the general case.
- if (present(radiusMinimum)) then
- fractionalRadiusMinimum=+ radiusMinimum &
+ if (haveRadiusMinimum) then
+ fractionalRadiusMinimum=+ radiusMinimum_ &
& /self%coreRadius
betaProfileDensitySquareIntegral=+betaProfileDensitySquareIntegral-4.0d0*Pi/3.0d0*fractionalRadiusMinimum**3*Hypergeometric_2F1([1.5d0,3.0d0*self%beta],[2.5d0],-fractionalRadiusMinimum**2)
else
betaProfileDensitySquareIntegral=+betaProfileDensitySquareIntegral+0.0d0
end if
- if (present(radiusMaximum)) then
- fractionalRadiusMaximum=+ radiusMaximum &
+ if (haveRadiusMaximum) then
+ fractionalRadiusMaximum=+ radiusMaximum_ &
& /self%coreRadius
betaProfileDensitySquareIntegral=+betaProfileDensitySquareIntegral+4.0d0*Pi/3.0d0*fractionalRadiusMaximum**3*Hypergeometric_2F1([1.5d0,3.0d0*self%beta],[2.5d0],-fractionalRadiusMaximum**2)
else
diff --git a/source/mass_distributions.spherical.black_hole.F90 b/source/mass_distributions.spherical.black_hole.F90
new file mode 100644
index 0000000000..4b5e79e586
--- /dev/null
+++ b/source/mass_distributions.spherical.black_hole.F90
@@ -0,0 +1,319 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a black hole distribution class.
+ !!}
+
+ !![
+
+ A mass distribution class for point masses.
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionBlackHole
+ !!{
+ A black hole distribution.
+ !!}
+ double precision :: mass, radiusGravitational
+ contains
+ procedure :: massTotal => blackHoleMassTotal
+ procedure :: density => blackHoleDensity
+ procedure :: densityGradientRadial => blackHoleDensityGradientRadial
+ procedure :: densityRadialMoment => blackHoleDensityRadialMoment
+ procedure :: massEnclosedBySphere => blackHoleMassEnclosedBySphere
+ procedure :: rotationCurve => blackHoleRotationCurve
+ procedure :: rotationCurveGradient => blackHoleRotationCurveGradient
+ procedure :: potentialIsAnalytic => blackHolePotentialIsAnalytic
+ procedure :: potential => blackHolePotential
+ end type massDistributionBlackHole
+
+ interface massDistributionBlackHole
+ !!{
+ Constructors for the {\normalfont \ttfamily blackHole} mass distribution class.
+ !!}
+ module procedure blackHoleConstructorParameters
+ module procedure blackHoleConstructorInternal
+ end interface massDistributionBlackHole
+
+contains
+
+ function blackHoleConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily blackHole} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionBlackHole) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: mass
+ logical :: dimensionless
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ mass
+ 1.0d0
+ The mass of the black hole.
+
+
+
+ dimensionless
+ .true.
+ If true the point mass distribution is considered to be dimensionless.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ self=massDistributionBlackHole(componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+ !!]
+ return
+ end function blackHoleConstructorParameters
+
+ function blackHoleConstructorInternal(mass,dimensionless,componentType,massType) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily blackHole} mass distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Comparison , only : Values_Differ
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Physical , only : speedLight
+ use :: Numerical_Constants_Prefixes , only : milli
+ implicit none
+ type (massDistributionBlackHole ) :: self
+ double precision , intent(in ), optional :: mass
+ logical , intent(in ), optional :: dimensionless
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+ ! Determine if profile is dimensionless.
+ self%dimensionless=.false.
+ if (present(dimensionless)) self%dimensionless=dimensionless
+ ! If dimensionless, then set scale length and mass to unity.
+ if (self%dimensionless) then
+ if (present(mass)) then
+ if (Values_Differ(mass,1.0d0,absTol=1.0d-6)) call Error_Report('mass should be unity for a dimensionless profile (or simply do not specify a mass)'//{introspection:location})
+ end if
+ self%mass =1.0d0
+ else if (present(mass)) then
+ self%mass =mass
+ self%dimensionless=.false.
+ else
+ call Error_Report('either specify a mass, or declare the distribution to be dimensionless'//{introspection:location})
+ end if
+ ! Compute the gravitational radius for the black hole.
+ if (self%dimensionless) then
+ self%radiusGravitational=+1.0d0
+ else
+ self%radiusGravitational=+ gravitationalConstantGalacticus &
+ & *self%mass &
+ & / milli **2 &
+ & / speedLight **2
+ end if
+ return
+ end function blackHoleConstructorInternal
+
+ double precision function blackHoleMassTotal(self)
+ !!{
+ Return the total mass in the black hole.
+ !!}
+ implicit none
+ class(massDistributionBlackHole), intent(inout) :: self
+
+ blackHoleMassTotal=self%mass
+ return
+ end function blackHoleMassTotal
+
+ double precision function blackHoleDensity(self,coordinates)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a $\beta$-profile mass distribution.
+ !!}
+ implicit none
+ class(massDistributionBlackHole), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ blackHoleDensity= 0.0d0
+ if (coordinates%rSphericalSquared() > 0.0d0) return
+ blackHoleDensity=huge(0.0d0)
+ return
+ end function blackHoleDensity
+
+ double precision function blackHoleDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density gradient in the radial direction for a point mass.
+ !!}
+ implicit none
+ class (massDistributionBlackHole), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !$GLC attributes unused :: logarithmic
+
+ blackHoleDensityGradientRadial= 0.0d0
+ if (coordinates%rSphericalSquared() > 0.0d0) return
+ blackHoleDensityGradientRadial=-huge(0.0d0)
+ return
+ end function blackHoleDensityGradientRadial
+
+ double precision function blackHoleMassEnclosedBySphere(self,radius)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a black hole.
+ !!}
+ implicit none
+ class (massDistributionBlackHole), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ blackHoleMassEnclosedBySphere=+self%mass
+ return
+ end function blackHoleMassEnclosedBySphere
+
+ double precision function blackHoleRotationCurve(self,radius)
+ !!{
+ Return the rotation curve for a blackHole mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Physical , only : speedLight
+ use :: Numerical_Constants_Prefixes , only : milli
+ implicit none
+ class (massDistributionBlackHole), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ if (self%mass <= 0.0d0) then
+ blackHoleRotationCurve=0.0d0
+ else if (radius <= self%radiusGravitational) then
+ if (self%dimensionless) then
+ blackHoleRotationCurve=+1.0d0
+ else
+ blackHoleRotationCurve=+milli &
+ & *speedLight
+ end if
+ else
+ blackHoleRotationCurve=+sqrt( &
+ & +self%massEnclosedBySphere(radius) &
+ & / radius &
+ & )
+ ! Make dimensionful if necessary.
+ if (.not.self%dimensionless) blackHoleRotationCurve=+sqrt(gravitationalConstantGalacticus) &
+ & *blackHoleRotationCurve
+ end if
+ return
+ end function blackHoleRotationCurve
+
+ double precision function blackHoleRotationCurveGradient(self,radius)
+ !!{
+ Return the rotation curve gradient for a spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionBlackHole), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ if ( &
+ & self%mass <= 0.0d0 &
+ & .or. &
+ & self%radiusGravitational > radius &
+ &) then
+ blackHoleRotationCurveGradient=+0.0d0
+ else
+ blackHoleRotationCurveGradient=-self%mass &
+ & / radius**2
+ ! Make dimensionful if necessary.
+ if (.not.self%dimensionless) blackHoleRotationCurveGradient=+gravitationalConstantGalacticus &
+ & *blackHoleRotationCurveGradient
+ end if
+ return
+ end function blackHoleRotationCurveGradient
+
+ logical function blackHolePotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionBlackHole), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function blackHolePotentialIsAnalytic
+
+ double precision function blackHolePotential(self,coordinates,status)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} for a point mass.
+ !!}
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class(massDistributionBlackHole ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+
+ if (present(status)) status=structureErrorCodeSuccess
+ blackHolePotential=- self %mass &
+ & /max( &
+ & coordinates%rSpherical (), &
+ & self %radiusGravitational &
+ & )
+ if (.not.self%dimensionless) &
+ & blackHolePotential=+blackHolePotential &
+ & *gravitationalConstantGalacticus
+ return
+ end function blackHolePotential
+
+ double precision function blackHoleDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
+ !!{
+ Computes radial moments of the density for a point mass.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionBlackHole), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ blackHoleDensityRadialMoment=0.0d0
+ ! Moment is zero if:
+ if (moment > 0.0d0) return ! The moment is positive (in which case ∫ δ(r) rᵐ dr = 0).
+ if (present(radiusMinimum)) then
+ if (radiusMinimum > 0.0d0) return ! The lower limit of the integral does not extend to zero.
+ end if
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ else
+ call Error_Report('radial moment is infinite'//{introspection:location})
+ end if
+ return
+ end function blackHoleDensityRadialMoment
diff --git a/source/mass_distributions.spherical.constant_density_cloud.F90 b/source/mass_distributions.spherical.constant_density_cloud.F90
index f6796600d9..80c7986a0a 100644
--- a/source/mass_distributions.spherical.constant_density_cloud.F90
+++ b/source/mass_distributions.spherical.constant_density_cloud.F90
@@ -37,7 +37,8 @@
procedure :: densityGradientRadial => constantDensityCloudDensityGradientRadial
procedure :: densityRadialMoment => constantDensityCloudDensityRadialMoment
procedure :: massEnclosedBySphere => constantDensityCloudMassEnclosedBySphere
- procedure :: potential => constantDensityCloudPotential
+ procedure :: potentialIsAnalytic => constantDensityCloudPotentialIsAnalytic
+ procedure :: potential => constantDensityCloudPotential
end type massDistributionConstantDensityCloud
interface massDistributionConstantDensityCloud
@@ -118,20 +119,14 @@ function constantDensityCloudConstructorInternal(mass,radius,componentType,massT
return
end function constantDensityCloudConstructorInternal
- double precision function constantDensityCloudDensity(self,coordinates,componentType,massType)
+ double precision function constantDensityCloudDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a $\beta$-profile mass distribution.
!!}
implicit none
- class(massDistributionConstantDensityCloud), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionConstantDensityCloud), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
- if (.not.self%matches(componentType,massType)) then
- constantDensityCloudDensity=0.0d0
- return
- end if
if (coordinates%rSphericalSquared() < self%radiusSquared) then
constantDensityCloudDensity=self%density_
else
@@ -140,36 +135,28 @@ double precision function constantDensityCloudDensity(self,coordinates,component
return
end function constantDensityCloudDensity
- double precision function constantDensityCloudDensityGradientRadial(self,coordinates,logarithmic,componentType,massType)
+ double precision function constantDensityCloudDensityGradientRadial(self,coordinates,logarithmic)
!!{
Return the density gradient in the radial direction in a constant density cloud.
!!}
implicit none
- class (massDistributionConstantDensityCloud), intent(inout) :: self
+ class (massDistributionConstantDensityCloud), intent(inout), target :: self
class (coordinate ), intent(in ) :: coordinates
logical , intent(in ), optional :: logarithmic
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- !$GLC attributes unused :: self, coordinates, logarithmic, componentType, massType
+ !$GLC attributes unused :: self, coordinates, logarithmic
constantDensityCloudDensityGradientRadial=0.0d0
return
end function constantDensityCloudDensityGradientRadial
- double precision function constantDensityCloudMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function constantDensityCloudMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a constant density cloud.
!!}
implicit none
- class (massDistributionConstantDensityCloud), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionConstantDensityCloud), intent(inout), target :: self
+ double precision , intent(in ) :: radius
- if (.not.self%matches(componentType,massType)) then
- constantDensityCloudMassEnclosedBySphere=0.0d0
- return
- end if
if (radius > self%radius) then
constantDensityCloudMassEnclosedBySphere=+self%mass
else
@@ -182,22 +169,30 @@ double precision function constantDensityCloudMassEnclosedBySphere(self,radius,c
return
end function constantDensityCloudMassEnclosedBySphere
- double precision function constantDensityCloudPotential(self,coordinates,componentType,massType)
+ logical function constantDensityCloudPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionConstantDensityCloud), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function constantDensityCloudPotentialIsAnalytic
+
+ double precision function constantDensityCloudPotential(self,coordinates,status)
!!{
Return the potential at the specified {\normalfont \ttfamily coordinates} in a constant density cloud.
!!}
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class (massDistributionConstantDensityCloud), intent(inout) :: self
+ class (massDistributionConstantDensityCloud), intent(inout), target :: self
class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
double precision :: radius
- if (.not.self%matches(componentType,massType)) then
- constantDensityCloudPotential=0.0d0
- return
- end if
+ if (present(status)) status=structureErrorCodeSuccess
radius=coordinates%rSpherical()
if (radius > self%radius) then
constantDensityCloudPotential=-gravitationalConstantGalacticus &
@@ -219,7 +214,7 @@ double precision function constantDensityCloudPotential(self,coordinates,compone
return
end function constantDensityCloudPotential
- double precision function constantDensityCloudDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function constantDensityCloudDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Computes radial moments of the density in a constant density cloud.
!!}
@@ -230,14 +225,8 @@ double precision function constantDensityCloudDensityRadialMoment(self,moment,ra
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum , radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
double precision :: radiusMaximum_
- if (.not.self%matches(componentType,massType)) then
- constantDensityCloudDensityRadialMoment=0.0d0
- return
- end if
constantDensityCloudDensityRadialMoment=+0.0d0
if (present(isInfinite)) isInfinite=.false.
radiusMaximum_=min(radiusMaximum,self%radius)
diff --git a/source/mass_distributions.spherical.decorator.F90 b/source/mass_distributions.spherical.decorator.F90
new file mode 100644
index 0000000000..61ea44d8b6
--- /dev/null
+++ b/source/mass_distributions.spherical.decorator.F90
@@ -0,0 +1,457 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements an abstract spherical mass distribution decorator class.
+ !!}
+
+ !![
+
+
+ An abstract mass distribution class for decorators of other mass distributions. ``Fallthrough'' functions are provided that
+ all the decorated class or numerical solutions to be used.
+
+
+ !!]
+ type, abstract, extends(massDistributionSpherical) :: massDistributionSphericalDecorator
+ !!{
+ Implementation of a decorator spherical mass distribution.
+ !!}
+ private
+ type (enumerationNonAnalyticSolversType) :: nonAnalyticSolver
+ class(massDistributionSpherical ), pointer :: massDistribution_ => null()
+ contains
+ !![
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ !!]
+ procedure :: massEnclosedBySphere => sphericalDecoratorMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => sphericalDecoratorRadiusEnclosingMass
+ procedure :: densityGradientRadial => sphericalDecoratorDensityGradientRadial
+ procedure :: densityRadialMoment => sphericalDecoratorDensityRadialMoment
+ procedure :: radiusEnclosingDensity => sphericalDecoratorRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => sphericalDecoratorRadiusFromSpecificAngularMomentum
+ procedure :: fourierTransform => sphericalDecoratorFourierTransform
+ procedure :: radiusFreefall => sphericalDecoratorRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => sphericalDecoratorRadiusFreefallIncreaseRate
+ procedure :: potentialIsAnalytic => sphericalDecoratorPotentialIsAnalytic
+ procedure :: potential => sphericalDecoratorPotential
+ procedure :: energyPotential => sphericalDecoratorEnergyPotential
+ procedure :: energyKinetic => sphericalDecoratorEnergyKinetic
+ procedure :: massEnclosedBySphereNonAnalytic => sphericalDecoratorMassEnclosedBySphereNonAnalytic
+ procedure :: radiusEnclosingMassNonAnalytic => sphericalDecoratorRadiusEnclosingMassNonAnalytic
+ procedure :: densityGradientRadialNonAnalytic => sphericalDecoratorDensityGradientRadialNonAnalytic
+ procedure :: densityRadialMomentNonAnalytic => sphericalDecoratorDensityRadialMomentNonAnalytic
+ procedure :: radiusEnclosingDensityNonAnalytic => sphericalDecoratorRadiusEnclosingDensityNonAnalytic
+ procedure :: radiusFromSpecificAngularMomentumNonAnalytic => sphericalDecoratorRadiusFromSpecificAngularMomentumNonAnalytic
+ procedure :: fourierTransformNonAnalytic => sphericalDecoratorFourierTransformNonAnalytic
+ procedure :: radiusFreefallNonAnalytic => sphericalDecoratorRadiusFreefallNonAnalytic
+ procedure :: radiusFreefallIncreaseRateNonAnalytic => sphericalDecoratorRadiusFreefallIncreaseRateNonAnalytic
+ procedure :: potentialNonAnalytic => sphericalDecoratorPotentialNonAnalytic
+ procedure :: energyPotentialNonAnalytic => sphericalDecoratorEnergyPotentialNonAnalytic
+ procedure :: energyKineticNonAnalytic => sphericalDecoratorEnergyKineticNonAnalytic
+ procedure :: useUndecorated => sphericalDecoratorUseUndecorated
+ end type massDistributionSphericalDecorator
+
+contains
+
+ logical function sphericalDecoratorUseUndecorated(self) result(useUndecorated)
+ !!{
+ Determines whether to use the undecorated solution.
+ !!}
+ implicit none
+ class(massDistributionSphericalDecorator), intent(inout) :: self
+
+ useUndecorated=self%nonAnalyticSolver == nonAnalyticSolversFallThrough
+ return
+ end function sphericalDecoratorUseUndecorated
+
+ double precision function sphericalDecoratorMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for decorator mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ mass=self%massEnclosedBySphereNonAnalytic(radius)
+ return
+ end function sphericalDecoratorMassEnclosedBySphere
+
+ double precision function sphericalDecoratorMassEnclosedBySphereNonAnalytic(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for decorator mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ if (self%useUndecorated()) then
+ mass=self%massDistribution_%massEnclosedBySphere (radius)
+ else
+ mass=self %massEnclosedBySphereNumerical(radius)
+ end if
+ return
+ end function sphericalDecoratorMassEnclosedBySphereNonAnalytic
+
+ double precision function sphericalDecoratorDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+
+ densityGradient=self%densityGradientRadialNonAnalytic(coordinates,logarithmic)
+ return
+ end function sphericalDecoratorDensityGradientRadial
+
+ double precision function sphericalDecoratorDensityGradientRadialNonAnalytic(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+
+ if (self%useUndecorated()) then
+ densityGradient=self%massDistribution_%densityGradientRadial (coordinates,logarithmic)
+ else
+ densityGradient=self %densityGradientRadialNumerical(coordinates,logarithmic)
+ end if
+ return
+ end function sphericalDecoratorDensityGradientRadialNonAnalytic
+
+ double precision function sphericalDecoratorRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for decorator spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ radius=self%radiusEnclosingDensityNonAnalytic(density,radiusGuess)
+ return
+ end function sphericalDecoratorRadiusEnclosingDensity
+
+ double precision function sphericalDecoratorRadiusEnclosingDensityNonAnalytic(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for decorator spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (self%useUndecorated()) then
+ radius=self%massDistribution_%radiusEnclosingDensity (density,radiusGuess)
+ else
+ radius=self %radiusEnclosingDensityNumerical(density,radiusGuess)
+ end if
+ return
+ end function sphericalDecoratorRadiusEnclosingDensityNonAnalytic
+
+ double precision function sphericalDecoratorRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for heated spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass, massFractional
+
+ radius=self%radiusEnclosingMassNonAnalytic(mass,massFractional)
+ return
+ end function sphericalDecoratorRadiusEnclosingMass
+
+ double precision function sphericalDecoratorRadiusEnclosingMassNonAnalytic(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for heated spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass, massFractional
+
+ if (self%useUndecorated()) then
+ radius=self%massDistribution_%radiusEnclosingMass (mass,massFractional)
+ else
+ radius=self %radiusEnclosingMassNumerical(mass,massFractional)
+ end if
+ return
+ end function sphericalDecoratorRadiusEnclosingMassNonAnalytic
+
+ double precision function sphericalDecoratorRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for decorator spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+
+ radius=self%radiusFromSpecificAngularMomentumNonAnalytic(angularMomentumSpecific)
+ return
+ end function sphericalDecoratorRadiusFromSpecificAngularMomentum
+
+ double precision function sphericalDecoratorRadiusFromSpecificAngularMomentumNonAnalytic(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for decorator spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+
+ if (self%useUndecorated()) then
+ radius=self%massDistribution_%radiusFromSpecificAngularMomentum (angularMomentumSpecific)
+ else
+ radius=self %radiusFromSpecificAngularMomentumNumerical(angularMomentumSpecific)
+ end if
+ return
+ end function sphericalDecoratorRadiusFromSpecificAngularMomentumNonAnalytic
+
+ double precision function sphericalDecoratorDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Returns a radial density moment for the decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ densityRadialMoment=self%densityRadialMomentNonAnalytic(moment,radiusMinimum,radiusMaximum,isInfinite)
+ return
+ end function sphericalDecoratorDensityRadialMoment
+
+ double precision function sphericalDecoratorDensityRadialMomentNonAnalytic(self,moment,radiusMinimum,radiusMaximum,isInfinite) result(densityRadialMoment)
+ !!{
+ Returns a radial density moment for the decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+
+ if (self%useUndecorated()) then
+ densityRadialMoment=self%massDistribution_%densityRadialMoment (moment,radiusMinimum,radiusMaximum,isInfinite)
+ else
+ densityRadialMoment=self %densityRadialMomentNumerical(moment,radiusMinimum,radiusMaximum,isInfinite)
+ end if
+ return
+ end function sphericalDecoratorDensityRadialMomentNonAnalytic
+
+ logical function sphericalDecoratorPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return if the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSphericalDecorator), intent(inout) :: self
+
+ isAnalytic=self%useUndecorated() .and. self%massDistribution_%potentialIsAnalytic()
+ return
+ end function sphericalDecoratorPotentialIsAnalytic
+
+ double precision function sphericalDecoratorPotential(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalDecorator), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
+
+ potential=self%potentialNonAnalytic(coordinates,status)
+ return
+ end function sphericalDecoratorPotential
+
+ double precision function sphericalDecoratorPotentialNonAnalytic(self,coordinates,status) result(potential)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalDecorator), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
+
+ if (self%useUndecorated()) then
+ potential=self%massDistribution_%potential (coordinates,status)
+ else
+ potential=self %potentialNumerical(coordinates,status)
+ end if
+ return
+ end function sphericalDecoratorPotentialNonAnalytic
+
+ double precision function sphericalDecoratorFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter, wavenumber
+
+ fourierTransform=self%fourierTransformNonAnalytic(radiusOuter,wavenumber)
+ return
+ end function sphericalDecoratorFourierTransform
+
+ double precision function sphericalDecoratorFourierTransformNonAnalytic(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter, wavenumber
+
+ if (self%useUndecorated()) then
+ fourierTransform=self%massDistribution_%fourierTransform (radiusOuter,wavenumber)
+ else
+ fourierTransform=self %fourierTransformNumerical(radiusOuter,wavenumber)
+ end if
+ return
+ end function sphericalDecoratorFourierTransformNonAnalytic
+
+ double precision function sphericalDecoratorRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radius=self%radiusFreefallNonAnalytic(time)
+ return
+ end function sphericalDecoratorRadiusFreefall
+
+ double precision function sphericalDecoratorRadiusFreefallNonAnalytic(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in a decorator spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ if (self%useUndecorated()) then
+ radius=self%massDistribution_%radiusFreefall (time)
+ else
+ radius=self %radiusFreefallNumerical(time)
+ end if
+ return
+ end function sphericalDecoratorRadiusFreefallNonAnalytic
+
+ double precision function sphericalDecoratorRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in a decorator spherical mass
+ distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radiusIncreaseRate=self%radiusFreefallIncreaseRateNonAnalytic(time)
+ return
+ end function sphericalDecoratorRadiusFreefallIncreaseRate
+
+ double precision function sphericalDecoratorRadiusFreefallIncreaseRateNonAnalytic(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in a decorator spherical mass
+ distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ if (self%useUndecorated()) then
+ radiusIncreaseRate=self%massDistribution_%radiusFreefallIncreaseRate (time)
+ else
+ radiusIncreaseRate=self %radiusFreefallIncreaseRateNumerical(time)
+ end if
+ return
+ end function sphericalDecoratorRadiusFreefallIncreaseRateNonAnalytic
+
+ double precision function sphericalDecoratorEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius}.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ energy=self%energyPotentialNonAnalytic(radiusOuter)
+ return
+ end function sphericalDecoratorEnergyPotential
+
+ double precision function sphericalDecoratorEnergyPotentialNonAnalytic(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius}.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ if (self%useUndecorated()) then
+ energy=self%massDistribution_%energyPotential (radiusOuter)
+ else
+ energy=self %energyPotentialNumerical(radiusOuter)
+ end if
+ return
+ end function sphericalDecoratorEnergyPotentialNonAnalytic
+
+ double precision function sphericalDecoratorEnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius}.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ energy=self%energyKineticNonAnalytic(radiusOuter,massDistributionEmbedding)
+ return
+ end function sphericalDecoratorEnergyKinetic
+
+ double precision function sphericalDecoratorEnergyKineticNonAnalytic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius}.
+ !!}
+ implicit none
+ class (massDistributionSphericalDecorator), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+
+ if (self%useUndecorated()) then
+ energy=self%massDistribution_%energyKinetic (radiusOuter,self%massDistribution_ )
+ else
+ energy=self %energyKineticNumerical(radiusOuter, massDistributionEmbedding)
+ end if
+ return
+ end function sphericalDecoratorEnergyKineticNonAnalytic
diff --git a/source/mass_distributions.spherical.finite_resolution.F90 b/source/mass_distributions.spherical.finite_resolution.F90
new file mode 100644
index 0000000000..cb6266b978
--- /dev/null
+++ b/source/mass_distributions.spherical.finite_resolution.F90
@@ -0,0 +1,197 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a finite resolution spherical mass distribution.
+ !!}
+
+ !![
+
+
+ A mass distribution class which applies a finite resolution to some other mass distribution class, typically to mimic the
+ effects of finite resolution in an N-body simulation. Specifically, the density profile is given by
+ \begin{equation}
+ \rho(r) = \rho^\prime(r) \left( 1 + \left[ \frac{\Delta x}{r} \right]^2 \right)^{-1/2},
+ \end{equation}
+ where $\Delta x$ is the larger of the resolution length, {\normalfont \ttfamily [lengthResolution]}, and the radius in the
+ original profile enclosing the mass resolution, {\normalfont \ttfamily [massResolution]}.
+
+ Note that this choice was constructed to give a constant density core in an NFW density profile. For a density profile,
+ $\rho^\prime(r)$, which rises more steeply than $r^{-1}$ as $r \rightarrow 0$ we will still have a cuspy density profile
+ under this model.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalFiniteResolution
+ !!{
+ Implementation of a finite resolution spherical mass distribution.
+ !!}
+ private
+ double precision :: lengthResolution
+ contains
+ final :: sphericalFiniteResolutionDestructor
+ procedure :: density => sphericalFiniteResolutionDensity
+ procedure :: densityGradientRadial => sphericalFiniteResolutionDensityGradientRadial
+ end type massDistributionSphericalFiniteResolution
+
+ interface massDistributionSphericalFiniteResolution
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalFiniteResolution} mass distribution class.
+ !!}
+ module procedure sphericalFiniteResolutionConstructorParameters
+ module procedure sphericalFiniteResolutionConstructorInternal
+ end interface massDistributionSphericalFiniteResolution
+
+contains
+
+ function sphericalFiniteResolutionConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalFiniteResolution} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalFiniteResolution) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (varying_string ) :: nonAnalyticSolver
+ double precision :: lengthResolution
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ lengthResolution
+
+ The resolution length scale.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalFiniteResolution(lengthResolution,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ return
+ end function sphericalFiniteResolutionConstructorParameters
+
+ function sphericalFiniteResolutionConstructorInternal(lengthResolution,nonAnalyticSolver,massDistribution_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalFiniteResolution'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalFiniteResolution) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ double precision , intent(in ) :: lengthResolution
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%dimensionless=self%massDistribution_%isDimensionless()
+ return
+ end function sphericalFiniteResolutionConstructorInternal
+
+ subroutine sphericalFiniteResolutionDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalFiniteResolution} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalFiniteResolution), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine sphericalFiniteResolutionDestructor
+
+ double precision function sphericalFiniteResolutionDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalFiniteResolution), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ density=+self%massDistribution_%density(coordinates) &
+ & /sqrt( &
+ & +1.0d0 &
+ & +( &
+ & +self %lengthResolution &
+ & /coordinates%rSpherical () &
+ & )**2 &
+ & )
+ return
+ end function sphericalFiniteResolutionDensity
+
+ double precision function sphericalFiniteResolutionDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a finiteResolution spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalFiniteResolution), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !![
+
+ !!]
+
+ densityGradient=+self%massDistribution_%densityGradientRadial(coordinates,logarithmic=.true.) &
+ & +( &
+ & + self %lengthResolution &
+ & / coordinates%rSpherical () &
+ & ) **2 &
+ & /( &
+ & +1.0d0 &
+ & +( &
+ & +self %lengthResolution &
+ & /coordinates%rSpherical () &
+ & )**2 &
+ & )
+ if (.not.logarithmic_) &
+ densityGradient=+ densityGradient &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ return
+ end function sphericalFiniteResolutionDensityGradientRadial
diff --git a/source/mass_distributions.spherical.finite_resolution.NFW.F90 b/source/mass_distributions.spherical.finite_resolution.NFW.F90
new file mode 100644
index 0000000000..2cdd2fc26e
--- /dev/null
+++ b/source/mass_distributions.spherical.finite_resolution.NFW.F90
@@ -0,0 +1,1340 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a finite resolution NFW spherical mass distribution.
+ !!}
+
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+
+ A mass distribution class which applies a finite resolution to an NFW density profile, typically to mimic the effects
+ of finite resolution in an N-body simulation. Specifically, the density profile is given by
+ \begin{equation}
+ \rho(r) = \rho_\mathrm{NFW}(r) \left( 1 + \left[ \frac{\Delta x}{r} \right]^2 \right)^{-1/2},
+ \end{equation}
+ where $\Delta x$ is the larger of the resolution length, {\normalfont \ttfamily [lengthResolution]}, and the radius in the
+ original profile enclosing the mass resolution, {\normalfont \ttfamily [massResolution]}.
+
+
+ !!]
+ type, extends(massDistributionSpherical) :: massDistributionSphericalFiniteResolutionNFW
+ !!{
+ Implementation of a finite resolution spherical mass distribution.
+ !!}
+ private
+ double precision :: lengthResolution , radiusScale , &
+ & radiusVirial , mass , &
+ & densityNormalization , lengthResolutionScaleFree
+ double precision :: potentialRadiusPrevious , potentialPrevious , &
+ & massEnclosedMassPrevious , massEnclosedRadiusPrevious , &
+ & densityRadiusPrevious , densityPrevious , &
+ & densityNormalizationPrevious , radiusEnclosingDensityDensityPrevious , &
+ & radiusEnclosingDensityPrevious , radiusEnclosingMassMassPrevious , &
+ & radiusEnclosingMassPrevious , energyPrevious
+ ! Radius-enclosing-density tabulation.
+ logical :: radiusEnclosingDensityTableInitialized
+ integer :: radiusEnclosingDensityTableLengthResolutionCount , radiusEnclosingDensityTableDensityCount
+ double precision , allocatable, dimension(: ) :: radiusEnclosingDensityTableLengthResolution , radiusEnclosingDensityTableDensity
+ double precision , allocatable, dimension(:,:) :: radiusEnclosingDensityTable
+ type (interpolator), allocatable :: radiusEnclosingDensityTableLengthResolutionInterpolator, radiusEnclosingDensityTableDensityInterpolator
+ double precision :: radiusEnclosingDensityDensityMinimum , radiusEnclosingDensityDensityMaximum , &
+ & radiusEnclosingDensityLengthResolutionMinimum , radiusEnclosingDensityLengthResolutionMaximum
+ ! Radius-enclosing-mass tabulation.
+ logical :: radiusEnclosingMassTableInitialized
+ integer :: radiusEnclosingMassTableLengthResolutionCount , radiusEnclosingMassTableMassCount
+ double precision , allocatable, dimension(: ) :: radiusEnclosingMassTableLengthResolution , radiusEnclosingMassTableMass
+ double precision , allocatable, dimension(:,:) :: radiusEnclosingMassTable
+ type (interpolator), allocatable :: radiusEnclosingMassTableLengthResolutionInterpolator , radiusEnclosingMassTableMassInterpolator
+ double precision :: radiusEnclosingMassMassMinimum , radiusEnclosingMassMassMaximum , &
+ & radiusEnclosingMassLengthResolutionMinimum , radiusEnclosingMassLengthResolutionMaximum
+ ! Energy tabulation.
+ logical :: energyTableInitialized
+ integer :: energyTableLengthResolutionCount , energyTableRadiusOuterCount
+ double precision , allocatable, dimension(: ) :: energyTableLengthResolution , energyTableRadiusOuter
+ double precision , allocatable, dimension(:,:) :: energyTable
+ type (interpolator), allocatable :: energyTableLengthResolutionInterpolator , energyTableRadiusOuterInterpolator
+ double precision :: energyRadiusOuterMinimum , energyRadiusOuterMaximum , &
+ & energyLengthResolutionMinimum , energyLengthResolutionMaximum
+ ! Enclosed mass quantities.
+ double precision :: lengthResolutionScaleFreeLowerTerm , lengthResolutionScaleFreeSquared , &
+ & lengthResolutionScaleFreeCubed , lengthResolutionScaleFreeOnePlusTerm , &
+ & lengthResolutionScaleFreeOnePlus2Term , lengthResolutionScaleFreeSqrtTerm , &
+ & lengthResolutionScaleFreeSqrt2Term , lengthResolutionScaleFreeSqrtCubedTerm , &
+ & lengthResolutionScaleFreePrevious
+ contains
+ !![
+
+
+
+
+
+
+
+
+
+
+
+
+
+ !!]
+ procedure :: density => sphericalFiniteResolutionNFWDensity
+ procedure :: densityGradientRadial => sphericalFiniteResolutionNFWDensityGradientRadial
+ procedure :: massEnclosedBySphere => sphericalFiniteResolutionNFWMassEnclosedBySphere
+ procedure :: potentialIsAnalytic => sphericalFiniteResolutionNFWPotentialIsAnalytic
+ procedure :: potential => sphericalFiniteResolutionNFWPotential
+ procedure :: radiusEnclosingMass => sphericalFiniteResolutionNFWRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => sphericalFiniteResolutionNFWRadiusEnclosingDensity
+ procedure :: energy => sphericalFiniteResolutionNFWEnergy
+ procedure :: radiusEnclosingDensityTabulate => sphericalFiniteResolutionNFWRadiusEnclosingDensityTabulate
+ procedure :: radiusEnclosingMassTabulate => sphericalFiniteResolutionNFWRadiusEnclosingMassTabulate
+ procedure :: energyTabulate => sphericalFiniteResolutionNFWEnergyTabulate
+ procedure :: densityScaleFree => sphericalFiniteResolutionNFWDensityScaleFree
+ procedure :: massEnclosedScaleFree => sphericalFiniteResolutionNFWMassEnclosedScaleFree
+ procedure :: storeDensityTable => sphericalFiniteResolutionNFWStoreDensityTable
+ procedure :: restoreDensityTable => sphericalFiniteResolutionNFWRestoreDensityTable
+ procedure :: storeMassTable => sphericalFiniteResolutionNFWStoreMassTable
+ procedure :: restoreMassTable => sphericalFiniteResolutionNFWRestoreMassTable
+ procedure :: storeEnergyTable => sphericalFiniteResolutionNFWStoreEnergyTable
+ procedure :: restoreEnergyTable => sphericalFiniteResolutionNFWRestoreEnergyTable
+ end type massDistributionSphericalFiniteResolutionNFW
+
+ interface massDistributionSphericalFiniteResolutionNFW
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalFiniteResolutionNFW} mass distribution class.
+ !!}
+ module procedure sphericalFiniteResolutionNFWConstructorParameters
+ module procedure sphericalFiniteResolutionNFWConstructorInternal
+ end interface massDistributionSphericalFiniteResolutionNFW
+
+ ! Tabulation resolution parameters.
+ integer , parameter :: radiusEnclosingDensityTableDensityPointsPerDecade =100
+ integer , parameter :: radiusEnclosingDensityTableLengthResolutionPointsPerDecade=100
+ integer , parameter :: radiusEnclosingMassTableMassPointsPerDecade =100
+ integer , parameter :: radiusEnclosingMassTableLengthResolutionPointsPerDecade =100
+ integer , parameter :: energyTableRadiusOuterPointsPerDecade =100
+ integer , parameter :: energyTableLengthResolutionPointsPerDecade =100
+
+ ! Sub-module-scope variables used in integrations.
+ class (massDistributionSphericalFiniteResolutionNFW), pointer :: self_
+ integer :: iLengthResolution_ , iDensity_, &
+ & iMass_
+ !$omp threadprivate(self_,iLengthResolution_,iDensity_,iMass_)
+
+ ! Largest radius for precise arctanh() evaluation.
+ double precision , parameter :: radiusScaleFreeLargeATanh =1.0d+6
+contains
+
+ function sphericalFiniteResolutionNFWConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalFiniteResolutionNFW} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalFiniteResolutionNFW) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: lengthResolution, radiusScale, &
+ & radiusVirial , mass
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ lengthResolution
+
+ The resolution length scale.
+
+
+ radiusScale
+
+ The NFW scale radius.
+
+
+ radiusVirial
+
+ The virial radius.
+
+
+ mass
+
+ The mass within the virial radius.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+ !!]
+ self=massDistributionSphericalFiniteResolutionNFW(lengthResolution,radiusScale,radiusVirial,mass,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ !![
+
+ !!]
+ return
+ end function sphericalFiniteResolutionNFWConstructorParameters
+
+ function sphericalFiniteResolutionNFWConstructorInternal(lengthResolution,radiusScale,radiusVirial,mass,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalFiniteResolutionNFW'' mass distribution class.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ type (massDistributionSphericalFiniteResolutionNFW) :: self
+ double precision , intent(in ) :: lengthResolution , radiusScale, &
+ & radiusVirial , mass
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ self%dimensionless =.false.
+ self%lengthResolutionScalefreePrevious =-huge(0.0d0)
+ self%massEnclosedMassPrevious =-huge(0.0d0)
+ self%massEnclosedRadiusPrevious =-huge(0.0d0)
+ self%potentialPrevious =-huge(0.0d0)
+ self%potentialRadiusPrevious =-huge(0.0d0)
+ self%densityRadiusPrevious =-huge(0.0d0)
+ self%densityPrevious =-huge(0.0d0)
+ self%densityNormalizationPrevious =-huge(0.0d0)
+ self%radiusEnclosingDensityDensityPrevious =-huge(0.0d0)
+ self%radiusEnclosingDensityPrevious =-huge(0.0d0)
+ self%radiusEnclosingMassMassPrevious =-huge(0.0d0)
+ self%radiusEnclosingMassPrevious =-huge(0.0d0)
+ self%energyPrevious =+huge(0.0d0)
+ ! Radius enclosing density table initialization.
+ self%radiusEnclosingDensityDensityMinimum =+huge(0.0d0)
+ self%radiusEnclosingDensityDensityMaximum =-huge(0.0d0)
+ self%radiusEnclosingDensityLengthResolutionMinimum =+huge(0.0d0)
+ self%radiusEnclosingDensityLengthResolutionMaximum =-huge(0.0d0)
+ self%radiusEnclosingDensityTableInitialized =.false.
+ ! Radius enclosing mass table initialization.
+ self%radiusEnclosingMassMassMinimum =+huge(0.0d0)
+ self%radiusEnclosingMassMassMaximum =-huge(0.0d0)
+ self%radiusEnclosingMassLengthResolutionMinimum =+huge(0.0d0)
+ self%radiusEnclosingMassLengthResolutionMaximum =-huge(0.0d0)
+ self%radiusEnclosingMassTableInitialized =.false.
+ ! Energy table initialization.
+ self%energyRadiusOuterMinimum =+huge(0.0d0)
+ self%energyRadiusOuterMaximum =-huge(0.0d0)
+ self%energyLengthResolutionMinimum =+huge(0.0d0)
+ self%energyLengthResolutionMaximum =-huge(0.0d0)
+ self%energyTableInitialized =.false.
+ ! Construct profile quantities.
+ radiusScaleFree =+ radiusVirial/radiusScale
+ self%lengthResolutionScaleFree=+lengthResolution/radiusScale
+ self%densityNormalization =+mass/4.0d0/Pi/radiusScale**3/(log(1.0d0+radiusScaleFree)-radiusScaleFree/(1.0d0+radiusScaleFree))
+ return
+ end function sphericalFiniteResolutionNFWConstructorInternal
+
+ double precision function sphericalFiniteResolutionNFWDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radiusScaleFree
+
+ ! Compute the density at this position.
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %radiusScale
+ density =+self %densityNormalization &
+ & /sqrt(+self%lengthResolutionScaleFree**2+radiusScaleFree **2) &
+ & / (+1.0d0 +radiusScaleFree)**2
+ return
+ end function sphericalFiniteResolutionNFWDensity
+
+ double precision function sphericalFiniteResolutionNFWDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a finiteResolution spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radiusScaleFree
+ !![
+
+ !!]
+
+ radiusScaleFree=+coordinates%rSpherical () &
+ & /self %radiusScale
+ densityGradient=-3.0d0 &
+ & +2.0d0/(1.0d0+ radiusScaleFree ) &
+ & +1.0d0/(1.0d0+(radiusScaleFree/self%lengthResolutionScaleFree)**2)
+ if (.not.logarithmic_) &
+ densityGradient=+ densityGradient &
+ & *self %density (coordinates) &
+ & /coordinates%rSpherical ( )
+ return
+ end function sphericalFiniteResolutionNFWDensityGradientRadial
+
+ double precision function sphericalFiniteResolutionNFWMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Returns the enclosed mass (in $M_\odot$) at the given {\normalfont \ttfamily radius} (given in units of Mpc). The analytic
+ solution (computed using Mathematica) is
+ \begin{equation}
+ M(x) = 4 \pi \rho_0 r_\mathrm{s}^3 \left[ -\frac{\sqrt{x^2+X^2}}{(1+x) \left(1+X^2\right)}+\tanh ^{-1}\left(\frac{x}{\sqrt{x^2+X^2}}\right)+\frac{\left(1+2X^2\right) \tanh ^{-1}\left(\frac{X^2-x}{\sqrt{1+X^2} \sqrt{x^2+X^2}}\right)}{\left(1+X^2\right)^{3/2}} -\frac{\left(1 + 2 X^2\right) \tanh ^{-1}\left(\sqrt{\frac{X^2}{1 + X^2}}\right)}{\left(1+ X^2\right)^{3/2}}+\frac{\sqrt{X^2}}{1 + X^2} \right],
+ \end{equation}
+ where $x=r/r_\mathrm{s}$, $X = \Delta x/r_\mathrm{s}$, and $r_\mathrm{s}$ is the NFW scale length.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ double precision :: radiusScaleFree
+
+ radiusScaleFree=+ radius &
+ & /self%radiusScale
+ mass =+self%densityNormalization &
+ & *self%radiusScale **3 &
+ & *self%massEnclosedScaleFree(radiusScaleFree,self%lengthResolutionScaleFree)
+ return
+ end function sphericalFiniteResolutionNFWMassEnclosedBySphere
+
+ double precision function sphericalFiniteResolutionNFWMassEnclosedScaleFree(self,radiusScaleFree,lengthResolutionScaleFree) result(mass)
+ !!{
+ Returns the scale-free enclosed mass at the given scale-free radius. The analytic solution (computed using Mathematica) is
+ \begin{equation}
+ M(x) = 4 \pi \left[ -\frac{\sqrt{x^2+X^2}}{(1+x) \left(1+X^2\right)}+\tanh ^{-1}\left(\frac{x}{\sqrt{x^2+X^2}}\right)+\frac{\left(1+2X^2\right) \tanh ^{-1}\left(\frac{X^2-x}{\sqrt{1+X^2} \sqrt{x^2+X^2}}\right)}{\left(1+X^2\right)^{3/2}} -\frac{\left(1 + 2 X^2\right) \tanh ^{-1}\left(\sqrt{\frac{X^2}{1 + X^2}}\right)}{\left(1+ X^2\right)^{3/2}}+\frac{\sqrt{X^2}}{1 + X^2} \right],
+ \end{equation}
+ where $x=r/r_\mathrm{s}$, $X = \Delta x/r_\mathrm{s}$, and $r_\mathrm{s}$ is the NFW scale length.
+ !!}
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ double precision , intent(in ) :: radiusScaleFree , lengthResolutionScaleFree
+ double precision , parameter :: radiusScaleFreeSmall =1.0d-3, radiusScaleFreeLarge =1.0d4
+ double precision :: radiusScaleFreeEffective , arctanhTerm1 , &
+ & arctanhTerm
+
+ if (radiusScaleFree /= self%massEnclosedRadiusPrevious) then
+ self%massEnclosedRadiusPrevious=+radiusScaleFree
+ if (lengthResolutionScaleFree /= self%lengthResolutionScaleFreePrevious) then
+ ! Construct quantities used in the mass enclosed within a sphere.
+ self%lengthResolutionScaleFreePrevious = lengthResolutionScaleFree
+ self%lengthResolutionScaleFreeSquared =self%lengthResolutionScaleFreePrevious**2
+ self%lengthResolutionScaleFreeCubed =self%lengthResolutionScaleFreePrevious**3
+ self%lengthResolutionScaleFreeOnePlusTerm =+1.0d0+ self%lengthResolutionScaleFreeSquared
+ self%lengthResolutionScaleFreeOnePlus2Term =+1.0d0+2.0d0*self%lengthResolutionScaleFreeSquared
+ self%lengthResolutionScaleFreeSqrtTerm =sqrt(self%lengthResolutionScaleFreeOnePlusTerm )
+ self%lengthResolutionScaleFreeSqrt2Term =sqrt(self%lengthResolutionScaleFreeOnePlus2Term)
+ self%lengthResolutionScaleFreeSqrtCubedTerm=self%lengthResolutionScaleFreeSqrtTerm**3
+ ! For large values of the argument to arctanh(), use a series solution to avoiding floating point errors.
+ if (self%lengthResolutionScaleFreePrevious > radiusScaleFreeLargeATanh) then
+ arctanhTerm=-log( &
+ & +2.0d0 &
+ & *self%lengthResolutionScaleFreePrevious &
+ & ) &
+ & /2.0d0 &
+ & +1.0d0 &
+ & /2.0d0 &
+ & /self%lengthResolutionScaleFreePrevious &
+ & +1.0d0 &
+ & /8.0d0 &
+ & /self%lengthResolutionScaleFreePrevious**2
+ else
+ arctanhTerm=+atanh( &
+ & +(+1.0d0-self%lengthResolutionScaleFreePrevious) &
+ & /self%lengthResolutionScaleFreeSqrtTerm &
+ & )
+ end if
+ self%lengthResolutionScaleFreeLowerTerm=+self%lengthResolutionScaleFreePrevious &
+ & /self%lengthResolutionScaleFreeOnePlusTerm &
+ & +2.0d0 &
+ & *self%lengthResolutionScaleFreeOnePlus2Term &
+ & *arctanhTerm &
+ & /self%lengthResolutionScaleFreeSqrtCubedTerm
+ end if
+ if (radiusScaleFree < radiusScaleFreeSmall) then
+ ! Series expansion for small radii.
+ self%massEnclosedMassPrevious=+ radiusScaleFree**3 &
+ & *( &
+ & +1.0d0 /self%lengthResolutionScaleFreePrevious/ 3.0d0 &
+ & +radiusScaleFree *( +1.0d0 /self%lengthResolutionScaleFreePrevious/ 2.0d0 &
+ & +radiusScaleFree * ( 1.0d0+(+6.0d0*self%lengthResolutionScaleFreeSquared-1.0d0)/self%lengthResolutionScaleFreeCubed /10.0d0 &
+ & +radiusScaleFree * (1.0d0-(+4.0d0*self%lengthResolutionScaleFreeSquared-1.0d0)/self%lengthResolutionScaleFreeCubed / 6.0d0 &
+ & ) &
+ & ) &
+ & ) &
+ & )
+ else
+ ! Full analytic solution.
+ !! Limit the evaluation to some large radius.
+ radiusScaleFreeEffective=min(radiusScaleFree,radiusScaleFreeLarge)
+ if (radiusScaleFreeEffective > radiusScaleFreeLargeATanh*self%lengthResolutionScaleFreePrevious) then
+ arctanhTerm1=+log ( &
+ & +4.0d0 &
+ & *radiusScaleFreeEffective**2 &
+ & /self%lengthResolutionScaleFreeSquared &
+ & ) &
+ & /2.0d0 &
+ & -self%lengthResolutionScaleFreeSquared &
+ & /8.0d0 &
+ & / radiusScaleFreeEffective**2
+ else
+ arctanhTerm1=+atanh( &
+ & +radiusScaleFreeEffective &
+ & /sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreeSquared) &
+ & )
+ end if
+ self%massEnclosedMassPrevious=- sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreePrevious**2) &
+ & /(+1.0d0+radiusScaleFreeEffective) &
+ & /self%lengthResolutionScaleFreeOnePlusTerm &
+ & -2.0d0 &
+ & *self%lengthResolutionScaleFreeOnePlus2Term &
+ & *atanh( &
+ & +( &
+ & +1.0d0 &
+ & +radiusScaleFreeEffective &
+ & -sqrt(+radiusScaleFreeEffective**2+self%lengthResolutionScaleFreePrevious**2) &
+ & ) &
+ & /self%lengthResolutionScaleFreeSqrtTerm &
+ & ) &
+ & /self%lengthResolutionScaleFreeSqrtCubedTerm &
+ & +arctanhTerm1 &
+ & +self%lengthResolutionScaleFreeLowerTerm
+ !! Beyond the limiting radius assume logarithmic growth in mass as appropriate for an r⁻³ profile.
+ if (radiusScaleFree > radiusScaleFreeEffective) &
+ & self%massEnclosedMassPrevious=+self%massEnclosedMassPrevious &
+ & *log( &
+ & +radiusScaleFree &
+ & /radiusScaleFreeEffective &
+ & )
+ end if
+ self%massEnclosedMassPrevious=+4.0d0 &
+ & *Pi &
+ & *self%massEnclosedMassPrevious
+ end if
+ mass=self%massEnclosedMassPrevious
+ return
+ end function sphericalFiniteResolutionNFWMassEnclosedScaleFree
+
+ logical function sphericalFiniteResolutionNFWPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function sphericalFiniteResolutionNFWPotentialIsAnalytic
+
+ double precision function sphericalFiniteResolutionNFWPotential(self,coordinates,status) result(potential)
+ !!{
+ Returns the potential (in (km/s)$^2$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont
+ \ttfamily radius} (given in units of Mpc). The analytic solution (computed using Mathematica) is
+ \begin{eqnarray}
+ \Phi(x) &=& -\frac{\mathrm{G} M}{r_\mathrm{s}} \nonumber \\
+ & & \left\{ +\frac{\sqrt{x^2+X^2}}{x \left(X^2+1\right)} \right. \nonumber \\
+ & & -\frac{X^2 \log \left(\sqrt{X^2+1} \sqrt{x^2+X^2}-x+X^2\right)}{\left(X^2+1\right)^{3/2}} \nonumber \\
+ & & -\frac{\tanh ^{-1}\left(\frac{x}{\sqrt{x^2+X^2}}\right)}{x} \nonumber \\
+ & & -\frac{\left(2 X^2+1\right) \tanh ^{-1}\left(\frac{X^2-x}{\sqrt{X^2+1} \sqrt{x^2+X^2}}\right)}{x \left(X^2+1\right)^{3/2}} \nonumber \\
+ & & -\frac{\sqrt{X^2}}{x \left(X^2+1\right)}+\frac{X^2 \log (x+1)}{\left(X^2+1\right)^{3/2}} \nonumber \\
+ & & +\frac{\left(2 X^2+1\right) \tanh ^{-1}\left(\sqrt{\frac{X^2}{X^2+1}}\right)}{x \left(X^2+1\right)^{3/2}} \nonumber \\
+ & & \left. +\frac{ \left(\sqrt{X^2+1}-X^2 \log \left(\sqrt{X^2+1}-1\right)\right)}{\left(X^2+1\right)^{3/2}} \right\} \nonumber \\
+ & & /\left[\log (1+c)-\frac{c}{1+c}\right]
+ \end{eqnarray}
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType ), intent( out), optional :: status
+ double precision , parameter :: radiusScaleFreeSmall=1.0d-3
+ double precision :: radiusScaleFree
+
+ if (present(status)) status=structureErrorCodeSuccess
+ if (coordinates%rSpherical()/= self%potentialRadiusPrevious) then
+ self%potentialRadiusPrevious=+coordinates%rSpherical ()
+ radiusScaleFree =+coordinates%rSpherical () &
+ & /self %radiusScale
+ if (radiusScaleFree < radiusScaleFreeSmall) then
+ ! Series expansion for small radii.
+ self%potentialPrevious = -4.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & *self%radiusScale **2 &
+ & *( &
+ & +(+1.0d0-self%lengthResolutionScaleFree ) &
+ & /(+1.0d0+self%lengthResolutionScaleFree**2) &
+ & + self%lengthResolutionScaleFree**2 &
+ & *( &
+ & +asinh(self%lengthResolutionScaleFree ) &
+ & +log ( &
+ & +(1.0d0+sqrt(+1.0d0+self%lengthResolutionScaleFree**2)) &
+ & / self%lengthResolutionScaleFree &
+ & ) &
+ & ) &
+ & /(+1.0d0+self%lengthResolutionScaleFree**2)**1.5d0 &
+ & - radiusScaleFree**2 &
+ & *(+1.0d0-radiusScaleFree ) &
+ & / self%lengthResolutionScaleFree &
+ & /6.0d0 &
+ & )
+ else
+ self%potentialPrevious = -4.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *self%densityNormalization &
+ & *self%radiusScale **2 &
+ & *( &
+ & + sqrt( self%lengthResolutionScaleFree**2 ) &
+ & / radiusScaleFree /(+1.0d0+ self%lengthResolutionScaleFree**2) &
+ & - sqrt( +radiusScaleFree**2+self%lengthResolutionScaleFree**2 ) &
+ & / radiusScaleFree /(+1.0d0+ self%lengthResolutionScaleFree**2) &
+ & - (+1.0d0+2.0d0*self%lengthResolutionScaleFree**2) &
+ & *atanh( &
+ & +sqrt( +self%lengthResolutionScaleFree**2/(+1.0d0+ self%lengthResolutionScaleFree**2) ) &
+ & ) &
+ & / radiusScaleFree /(+1.0d0+ self%lengthResolutionScaleFree**2)**1.5d0 &
+ & +atanh( &
+ & + radiusScaleFree &
+ & /sqrt( +radiusScaleFree**2+self%lengthResolutionScaleFree**2 ) &
+ & ) &
+ & / radiusScaleFree &
+ & + (+1.0d0+2.0d0*self%lengthResolutionScaleFree**2) &
+ & *atanh( &
+ & ( -radiusScaleFree +self%lengthResolutionScaleFree**2 ) &
+ & /sqrt (+1.0d0+ self%lengthResolutionScaleFree**2) &
+ & /sqrt( +radiusScaleFree**2+self%lengthResolutionScaleFree**2 ) &
+ & ) &
+ & / radiusScaleFree /(+1.0d0+ self%lengthResolutionScaleFree**2)**1.5d0 &
+ & - self%lengthResolutionScaleFree**2 &
+ & *log ( &
+ & +1.0d0+radiusScaleFree &
+ & ) &
+ & / (+1.0d0+ self%lengthResolutionScaleFree**2)**1.5d0 &
+ & + self%lengthResolutionScaleFree**2/(+1.0d0+ self%lengthResolutionScaleFree**2)**1.5d0 &
+ & *log ( &
+ & -radiusScaleFree +self%lengthResolutionScaleFree**2 &
+ & +sqrt(+1.0d0 +self%lengthResolutionScaleFree**2 ) &
+ & *sqrt( +radiusScaleFree**2+self%lengthResolutionScaleFree**2 ) &
+ & ) &
+ & +( &
+ & + sqrt(+1.0d0 +self%lengthResolutionScaleFree**2 ) &
+ & - self%lengthResolutionScaleFree**2 &
+ & *log( &
+ & -1.0d0 &
+ & +sqrt(+1.0d0 +self%lengthResolutionScaleFree**2 ) &
+ & ) &
+ & ) &
+ & / (+1.0d0+ self%lengthResolutionScaleFree**2)**1.5d0 &
+ & )
+ end if
+ end if
+ potential=self%potentialPrevious
+ return
+ end function sphericalFiniteResolutionNFWPotential
+
+ double precision function sphericalFiniteResolutionNFWRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for finite-resolution NFW distributions.
+ !!}
+ use :: Error , only : Error_Report
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ integer (c_size_t ), dimension(0:1) :: jLengthResolution
+ double precision , dimension(0:1) :: hLengthResolution
+ integer :: iLengthResolution
+ double precision :: mass_ , massScaleFree
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_=mass
+ else if (present(massFractional)) then
+ call Error_Report('mass is unbounded, so mass fraction is undefined'//{introspection:location})
+ else
+ call Error_Report('either mass or massFractional must be supplied' //{introspection:location})
+ end if
+ if (mass /= self%radiusEnclosingMassMassPrevious) then
+ self%radiusEnclosingMassMassPrevious=mass
+ ! Find scale free mass, and the maximum such mass reached in the profile.
+ massScaleFree=+ mass &
+ & /self%densityNormalization &
+ & /self%radiusScale **3
+ ! Ensure table is sufficiently extensive.
+ call self%radiusEnclosingMassTabulate(massScaleFree,self%lengthResolutionScaleFree)
+ ! Interpolate to get the scale free radius enclosing the scale free mass.
+ call self%radiusEnclosingMassTableLengthResolutionInterpolator%linearFactors(self%lengthResolutionScaleFree,jLengthResolution(0),hLengthResolution)
+ jLengthResolution(1)=jLengthResolution(0)+1
+ self%radiusEnclosingMassPrevious=0.0d0
+ do iLengthResolution=0,1
+ self%radiusEnclosingMassPrevious=+self%radiusEnclosingMassPrevious &
+ & +self%radiusEnclosingMassTableMassInterpolator%interpolate(massScaleFree,self%radiusEnclosingMassTable(:,jLengthResolution(iLengthResolution))) &
+ & * hLengthResolution(iLengthResolution)
+ end do
+ self%radiusEnclosingMassPrevious=+self%radiusEnclosingMassPrevious &
+ & *self%radiusScale
+ end if
+ radius=self%radiusEnclosingMassPrevious
+ return
+ end function sphericalFiniteResolutionNFWRadiusEnclosingMass
+
+ subroutine sphericalFiniteResolutionNFWRadiusEnclosingMassTabulate(self,mass,lengthResolution)
+ !!{
+ Tabulates the radius enclosing a given mass for finite resolution NFW mass profiles.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: mass , lengthResolution
+ double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-9
+ logical :: retabulate
+ integer :: iLengthResolution , iMass , &
+ & i
+ type (rootFinder ) :: finder
+
+ do i=1,2
+ retabulate=.false.
+ if (.not.self%radiusEnclosingMassTableInitialized) then
+ retabulate=.true.
+ else if ( &
+ & mass < self%radiusEnclosingMassMassMinimum &
+ & .or. &
+ & mass > self%radiusEnclosingMassMassMaximum &
+ & .or. &
+ & lengthResolution < self%radiusEnclosingMassLengthResolutionMinimum &
+ & .or. &
+ & lengthResolution > self%radiusEnclosingMassLengthResolutionMaximum &
+ & ) then
+ retabulate=.true.
+ end if
+ if (retabulate .and.i==1) call self%restoreMassTable()
+ if (.not.retabulate ) exit
+ end do
+ if (retabulate) then
+ ! Decide how many points to tabulate and allocate table arrays.
+ self%radiusEnclosingMassMassMinimum =min(0.5d0*mass ,self%radiusEnclosingMassMassMinimum )
+ self%radiusEnclosingMassMassMaximum =max(2.0d0*mass ,self%radiusEnclosingMassMassMaximum )
+ self%radiusEnclosingMassLengthResolutionMinimum =min(0.5d0*lengthResolution,self%radiusEnclosingMassLengthResolutionMinimum)
+ self%radiusEnclosingMassLengthResolutionMaximum =max(2.0d0*lengthResolution,self%radiusEnclosingMassLengthResolutionMaximum)
+ self%radiusEnclosingMassTableMassCount =int(log10(self%radiusEnclosingMassMassMaximum /self%radiusEnclosingMassMassMinimum )*dble(radiusEnclosingMassTableMassPointsPerDecade ))+1
+ self%radiusEnclosingMassTableLengthResolutionCount=int(log10(self%radiusEnclosingMassLengthResolutionMaximum/self%radiusEnclosingMassLengthResolutionMinimum)*dble(radiusEnclosingMassTableLengthResolutionPointsPerDecade))+1
+ if (allocated(self%radiusEnclosingMassTableMass)) then
+ deallocate(self%radiusEnclosingMassTableLengthResolution)
+ deallocate(self%radiusEnclosingMassTableMass )
+ deallocate(self%radiusEnclosingMassTable )
+ end if
+ allocate(self%radiusEnclosingMassTableLengthResolution( self%radiusEnclosingMassTableLengthResolutionCount))
+ allocate(self%radiusEnclosingMassTableMass (self%radiusEnclosingMassTableMassCount ))
+ allocate(self%radiusEnclosingMassTable (self%radiusEnclosingMassTableMassCount,self%radiusEnclosingMassTableLengthResolutionCount))
+ ! Create a range of radii and core radii.
+ self%radiusEnclosingMassTableMass =Make_Range(self%radiusEnclosingMassMassMinimum ,self%radiusEnclosingMassMassMaximum ,self%radiusEnclosingMassTableMassCount ,rangeType=rangeTypeLogarithmic)
+ self%radiusEnclosingMassTableLengthResolution=Make_Range(self%radiusEnclosingMassLengthResolutionMinimum,self%radiusEnclosingMassLengthResolutionMaximum,self%radiusEnclosingMassTableLengthResolutionCount,rangeType=rangeTypeLogarithmic)
+ ! Initialize our root finder.
+ finder=rootFinder( &
+ & rootFunction =rootMass , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative &
+ & )
+ ! Loop over mass and core radius and populate tables.
+ self_ => self
+ do iLengthResolution=1,self%radiusEnclosingMassTableLengthResolutionCount
+ iLengthResolution_=iLengthResolution
+ do iMass=1,self%radiusEnclosingMassTableMassCount
+ iMass_=iMass
+ ! Check that the root condition is satisfied at infinitely large radius. If it is not, then no radius encloses the
+ ! required mass. Simply set the radius to an infinitely large value in such case.
+ if (rootMass(radius=huge(0.0d0)) < 0.0d0) then
+ self%radiusEnclosingMassTable(iMass,iLengthResolution)=huge(0.0d0)
+ else
+ self%radiusEnclosingMassTable(iMass,iLengthResolution)=finder%find(rootGuess=1.0d0)
+ end if
+ end do
+ end do
+ ! Build interpolators.
+ if (allocated(self%radiusEnclosingMassTableLengthResolutionInterpolator)) deallocate(self%radiusEnclosingMassTableLengthResolutionInterpolator)
+ if (allocated(self%radiusEnclosingMassTableMassInterpolator )) deallocate(self%radiusEnclosingMassTableMassInterpolator )
+ allocate(self%radiusEnclosingMassTableLengthResolutionInterpolator)
+ allocate(self%radiusEnclosingMassTableMassInterpolator )
+ self%radiusEnclosingMassTableLengthResolutionInterpolator=interpolator(self%radiusEnclosingMassTableLengthResolution)
+ self%radiusEnclosingMassTableMassInterpolator =interpolator(self%radiusEnclosingMassTableMass )
+ ! Specify that tabulation has been made.
+ self%radiusEnclosingMassTableInitialized=.true.
+ call self%storeMassTable()
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWRadiusEnclosingMassTabulate
+
+ double precision function rootMass(radius)
+ !!{
+ Root function used in finding the radius enclosing a given mean mass.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ rootMass=+self_%massEnclosedScaleFree (radius,self_%radiusEnclosingMassTableLengthResolution(iLengthResolution_)) &
+ & -self_%radiusEnclosingMassTableMass( iMass_ )
+ return
+ end function rootMass
+
+ subroutine sphericalFiniteResolutionNFWStoreMassTable(self)
+ !!{
+ Store the tabulated radius-enclosing-mass data to file.
+ !!}
+ use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
+ & File_Path
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//) , char
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Mass_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ call Directory_Make(char(File_Path(char(fileName))))
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
+ call file%writeDataset(self%radiusEnclosingMassTableLengthResolution,'lengthResolution')
+ call file%writeDataset(self%radiusEnclosingMassTableMass ,'mass' )
+ call file%writeDataset(self%radiusEnclosingMassTable ,'radius' )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ return
+ end subroutine sphericalFiniteResolutionNFWStoreMassTable
+
+ subroutine sphericalFiniteResolutionNFWRestoreMassTable(self)
+ !!{
+ Restore the tabulated radius-enclosing-mass data from file, returning true if successful.
+ !!}
+ use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//)
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Mass_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ if (File_Exists(fileName)) then
+ if (allocated(self%radiusEnclosingMassTableMass)) then
+ deallocate(self%radiusEnclosingMassTableLengthResolution)
+ deallocate(self%radiusEnclosingMassTableMass )
+ deallocate(self%radiusEnclosingMassTable )
+ end if
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName))
+ call file%readDataset('lengthResolution',self%radiusEnclosingMassTableLengthResolution)
+ call file%readDataset('mass' ,self%radiusEnclosingMassTableMass )
+ call file%readDataset('radius' ,self%radiusEnclosingMassTable )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ self%radiusEnclosingMassTableMassCount =size(self%radiusEnclosingMassTableMass )
+ self%radiusEnclosingMassTableLengthResolutionCount=size(self%radiusEnclosingMassTableLengthResolution)
+ self%radiusEnclosingMassMassMinimum =self%radiusEnclosingMassTableMass ( 1)
+ self%radiusEnclosingMassMassMaximum =self%radiusEnclosingMassTableMass (self%radiusEnclosingMassTableMassCount )
+ self%radiusEnclosingMassLengthResolutionMinimum =self%radiusEnclosingMassTableLengthResolution( 1)
+ self%radiusEnclosingMassLengthResolutionMaximum =self%radiusEnclosingMassTableLengthResolution(self%radiusEnclosingMassTableLengthResolutionCount)
+ if (allocated(self%radiusEnclosingMassTableLengthResolutionInterpolator)) deallocate(self%radiusEnclosingMassTableLengthResolutionInterpolator)
+ if (allocated(self%radiusEnclosingMassTableMassInterpolator )) deallocate(self%radiusEnclosingMassTableMassInterpolator )
+ allocate(self%radiusEnclosingMassTableLengthResolutionInterpolator)
+ allocate(self%radiusEnclosingMassTableMassInterpolator )
+ self%radiusEnclosingMassTableLengthResolutionInterpolator=interpolator(self%radiusEnclosingMassTableLengthResolution)
+ self%radiusEnclosingMassTableMassInterpolator =interpolator(self%radiusEnclosingMassTableMass )
+ self%radiusEnclosingMassTableInitialized =.true.
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWRestoreMassTable
+
+ double precision function sphericalFiniteResolutionNFWRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for finite-resolution NFW mass distributions.
+ !!}
+ use :: Numerical_Ranges, only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+ double precision , parameter :: epsilonDensity =1.0d-3
+ double precision :: densityScaleFreeMaximum , densityScaleFree
+ integer (c_size_t ), dimension(0:1) :: jLengthResolution
+ double precision , dimension(0:1) :: hLengthResolution
+ integer :: iLengthResolution
+
+ if (density /= self%radiusEnclosingDensityDensityPrevious) then
+ self%radiusEnclosingDensityDensityPrevious=density
+ ! Find scale free density, and the maximum such density reached in the profile.
+ densityScaleFree =+ density &
+ & /self%densityNormalization
+ densityScaleFreeMaximum=+1.0d0 &
+ & /self%lengthResolutionScaleFree
+ if (densityScaleFree >= densityScaleFreeMaximum) then
+ ! Maximum density is exceeded - return zero radius.
+ self%radiusEnclosingDensityPrevious=0.0d0
+ else if (densityScaleFree >= densityScaleFreeMaximum*(1.0d0-epsilonDensity)) then
+ ! For densities close to the maximum density, use a series solution.
+ self%radiusEnclosingDensityPrevious=+0.5d0 &
+ & *( &
+ & +1.0d0 &
+ & -densityScaleFree &
+ & /densityScaleFreeMaximum &
+ & ) &
+ & *self%radiusScale
+ else
+ ! Use a tabulated solution in other regimes.
+ ! Ensure table is sufficiently extensive.
+ call self%radiusEnclosingDensityTabulate(densityScaleFree,self%lengthResolutionScaleFree)
+ ! Interpolate to get the scale free radius enclosing the scale free density.
+ call self%radiusEnclosingDensityTableLengthResolutionInterpolator%linearFactors(self%lengthResolutionScaleFree,jLengthResolution(0),hLengthResolution)
+ jLengthResolution(1)=jLengthResolution(0)+1
+ self%radiusEnclosingDensityPrevious=0.0d0
+ do iLengthResolution=0,1
+ self%radiusEnclosingDensityPrevious=+self%radiusEnclosingDensityPrevious &
+ & +self%radiusEnclosingDensityTableDensityInterpolator%interpolate(densityScaleFree,self%radiusEnclosingDensityTable(:,jLengthResolution(iLengthResolution))) &
+ & * hLengthResolution(iLengthResolution)
+ end do
+ self%radiusEnclosingDensityPrevious=+self%radiusEnclosingDensityPrevious &
+ & *self%radiusScale
+ end if
+ end if
+ radius=self%radiusEnclosingDensityPrevious
+ return
+ end function sphericalFiniteResolutionNFWRadiusEnclosingDensity
+
+ subroutine sphericalFiniteResolutionNFWRadiusEnclosingDensityTabulate(self,density,lengthResolution)
+ !!{
+ Tabulates the radius enclosing a given density for finite resolution NFW density profiles.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: density , lengthResolution
+ double precision , parameter :: toleranceAbsolute=0.0d0, toleranceRelative=1.0d-9
+ logical :: retabulate
+ integer :: iLengthResolution , iDensity , &
+ & i
+ type (rootFinder ) :: finder
+
+ do i=1,2
+ retabulate=.false.
+ if (.not.self%radiusEnclosingDensityTableInitialized) then
+ retabulate=.true.
+ else if ( &
+ & density < self%radiusEnclosingDensityDensityMinimum &
+ & .or. &
+ & density > self%radiusEnclosingDensityDensityMaximum &
+ & .or. &
+ & lengthResolution < self%radiusEnclosingDensityLengthResolutionMinimum &
+ & .or. &
+ & lengthResolution > self%radiusEnclosingDensityLengthResolutionMaximum &
+ & ) then
+ retabulate=.true.
+ end if
+ if (retabulate .and.i==1) call self%restoreDensityTable()
+ if (.not.retabulate ) exit
+ end do
+ if (retabulate) then
+ ! Decide how many points to tabulate and allocate table arrays.
+ self%radiusEnclosingDensityDensityMinimum =min(0.5d0*density ,self%radiusEnclosingDensityDensityMinimum )
+ self%radiusEnclosingDensityDensityMaximum =max(2.0d0*density ,self%radiusEnclosingDensityDensityMaximum )
+ self%radiusEnclosingDensityLengthResolutionMinimum =min(0.5d0*lengthResolution,self%radiusEnclosingDensityLengthResolutionMinimum)
+ self%radiusEnclosingDensityLengthResolutionMaximum =max(2.0d0*lengthResolution,self%radiusEnclosingDensityLengthResolutionMaximum)
+ self%radiusEnclosingDensityTableDensityCount =int(log10(self%radiusEnclosingDensityDensityMaximum /self%radiusEnclosingDensityDensityMinimum )*dble(radiusEnclosingDensityTableDensityPointsPerDecade ))+1
+ self%radiusEnclosingDensityTableLengthResolutionCount=int(log10(self%radiusEnclosingDensityLengthResolutionMaximum/self%radiusEnclosingDensityLengthResolutionMinimum)*dble(radiusEnclosingDensityTableLengthResolutionPointsPerDecade))+1
+ if (allocated(self%radiusEnclosingDensityTableDensity)) then
+ deallocate(self%radiusEnclosingDensityTableLengthResolution)
+ deallocate(self%radiusEnclosingDensityTableDensity )
+ deallocate(self%radiusEnclosingDensityTable )
+ end if
+ allocate(self%radiusEnclosingDensityTableLengthResolution( self%radiusEnclosingDensityTableLengthResolutionCount))
+ allocate(self%radiusEnclosingDensityTableDensity (self%radiusEnclosingDensityTableDensityCount ))
+ allocate(self%radiusEnclosingDensityTable (self%radiusEnclosingDensityTabledensityCount,self%radiusEnclosingDensityTableLengthResolutionCount))
+ ! Create a range of radii and core radii.
+ self%radiusEnclosingDensityTableDensity =Make_Range(self%radiusEnclosingDensityDensityMinimum ,self%radiusEnclosingDensityDensityMaximum ,self%radiusEnclosingDensityTableDensityCount ,rangeType=rangeTypeLogarithmic)
+ self%radiusEnclosingDensityTableLengthResolution=Make_Range(self%radiusEnclosingDensityLengthResolutionMinimum,self%radiusEnclosingDensityLengthResolutionMaximum,self%radiusEnclosingDensityTableLengthResolutionCount,rangeType=rangeTypeLogarithmic)
+ ! Initialize our root finder.
+ finder=rootFinder( &
+ & rootFunction =rootDensity , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive &
+ & )
+ ! Loop over density and core radius and populate tables.
+ self_ => self
+ do iLengthResolution=1,self%radiusEnclosingDensityTableLengthResolutionCount
+ iLengthResolution_=iLengthResolution
+ do iDensity=1,self%radiusEnclosingDensityTableDensityCount
+ iDensity_=iDensity
+ if (self%radiusEnclosingDensityTableDensity(iDensity) > 1.0d0/self%radiusEnclosingDensityTableLengthResolution(iLengthResolution)) then
+ ! Density exceeds the maximum density in the profile - so set zero radius.
+ self%radiusEnclosingDensityTable(iDensity,iLengthResolution)=0.0d0
+ else
+ self%radiusEnclosingDensityTable(iDensity,iLengthResolution)=finder%find(rootGuess=1.0d0)
+ end if
+ end do
+ end do
+ ! Build interpolators.
+ if (allocated(self%radiusEnclosingDensityTableLengthResolutionInterpolator)) deallocate(self%radiusEnclosingDensityTableLengthResolutionInterpolator)
+ if (allocated(self%radiusEnclosingDensityTableDensityInterpolator )) deallocate(self%radiusEnclosingDensityTableDensityInterpolator )
+ allocate(self%radiusEnclosingDensityTableLengthResolutionInterpolator)
+ allocate(self%radiusEnclosingDensityTableDensityInterpolator )
+ self%radiusEnclosingDensityTableLengthResolutionInterpolator=interpolator(self%radiusEnclosingDensityTableLengthResolution)
+ self%radiusEnclosingDensityTableDensityInterpolator =interpolator(self%radiusEnclosingDensityTableDensity )
+ ! Specify that tabulation has been made.
+ self%radiusEnclosingDensityTableInitialized=.true.
+ call self%storeDensityTable()
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWRadiusEnclosingDensityTabulate
+
+ double precision function rootDensity(radius)
+ !!{
+ Root function used in finding the radius enclosing a given mean density.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision, intent(in ) :: radius
+
+ rootDensity=+3.0d0 &
+ & *self_%massEnclosedScaleFree (radius,self_%radiusEnclosingDensityTableLengthResolution(iLengthResolution_)) &
+ & /4.0d0 &
+ & /Pi &
+ & / radius **3 &
+ & -self_%radiusEnclosingDensityTableDensity( iDensity_ )
+ return
+ end function rootDensity
+
+ subroutine sphericalFiniteResolutionNFWStoreDensityTable(self)
+ !!{
+ Store the tabulated radius-enclosing-density data to file.
+ !!}
+ use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
+ & File_Path
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//) , char
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Density_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ call Directory_Make(char(File_Path(char(fileName))))
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
+ call file%writeDataset(self%radiusEnclosingDensityTableLengthResolution,'lengthResolution')
+ call file%writeDataset(self%radiusEnclosingDensityTableDensity ,'density' )
+ call file%writeDataset(self%radiusEnclosingDensityTable ,'radius' )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ return
+ end subroutine sphericalFiniteResolutionNFWStoreDensityTable
+
+ subroutine sphericalFiniteResolutionNFWRestoreDensityTable(self)
+ !!{
+ Restore the tabulated radius-enclosing-density data from file, returning true if successful.
+ !!}
+ use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//)
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Density_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ if (File_Exists(fileName)) then
+ if (allocated(self%radiusEnclosingDensityTableDensity)) then
+ deallocate(self%radiusEnclosingDensityTableLengthResolution)
+ deallocate(self%radiusEnclosingDensityTableDensity )
+ deallocate(self%radiusEnclosingDensityTable )
+ end if
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName))
+ call file%readDataset('lengthResolution',self%radiusEnclosingDensityTableLengthResolution)
+ call file%readDataset('density' ,self%radiusEnclosingDensityTableDensity )
+ call file%readDataset('radius' ,self%radiusEnclosingDensityTable )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ self%radiusEnclosingDensityTableDensityCount =size(self%radiusEnclosingDensityTableDensity )
+ self%radiusEnclosingDensityTableLengthResolutionCount=size(self%radiusEnclosingDensityTableLengthResolution)
+ self%radiusEnclosingDensityDensityMinimum =self%radiusEnclosingDensityTableDensity ( 1)
+ self%radiusEnclosingDensityDensityMaximum =self%radiusEnclosingDensityTableDensity (self%radiusEnclosingDensityTableDensityCount )
+ self%radiusEnclosingDensityLengthResolutionMinimum =self%radiusEnclosingDensityTableLengthResolution( 1)
+ self%radiusEnclosingDensityLengthResolutionMaximum =self%radiusEnclosingDensityTableLengthResolution(self%radiusEnclosingDensityTableLengthResolutionCount)
+ if (allocated(self%radiusEnclosingDensityTableLengthResolutionInterpolator)) deallocate(self%radiusEnclosingDensityTableLengthResolutionInterpolator)
+ if (allocated(self%radiusEnclosingDensityTableDensityInterpolator )) deallocate(self%radiusEnclosingDensityTableDensityInterpolator )
+ allocate(self%radiusEnclosingDensityTableLengthResolutionInterpolator)
+ allocate(self%radiusEnclosingDensityTableDensityInterpolator )
+ self%radiusEnclosingDensityTableLengthResolutionInterpolator=interpolator(self%radiusEnclosingDensityTableLengthResolution)
+ self%radiusEnclosingDensityTableDensityInterpolator =interpolator(self%radiusEnclosingDensityTableDensity )
+ self%radiusEnclosingDensityTableInitialized =.true.
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWRestoreDensityTable
+
+ double precision function sphericalFiniteResolutionNFWEnergy(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the energy within a given {\normalfont \ttfamily radius} in a finite-resolution NFW mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout) , target :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) , target :: massDistributionEmbedding
+ integer (c_size_t ), dimension(0:1) :: jLengthResolution
+ double precision , dimension(0:1) :: hLengthResolution
+ integer :: iLengthResolution
+
+ if (self%energyPrevious > 0.0d0) then
+ ! Ensure table is sufficiently extensive.
+ call self%energyTabulate(self%lengthResolutionScaleFree,radiusOuter/self%radiusScale)
+ ! Interpolate to get the scale free energy.
+ call self%energyTableLengthResolutionInterpolator%linearFactors(self%lengthResolutionScaleFree,jLengthResolution(0),hLengthResolution)
+ jLengthResolution(1)=jLengthResolution(0)+1
+ self%energyPrevious=0.0d0
+ do iLengthResolution=0,1
+ self%energyPrevious=+self%energyPrevious &
+ & +self%energyTableRadiusOuterInterpolator%interpolate(radiusOuter/self%radiusScale,self%energyTable(:,jLengthResolution(iLengthResolution))) &
+ & * hLengthResolution(iLengthResolution)
+ end do
+ self%energyPrevious=+self %energyPrevious &
+ & *gravitationalConstantGalacticus &
+ & *self %densityNormalization**2 &
+ & *self %radiusScale **5
+ end if
+ energy=self%energyPrevious
+ return
+ end function sphericalFiniteResolutionNFWEnergy
+
+ subroutine sphericalFiniteResolutionNFWEnergyTabulate(self,lengthResolution,radiusOuter)
+ !!{
+ Tabulates the energy for finite resolution NFW mass profiles.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Integration , only : integrator
+ use :: Numerical_Ranges , only : Make_Range, rangeTypeLogarithmic
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout), target :: self
+ double precision , intent(in ) :: radiusOuter , lengthResolution
+ double precision , parameter :: multiplierRadius =100.0d0
+ type (integrator ) :: integratorPotential , integratorKinetic, &
+ & integratorPressure
+ double precision :: pseudoPressure , energyKinetic , &
+ & energyPotential , radiusOuter_
+ logical :: retabulate
+ integer :: iLengthResolution , iRadiusOuter , &
+ & i
+
+ do i=1,2
+ retabulate=.false.
+ if (.not.self%energyTableInitialized) then
+ retabulate=.true.
+ else if ( &
+ & radiusOuter < self%energyRadiusOuterMinimum &
+ & .or. &
+ & radiusOuter > self%energyRadiusOuterMaximum &
+ & .or. &
+ & lengthResolution < self%energyLengthResolutionMinimum &
+ & .or. &
+ & lengthResolution > self%energyLengthResolutionMaximum &
+ & ) then
+ retabulate=.true.
+ end if
+ if ( retabulate.and.i==1) call self%restoreEnergyTable()
+ if (.not.retabulate ) exit
+ end do
+ if (retabulate) then
+ ! Decide how many points to tabulate and allocate table arrays.
+ self%energyRadiusOuterMinimum =min(0.5d0*radiusOuter ,self%energyRadiusOuterMinimum )
+ self%energyRadiusOuterMaximum =max(2.0d0*radiusOuter ,self%energyRadiusOuterMaximum )
+ self%energyLengthResolutionMinimum =min(0.5d0*lengthResolution,self%energyLengthResolutionMinimum)
+ self%energyLengthResolutionMaximum =max(2.0d0*lengthResolution,self%energyLengthResolutionMaximum)
+ self%energyTableRadiusOuterCount =int(log10(self%energyRadiusOuterMaximum /self%energyRadiusOuterMinimum )*dble(energyTableRadiusOuterPointsPerDecade ))+1
+ self%energyTableLengthResolutionCount=int(log10(self%energyLengthResolutionMaximum/self%energyLengthResolutionMinimum)*dble(energyTableLengthResolutionPointsPerDecade))+1
+ if (allocated(self%energyTableRadiusOuter)) then
+ deallocate(self%energyTableLengthResolution)
+ deallocate(self%energyTableRadiusOuter )
+ deallocate(self%energyTable )
+ end if
+ allocate(self%energyTableLengthResolution( self%energyTableLengthResolutionCount))
+ allocate(self%energyTableRadiusOuter (self%energyTableRadiusOuterCount ))
+ allocate(self%energyTable (self%energyTableradiusOuterCount,self%energyTableLengthResolutionCount))
+ ! Create a range of radii and core radii.
+ self%energyTableRadiusOuter =Make_Range(self%energyRadiusOuterMinimum ,self%energyRadiusOuterMaximum ,self%energyTableRadiusOuterCount ,rangeType=rangeTypeLogarithmic)
+ self%energyTableLengthResolution=Make_Range(self%energyLengthResolutionMinimum,self%energyLengthResolutionMaximum,self%energyTableLengthResolutionCount,rangeType=rangeTypeLogarithmic)
+ ! Initialize integrators.
+ integratorPotential=integrator(integrandEnergyPotential,toleranceRelative=1.0d-3)
+ integratorKinetic =integrator(integrandEnergyKinetic ,toleranceRelative=1.0d-3)
+ integratorPressure =integrator(integrandPseudoPressure ,toleranceRelative=1.0d-3)
+ ! Loop over radiusOuter and core radius and populate tables.
+ self_ => self
+ do iLengthResolution=1,self%energyTableLengthResolutionCount
+ iLengthResolution_=iLengthResolution
+ do iRadiusOuter=1,self%energyTableRadiusOuterCount
+ radiusOuter_ =self%energyTableRadiusOuter(iRadiusOuter)
+ energyPotential =+integratorPotential%integrate( 0.0d0, radiusOuter_)
+ energyKinetic =+integratorKinetic %integrate( 0.0d0, radiusOuter_)
+ pseudoPressure =+integratorPressure %integrate(radiusOuter_,multiplierRadius*radiusOuter_)
+ self%energyTable(iRadiusOuter,iLengthResolution)=-0.5d0 &
+ & *( &
+ & +energyPotential &
+ & +self%massEnclosedScaleFree(radiusOuter_,self%energyTableLengthResolution(iLengthResolution))**2 &
+ & /radiusOuter_ &
+ & ) &
+ & +2.0d0 &
+ & *Pi &
+ & *( &
+ & +radiusOuter_ **3 &
+ & *pseudoPressure &
+ & +energyKinetic &
+ & )
+ end do
+ end do
+ ! Build interpolators.
+ if (allocated(self%energyTableLengthResolutionInterpolator)) deallocate(self%energyTableLengthResolutionInterpolator)
+ if (allocated(self%energyTableRadiusOuterInterpolator )) deallocate(self%energyTableRadiusOuterInterpolator )
+ allocate(self%energyTableLengthResolutionInterpolator)
+ allocate(self%energyTableRadiusOuterInterpolator )
+ self%energyTableLengthResolutionInterpolator=interpolator(self%energyTableLengthResolution)
+ self%energyTableRadiusOuterInterpolator =interpolator(self%energyTableRadiusOuter )
+ ! Specify that tabulation has been made.
+ self%energyTableInitialized=.true.
+ call self%storeEnergyTable()
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWEnergyTabulate
+
+ double precision function integrandEnergyPotential(radius)
+ !!{
+ Integrand for potential energy of the halo.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ integrandEnergyPotential=( &
+ & +self_%massEnclosedScaleFree(radius,self_%energyTableLengthResolution(iLengthResolution_)) &
+ & / radius &
+ & )**2
+ else
+ integrandEnergyPotential=+0.0d0
+ end if
+ return
+ end function integrandEnergyPotential
+
+ double precision function integrandEnergyKinetic(radius)
+ !!{
+ Integrand for kinetic energy of the halo.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ integrandEnergyKinetic=+self_%massEnclosedScaleFree(radius,self_%energyTableLengthResolution(iLengthResolution_)) &
+ & *self_%densityScaleFree (radius,self_%energyTableLengthResolution(iLengthResolution_)) &
+ & * radius
+ else
+ integrandEnergyKinetic=+0.0d0
+ end if
+ return
+ end function integrandEnergyKinetic
+
+ double precision function integrandPseudoPressure(radius)
+ !!{
+ Integrand for pseudo-pressure ($\rho(r) \sigma^2(r)$) of the halo.
+ !!}
+ implicit none
+ double precision, intent(in ) :: radius
+
+ if (radius > 0.0d0) then
+ integrandPseudoPressure=+self_%massEnclosedScaleFree(radius,self_%energyTableLengthResolution(iLengthResolution_)) &
+ & *self_%densityScaleFree (radius,self_%energyTableLengthResolution(iLengthResolution_)) &
+ & / radius **2
+ else
+ integrandPseudoPressure=+0.0d0
+ end if
+ return
+ end function integrandPseudoPressure
+
+ double precision function sphericalFiniteResolutionNFWDensityScaleFree(self,radius,radiusCore) result(densityScaleFree)
+ !!{
+ Returns the scale-free density in the dark matter profile at the given {\normalfont \ttfamily radius}.
+ !!}
+ implicit none
+ class (massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ double precision , intent(in ) :: radius, radiusCore
+ !$GLC attributes unused :: self
+
+ densityScaleFree=1.0d0/(1.0d0+radius)**2/sqrt(radius**2+radiusCore**2)
+ return
+ end function sphericalFiniteResolutionNFWDensityScaleFree
+
+ subroutine sphericalFiniteResolutionNFWStoreEnergyTable(self)
+ !!{
+ Store the tabulated energy data to file.
+ !!}
+ use :: File_Utilities , only : File_Lock , File_Unlock , lockDescriptor, Directory_Make, &
+ & File_Path
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//) , char
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Energy_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ call Directory_Make(char(File_Path(char(fileName))))
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.false.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName),overWrite=.true.,objectsOverwritable=.true.,readOnly=.false.)
+ call file%writeDataset(self%energyTableLengthResolution,'lengthResolution')
+ call file%writeDataset(self%energyTableRadiusOuter ,'radiusOuter' )
+ call file%writeDataset(self%energyTable ,'energy' )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ return
+ end subroutine sphericalFiniteResolutionNFWStoreEnergyTable
+
+ subroutine sphericalFiniteResolutionNFWRestoreEnergyTable(self)
+ !!{
+ Restore the tabulated radius-enclosing-mass data from file, returning true if successful.
+ !!}
+ use :: File_Utilities , only : File_Exists , File_Lock , File_Unlock, lockDescriptor
+ use :: HDF5_Access , only : hdf5Access
+ use :: IO_HDF5 , only : hdf5Object
+ use :: Input_Paths , only : inputPath , pathTypeDataDynamic
+ use :: ISO_Varying_String, only : varying_string, operator(//)
+ implicit none
+ class(massDistributionSphericalFiniteResolutionNFW), intent(inout) :: self
+ type (lockDescriptor ) :: fileLock
+ type (hdf5Object ) :: file
+ type (varying_string ) :: fileName
+
+ fileName=inputPath(pathTypeDataDynamic) // &
+ & 'darkMatter/' // &
+ & self%objectType ( )// &
+ & 'Energy_' // &
+ & self%hashedDescriptor(includeSourceDigest=.true.)// &
+ & '.hdf5'
+ if (File_Exists(fileName)) then
+ if (allocated(self%energyTableRadiusOuter)) then
+ deallocate(self%energyTableLengthResolution )
+ deallocate(self%energyTableRadiusOuter)
+ deallocate(self%energyTable )
+ end if
+ ! Always obtain the file lock before the hdf5Access lock to avoid deadlocks between OpenMP threads.
+ call File_Lock(char(fileName),fileLock,lockIsShared=.true.)
+ !$ call hdf5Access%set()
+ call file%openFile(char(fileName))
+ call file%readDataset('lengthResolution',self%energyTableLengthResolution)
+ call file%readDataset('radiusOuter' ,self%energyTableRadiusOuter )
+ call file%readDataset('energy' ,self%energyTable )
+ call file%close()
+ !$ call hdf5Access%unset()
+ call File_Unlock(fileLock)
+ self%energyTableRadiusOuterCount =size(self%energyTableRadiusOuter )
+ self%energyTableLengthResolutionCount=size(self%energyTableLengthResolution)
+ self%energyRadiusOuterMinimum =self%energyTableRadiusOuter ( 1)
+ self%energyRadiusOuterMaximum =self%energyTableRadiusOuter (self%energyTableRadiusOuterCount )
+ self%energyLengthResolutionMinimum =self%energyTableLengthResolution( 1)
+ self%energyLengthResolutionMaximum =self%energyTableLengthResolution(self%energyTableLengthResolutionCount)
+ if (allocated(self%energyTableLengthResolutionInterpolator)) deallocate(self%energyTableLengthResolutionInterpolator)
+ if (allocated(self%energyTableRadiusOuterInterpolator )) deallocate(self%energyTableRadiusOuterInterpolator )
+ allocate(self%energyTableLengthResolutionInterpolator)
+ allocate(self%energyTableRadiusOuterInterpolator )
+ self%energyTableLengthResolutionInterpolator=interpolator(self%energyTableLengthResolution)
+ self%energyTableRadiusOuterInterpolator =interpolator(self%energyTableRadiusOuter )
+ self%energyTableInitialized =.true.
+ end if
+ return
+ end subroutine sphericalFiniteResolutionNFWRestoreEnergyTable
diff --git a/source/mass_distributions.spherical.heated.F90 b/source/mass_distributions.spherical.heated.F90
new file mode 100644
index 0000000000..0d72765af1
--- /dev/null
+++ b/source/mass_distributions.spherical.heated.F90
@@ -0,0 +1,438 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a heated spherical mass distribution.
+ !!}
+
+ use :: Root_Finder, only : rootFinder
+
+ !![
+
+
+ A mass distribution class in which the distribution starts out with a density profile defined by another {\normalfont
+ \ttfamily massDistribution}. This profile is then modified by heating, under the assumption that the
+ energy of a shell of mass before and after heating are related by
+ \begin{equation}
+ -{ \mathrm{G} M^\prime(r^\prime) \over r^\prime } = -{ \mathrm{G} M(r) \over r } + 2 \epsilon(r),
+ \end{equation}
+ where $M(r)$ is the mass enclosed within a radius $r$, and $\epsilon(r)$ represents the specific heating in the shell
+ initially at radius $r$. Primes indicate values after heating, while unprimed variables indicate quantities prior to
+ heating. With the assumption of no shell crossing, $M^\prime(r^\prime)=M(r)$ and this equation can be solved for $r$ given
+ $r^\prime$ and $\epsilon(r)$.
+
+ Not all methods have analytic solutions for this profile. If {\normalfont \ttfamily [nonAnalyticSolver]}$=${\normalfont
+ \ttfamily fallThrough} then attempts to call these methods in heated profiles will simply return the result from the
+ unheated profile, otherwise a numerical calculation is performed.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalHeated
+ !!{
+ Implementation of a heated spherical mass distribution.
+ !!}
+ !![
+
+
+
+
+
+ !!]
+ private
+ class (massDistributionHeatingClass), pointer :: massDistributionHeating_ => null()
+ double precision :: radiusFinalPrevious , radiusInitialPrevious
+ type (rootFinder ) :: finder
+ contains
+ !![
+
+
+
+
+ !!]
+ final :: sphericalHeatedDestructor
+ procedure :: radiusInitial => sphericalHeatedRadiusInitial
+ procedure :: noShellCrossingIsValid => sphericalHeatedNoShellCrossingIsValid
+ procedure :: density => sphericalHeatedDensity
+ procedure :: massEnclosedBySphere => sphericalHeatedMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => sphericalHeatedRadiusEnclosingMass
+ procedure :: useUndecorated => sphericalHeatedUseUndecorated
+ end type massDistributionSphericalHeated
+
+ interface massDistributionSphericalHeated
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalHeated} mass distribution class.
+ !!}
+ module procedure sphericalHeatedConstructorParameters
+ module procedure sphericalHeatedConstructorInternal
+ end interface massDistributionSphericalHeated
+
+ ! Global variables used in root solving.
+ double precision :: radiusFinal_
+ class (massDistributionSphericalHeated), pointer :: self_
+ !$omp threadprivate(radiusFinal_,self_)
+
+contains
+
+ function sphericalHeatedConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalHeated} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalHeated) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(massDistributionClass ), pointer :: massDistribution_
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeating_
+ type (varying_string ) :: nonAnalyticSolver , componentType, &
+ & massType
+ logical :: tolerateVelocityMaximumFailure
+
+ !![
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ tolerateVelocityMaximumFailure
+ .false.
+ If true, tolerate failures to find the radius of the peak in the rotation curve.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalHeated(enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),tolerateVelocityMaximumFailure,massDistribution_,massDistributionHeating_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalHeatedConstructorParameters
+
+ function sphericalHeatedConstructorInternal(nonAnalyticSolver,tolerateVelocityMaximumFailure,massDistribution_,massDistributionHeating_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalHeated'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalHeated ) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ class (massDistributionHeatingClass ), intent(in ), target :: massDistributionHeating_
+ type (enumerationNonAnalyticSolversType), intent(in ) :: nonAnalyticSolver
+ logical , intent(in ) :: tolerateVelocityMaximumFailure
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
+ !![
+
+ !!]
+
+ self% componentType=self%massDistribution_%componentType
+ self% massType=self%massDistribution_% massType
+ self%radiusFinalPrevious=-huge(0.0d0)
+ self%finder =rootFinder( &
+ & rootFunction =radiusInitialRoot, &
+ & toleranceAbsolute=toleranceAbsolute, &
+ & toleranceRelative=toleranceRelative &
+ & )
+ self%dimensionless =.false.
+ return
+ end function sphericalHeatedConstructorInternal
+
+ subroutine sphericalHeatedDestructor(self)
+ !!{
+ Destructor for the ``sphericalHeated'' mass distribution class.
+ !!}
+ implicit none
+ type(massDistributionSphericalHeated), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalHeatedDestructor
+
+ logical function sphericalHeatedUseUndecorated(self) result(useUndecorated)
+ !!{
+ Determines whether to use the undecorated solution.
+ !!}
+ implicit none
+ class(massDistributionSphericalHeated), intent(inout) :: self
+
+ useUndecorated=self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. self%massDistributionHeating_%specificEnergyIsEverywhereZero()
+ return
+ end function sphericalHeatedUseUndecorated
+
+ double precision function sphericalHeatedDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalHeated), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ type (coordinateSpherical ) :: coordinatesInitial
+ double precision :: radius , radiusInitial, &
+ & densityInitial , massEnclosed , &
+ & jacobian
+
+ if (self%massDistributionHeating_%specificEnergyIsEverywhereZero()) then
+ ! No heating, the density is unchanged.
+ density=+self%massDistribution_%density(coordinates)
+ return
+ end if
+ radius =coordinates %rSpherical ( )
+ radiusInitial =self %radiusInitial(radius )
+ coordinatesInitial=[radiusInitial,0.0d0,0.0d0]
+ densityInitial =self%massDistribution_%density (coordinatesInitial)
+ if (radius == 0.0d0 .and. radiusInitial == 0.0d0) then
+ ! At zero radius, the density is unchanged.
+ density =+densityInitial
+ else if (.not.self%noShellCrossingIsValid(radiusInitial,radius)) then
+ ! Shell crossing assumption is broken - simply return the density unchanged.
+ density =+self%massDistribution_%density (coordinates )
+ else
+ massEnclosed=+self%massDistribution_%massEnclosedBySphere(radiusInitial)
+ if (massEnclosed > 0.0d0) then
+ jacobian=+1.0d0 &
+ & /( &
+ & +( &
+ & +radius &
+ & /radiusInitial &
+ & ) **2 &
+ & +2.0d0 &
+ & *radius **2 &
+ & /gravitationalConstantGalacticus &
+ & /massEnclosed &
+ & *( &
+ & +self%massDistributionHeating_%specificEnergyGradient(radiusInitial,self%massDistribution_) &
+ & -4.0d0 &
+ & *Pi &
+ & *radiusInitial **2 &
+ & *densityInitial &
+ & *self%massDistributionHeating_%specificEnergy (radiusInitial,self%massDistribution_) &
+ & /massEnclosed &
+ & ) &
+ & )
+ density =+densityInitial &
+ & *( &
+ & +radiusInitial &
+ & /radius &
+ & ) **2 &
+ & *jacobian
+ else
+ density =+densityInitial
+ end if
+ end if
+ return
+ end function sphericalHeatedDensity
+
+ double precision function sphericalHeatedMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a heated mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalHeated), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ mass=self%massDistribution_%massEnclosedBySphere(self%radiusInitial(radius))
+ return
+ end function sphericalHeatedMassEnclosedBySphere
+
+ double precision function sphericalHeatedRadiusInitial(self,radiusFinal) result(radiusInitial)
+ !!{
+ Find the initial radius corresponding to the given {\normalfont \ttfamily radiusFinal} in
+ the heated mass distribution.
+ !!}
+ use :: Root_Finder, only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
+ implicit none
+ class (massDistributionSphericalHeated), intent(inout), target :: self
+ double precision , intent(in ) :: radiusFinal
+ double precision , parameter :: epsilonExpand=1.0d-2
+ double precision :: factorExpand
+
+ ! If profile is unheated, the initial radius equals the final radius.
+ if (self%massDistributionHeating_%specificEnergyIsEverywhereZero()) then
+ radiusInitial=radiusFinal
+ return
+ end if
+ ! Zero radius always remains at zero.
+ if (radiusFinal <= 0.0d0) then
+ radiusInitial=0.0d0
+ return
+ end if
+ ! Find the initial radius in the unheated profile.
+ if (radiusFinal /= self%radiusFinalPrevious) then
+ self_ => self
+ radiusFinal_ = radiusFinal
+ if (self%radiusFinalPrevious <= -huge(0.0d0) .or. radiusFinal < self%radiusInitialPrevious .or. radiusFinal > 10.0d0*self%radiusInitialPrevious) then
+ ! No previous solution is available, or the requested final radius is smaller than the previous initial radius, or the
+ ! final radius is much larger than the previous initial radius. In this case, our guess for the initial radius is the
+ ! final radius, and we expand the range downward to find a solution.
+ call self%finder%rangeExpand( &
+ & rangeExpandUpward =1.01d0 , &
+ & rangeExpandDownward =0.50d0 , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ self%radiusInitialPrevious=self%finder%find(rootGuess=radiusFinal)
+ else
+ ! Previous solution exists, and the requested final radius is larger (but not too much larger) than the previous initial
+ ! radius. Use the previous initial radius as a guess for the solution, with range expansion in steps determined by the
+ ! relative values of the current and previous final radii. If the current final radius is close to the previous final
+ ! radius this should give a guess for the initial radius close to the actual solution.
+ if (radiusFinal > self%radiusFinalPrevious) then
+ factorExpand= radiusFinal /self%radiusFinalPrevious
+ else
+ factorExpand=self%radiusFinalPrevious/ radiusFinal
+ end if
+ factorExpand=max(factorExpand,1.0d0+epsilonExpand)
+ call self%finder%rangeExpand( &
+ & rangeExpandUpward =1.0d0*factorExpand , &
+ & rangeExpandDownward =1.0d0/factorExpand , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ self%radiusInitialPrevious=self%finder%find(rootGuess=self%radiusInitialPrevious)
+ end if
+ self%radiusFinalPrevious=radiusFinal
+ end if
+ radiusInitial=self%radiusInitialPrevious
+ return
+ end function sphericalHeatedRadiusInitial
+
+ double precision function radiusInitialRoot(radiusInitial)
+ !!{
+ Root function used in finding initial radii in heated mass distributions.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ double precision, intent(in ) :: radiusInitial
+ double precision, parameter :: fractionRadiusSmall=1.0d-3
+ double precision :: massEnclosed
+
+ if (radiusInitial < fractionRadiusSmall*radiusFinal_) then
+ ! The initial radius is a small fraction of the final radius. Check if the assumption of no shell crossing is locally
+ ! broken. If the gradient of the heating term is less than that of the gravitational potential term then it is likely that
+ ! no root exists. In this case shell crossing is likely to be occurring. Simply return a value of zero, which places the
+ ! root at the current radius.
+ if (.not.self_%noShellCrossingIsValid(radiusInitial,radiusFinal_)) then
+ radiusInitialRoot=0.0d0
+ return
+ end if
+ end if
+ massEnclosed =+self_%massDistribution_ %massEnclosedBySphere(radiusInitial )
+ radiusInitialRoot=+self_ %massDistributionHeating_%specificEnergy (radiusInitial,self_%massDistribution_) &
+ & +0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & *( &
+ & +1.0d0/radiusFinal_ &
+ & -1.0d0/radiusInitial &
+ & )
+ return
+ end function radiusInitialRoot
+
+ logical function sphericalHeatedNoShellCrossingIsValid(self,radiusInitial,radiusFinal) result(isValid)
+ !!{
+ Determines if the no shell crossing assumption is valid.
+ !!}
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ implicit none
+ class (massDistributionSphericalHeated), intent(inout) :: self
+ double precision , intent(in ) :: radiusInitial , radiusFinal
+ double precision :: massEnclosed
+ type (coordinateSpherical ) :: coordinatesInitial
+
+ coordinatesInitial= [radiusInitial,0.0d0,0.0d0]
+ massEnclosed = + self%massDistribution_ %massEnclosedBySphere ( radiusInitial )
+ isValid = + self %massDistributionHeating_%specificEnergyGradient( radiusInitial,self%massDistribution_) &
+ & > &
+ & +0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +4.0d0 &
+ & *Pi &
+ & *radiusInitial**2 &
+ & *self%massDistribution_%density (coordinatesInitial ) &
+ & *( &
+ & -1.0d0/radiusFinal &
+ & +1.0d0/radiusInitial &
+ & ) &
+ & -massEnclosed &
+ & /radiusInitial**2 &
+ & )
+ return
+ end function sphericalHeatedNoShellCrossingIsValid
+
+ double precision function sphericalHeatedRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for heated spherical mass distributions.
+ !!}
+ use :: Galactic_Structure_Options , only : radiusLarge
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalHeated), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision :: radiusInitial
+ double precision :: energySpecific
+
+ radiusInitial =self%massDistribution_ %radiusEnclosingMass(mass , massFractional )
+ energySpecific=self%massDistributionHeating_%specificEnergy (radiusInitial,self%massDistribution_)
+ if (radiusInitial <= 0.0d0) then
+ radius=+radiusLarge
+ else
+ radius=+1.0d0 &
+ & /( &
+ & +1.0d0/radiusInitial &
+ & -2.0d0/gravitationalConstantGalacticus/mass*energySpecific &
+ & )
+ ! If the radius found is negative, which means the initial shell has expanded to infinity, return the largest radius.
+ if (radius < 0.0d0) radius=radiusLarge
+ end if
+ return
+ end function sphericalHeatedRadiusEnclosingMass
diff --git a/source/mass_distributions.spherical.heated.monotonic.F90 b/source/mass_distributions.spherical.heated.monotonic.F90
new file mode 100644
index 0000000000..495fb62fc1
--- /dev/null
+++ b/source/mass_distributions.spherical.heated.monotonic.F90
@@ -0,0 +1,368 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ An implementation of heated dark matter halo profiles based on the energy ordering of shells.
+ !!}
+
+ use :: Numerical_Interpolation, only : interpolator
+
+ !![
+
+
+ A mass distribution class in which dark matter halos start out with a density profile defined by another {\normalfont
+ \ttfamily massDistributionClass}. This profile is then modified by heating, under the assumption that the
+ energy of a shell of mass before and after heating are related by
+ \begin{equation}
+ -{ \mathrm{G} M^\prime(r^\prime) \over r^\prime } = -{ \mathrm{G} M(r) \over r } + 2 \epsilon(r),
+ \end{equation}
+ where $M(r)$ is the mass enclosed within a radius $r$, and $\epsilon(r)$ represents the specific heating in the shell
+ initially at radius $r$. Primes indicate values after heating, while unprimed variables indicate quantities prior to
+ heating.
+
+ The above equation can be re-written as
+ \begin{equation}
+ -r^{\prime -1} = -r^{-1} + \xi(r),
+ \end{equation}
+ where $\xi(r) = 2 \epsilon(r)/[\mathrm{G} M(r)/r]$ measures the perturbation to the shell. To avoid shell crossing a
+ monotonicity relation $r_1 < r_2 \implies \xi(r_1) \le \xi(r_2)$ is enforced by starting at large radius and stepping inward,
+ enforcing the condition in the next innermost shell as necessary.
+
+ Not all methods have analytic solutions for this profile. If {\normalfont \ttfamily [nonAnalyticSolver]}$=${\normalfont
+ \ttfamily fallThrough} then attempts to call these methods in heated profiles will simply return the result from the
+ unheated profile, otherwise a numerical calculation is performed.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalHeatedMonotonic
+ !!{
+ Implementation of a heated spherical mass distribution.
+ !!}
+ private
+ class (massDistributionHeatingClass), pointer :: massDistributionHeating_ => null()
+ double precision :: radiusInitialMinimum , radiusInitialMaximum, &
+ & radiusFinalMinimum , radiusFinalMaximum , &
+ & radiusVirial
+ type (interpolator ), allocatable :: massProfile
+ logical :: isBound
+
+ contains
+ !![
+
+
+
+ !!]
+ final :: sphericalHeatedMonotonicDestructor
+ procedure :: computeSolution => sphericalHeatedMonotonicComputeSolution
+ procedure :: density => sphericalHeatedMonotonicDensity
+ procedure :: massEnclosedBySphere => sphericalHeatedMonotonicMassEnclosedBySphere
+ procedure :: useUndecorated => sphericalHeatedMonotonicUseUndecorated
+ end type massDistributionSphericalHeatedMonotonic
+
+ interface massDistributionSphericalHeatedMonotonic
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalHeatedMonotonic} mass distribution class.
+ !!}
+ module procedure sphericalHeatedMonotonicConstructorParameters
+ module procedure sphericalHeatedMonotonicConstructorInternal
+ end interface massDistributionSphericalHeatedMonotonic
+
+contains
+
+ function sphericalHeatedMonotonicConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalHeatedMonotonic} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalHeatedMonotonic) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (massDistributionHeatingClass ), pointer :: massDistributionHeating_
+ type (varying_string ) :: nonAnalyticSolver , componentType, &
+ & massType
+ double precision :: radiusVirial
+
+ !![
+
+ radiusVirial
+
+ The virial radius of the halo.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available. If set to ``{\normalfont \ttfamily fallThrough}'' then the solution ignoring heating is used, while if set to ``{\normalfont \ttfamily numerical}'' then numerical solvers are used to find solutions.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalHeatedMonotonic(radiusVirial,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,massDistributionHeating_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+
+ !!]
+ return
+ end function sphericalHeatedMonotonicConstructorParameters
+
+ function sphericalHeatedMonotonicConstructorInternal(radiusVirial,nonAnalyticSolver,massDistribution_,massDistributionHeating_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalHeatedMonotonic'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalHeatedMonotonic) :: self
+ double precision , intent(in ) :: radiusVirial
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ class (massDistributionHeatingClass ), intent(in ), target :: massDistributionHeating_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Construct the object.
+ self%radiusInitialMinimum=+huge(0.0d0)
+ self%radiusInitialMaximum=-huge(0.0d0)
+ self%radiusFinalMinimum =+huge(0.0d0)
+ self%radiusFinalMaximum =-huge(0.0d0)
+ self%isBound =.true.
+ self%dimensionless =.false.
+ return
+ end function sphericalHeatedMonotonicConstructorInternal
+
+ subroutine sphericalHeatedMonotonicDestructor(self)
+ !!{
+ Destructor for the ``sphericalHeatedMonotonic'' mass distribution class.
+ !!}
+ implicit none
+ type(massDistributionSphericalHeatedMonotonic), intent(inout) :: self
+
+ !![
+
+
+ !!]
+ return
+ end subroutine sphericalHeatedMonotonicDestructor
+
+ logical function sphericalHeatedMonotonicUseUndecorated(self) result(useUndecorated)
+ !!{
+ Determines whether to use the undecorated solution.
+ !!}
+ implicit none
+ class(massDistributionSphericalHeatedMonotonic), intent(inout) :: self
+
+ useUndecorated=self%nonAnalyticSolver == nonAnalyticSolversFallThrough .or. self%massDistributionHeating_%specificEnergyIsEverywhereZero()
+ return
+ end function sphericalHeatedMonotonicUseUndecorated
+
+ double precision function sphericalHeatedMonotonicMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Returns the enclosed mass (in $M_\odot$) in the dark matter profile of {\normalfont \ttfamily node} at the given {\normalfont \ttfamily radius} (given in
+ units of Mpc).
+ !!}
+ implicit none
+ class (massDistributionSphericalHeatedMonotonic), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ if (self%massDistributionHeating_%specificEnergyIsEverywhereZero()) then
+ ! No heating - use the unheated solution.
+ mass=self%massDistribution_%massEnclosedBySphere(radius)
+ else if (radius <= 0.0d0) then
+ ! Non-positive radius, mass must be zero.
+ mass=0.0d0
+ else
+ ! Compute the solution (as needed).
+ call self%computeSolution(radius)
+ ! For bound halos, interpolate to find the enclosed mass. For unbound halos the enclosed mass is zero.
+ if (self%isBound) then
+ if (radius < self%radiusFinalMinimum) then
+ ! Assume constant density below the minimum radius.
+ mass =+exp(self%massProfile%interpolate(log(self%radiusFinalMinimum))) &
+ & *( &
+ & + radius &
+ & /self%radiusFinalMinimum &
+ & )**3
+ else
+ mass=+exp(self%massProfile%interpolate(log(radius)))
+ end if
+ else
+ mass=0.0d0
+ end if
+ end if
+ return
+ end function sphericalHeatedMonotonicMassEnclosedBySphere
+
+ double precision function sphericalHeatedMonotonicDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionSphericalHeatedMonotonic), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: radius , radius_
+
+ if (self%massDistributionHeating_%specificEnergyIsEverywhereZero()) then
+ ! No heating, the density is unchanged.
+ density=+self%massDistribution_%density(coordinates)
+ return
+ end if
+ radius=coordinates%rSpherical()
+ call self%computeSolution(radius)
+ ! For bound halos, interpolate to find the density. For unbound halos the density is zero.
+ if (self%isBound) then
+ radius_=max(radius,self%radiusFinalMinimum)
+ density=+ self%massProfile%derivative (log(radius_)) &
+ & *exp(self%massProfile%interpolate(log(radius_))) &
+ & /4.0d0 &
+ & /Pi &
+ & /radius**3
+ else
+ density=+0.0d0
+ end if
+ return
+ end function sphericalHeatedMonotonicDensity
+
+ subroutine sphericalHeatedMonotonicComputeSolution(self,radius)
+ !!{
+ Compute the solution for the heated density profile.
+ !!}
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Table_Labels , only : extrapolationTypeFix , extrapolationTypeZero
+ implicit none
+ class (massDistributionSphericalHeatedMonotonic), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision , parameter :: radiusFractionMinimum=1.0d-6, radiusFractionMaximum=10.0d0
+ integer , parameter :: countPerDecadeRadius =100
+ double precision , allocatable, dimension(:) :: massEnclosed , massShell , &
+ & radiusInitial , radiusFinal , &
+ & energyFinal , perturbation
+ logical , allocatable, dimension(:) :: isBound
+ integer :: i , countRadii
+
+ ! Nothing to do if profile is already tabulated.
+ if (allocated(self%massProfile)) return
+ ! Choose extent of radii at which to tabulate the initial profile.
+ self%radiusInitialMinimum=radiusFractionMinimum*self%radiusVirial
+ self%radiusInitialMaximum=radiusFractionMaximum*self%radiusVirial
+ ! Build grid of radii.
+ countRadii=int(log10(self%radiusInitialMaximum/self%radiusInitialMinimum)*dble(countPerDecadeRadius)+1.0d0)
+ if (allocated(radiusInitial)) then
+ deallocate(radiusInitial)
+ deallocate(radiusFinal )
+ deallocate(massEnclosed )
+ deallocate(massShell )
+ deallocate(energyFinal )
+ deallocate(perturbation )
+ end if
+ allocate(radiusInitial(countRadii))
+ allocate(radiusFinal (countRadii))
+ allocate(massEnclosed (countRadii))
+ allocate(massShell (countRadii))
+ allocate(energyFinal (countRadii))
+ allocate(perturbation (countRadii))
+ radiusInitial=Make_Range(self%radiusInitialMinimum,self%radiusInitialMaximum,countRadii,rangeTypeLogarithmic)
+ ! Evaluate masses and energies of shells.
+ do i=countRadii,1,-1
+ massEnclosed(i)=+self%massDistribution_ %massEnclosedBySphere(radiusInitial(i) )
+ perturbation(i)=+2.0d0 &
+ & *self%massDistributionHeating_ %specificEnergy (radiusInitial(i),self%massDistribution_) &
+ & /gravitationalConstantGalacticus &
+ & / massEnclosed (i) &
+ & * radiusInitial(i)
+ ! Limit the perturbation to avoid shell-crossing.
+ if (i < countRadii) &
+ & perturbation(i)=min( &
+ & +perturbation (i ), &
+ & +1.0d0 &
+ & -radiusInitial (i ) &
+ & /radiusInitial (i+1) &
+ & *( &
+ & +massEnclosed(i ) &
+ & /massEnclosed(i+1) &
+ & )**(-1.0d0/3.0d0) &
+ & *( &
+ & +1.0d0 &
+ & -perturbation(i+1) &
+ & ) &
+ & )
+ end do
+ ! Compute the final energy of the heated profile.
+ energyFinal=+gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & /radiusInitial &
+ & *( &
+ & -1.0d0 &
+ & +perturbation &
+ & )
+ ! Find shell masses.
+ massShell(1 )=+massEnclosed(1 )
+ massShell(2:countRadii)=+massEnclosed(2:countRadii ) &
+ & -massEnclosed(1:countRadii-1)
+ ! Evaluation boundedness.
+ isBound= energyFinal < 0.0d0 &
+ & .and. &
+ & massShell > 0.0d0
+ ! Find final radii.
+ where (isBound)
+ radiusFinal=-gravitationalConstantGalacticus &
+ & *massEnclosed &
+ & /energyFinal
+ elsewhere
+ radiusFinal=+huge(0.0d0)
+ end where
+ ! Build the final profile interpolator.
+ self%isBound=count(isBound) > 2
+ if (self%isBound) then
+ self%radiusFinalMinimum =minval(radiusFinal ,mask=isBound)
+ self%radiusFinalMaximum =maxval(radiusFinal ,mask=isBound)
+ ! Construct the interpolator.
+ if (allocated(self%massProfile)) deallocate(self%massProfile)
+ allocate(self%massProfile)
+ self%massProfile=interpolator( &
+ & x =log(pack(radiusFinal ,mask=isBound)), &
+ & y =log(pack(massEnclosed,mask=isBound)), &
+ & extrapolationType=extrapolationTypeFix &
+ & )
+ end if
+ return
+ end subroutine sphericalHeatedMonotonicComputeSolution
diff --git a/source/mass_distributions.spherical.heating.impulsive_outflow.F90 b/source/mass_distributions.spherical.heating.impulsive_outflow.F90
new file mode 100644
index 0000000000..709e4c877c
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.impulsive_outflow.F90
@@ -0,0 +1,247 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a mass distribution heating class that computes heating due to two-body relaxation.
+ !!}
+
+ !![
+
+
+ A mass distribution heating class that computes heating due to impulsive outflows---i.e. outflows occurring on
+ timescales that are small relative to the dynamical time of the halo. The model assumed is that the energy injection is given by
+ \begin{equation}
+ \dot{\epsilon}(r) = \alpha \frac{\mathrm{G} \dot{M}_\mathrm{outflow}(r)}{r} f\left( \frac{t_\phi}{t_\mathrm{dyn}} \right),
+ \end{equation}
+ where $\alpha$ is a normalization factor, $t_\phi = M_\mathrm{gas}/\dot{M}_\mathrm{outflow}$ is the timescale for the
+ outflow, and $t_\mathrm{dyn} = r_{1/2}/v_{1/2}$ is the dynamical time at the half-mass radius.
+
+ The quantity
+ \begin{equation}
+ \dot{\epsilon}^\prime = \dot{M}_\mathrm{outflow} f\left( \frac{t_\phi}{t_\mathrm{dyn}} \right),
+ \end{equation}
+ if provided as an argument to the class constructor.
+
+
+ !!]
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingImpulsiveOutflow
+ !!{
+ Implementation of a mass distribution heating class that computes heating due to impulsive outflows.
+ !!}
+ private
+ double precision :: energyImpulsiveOutflowDisk, energyImpulsiveOutflowSpheroid, &
+ & impulsiveEnergyFactor
+ contains
+ procedure :: specificEnergy => impulsiveOutflowSpecificEnergy
+ procedure :: specificEnergyGradient => impulsiveOutflowSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => impulsiveOutflowSpecificEnergyIsEverywhereZero
+ end type massDistributionHeatingImpulsiveOutflow
+
+ interface massDistributionHeatingImpulsiveOutflow
+ !!{
+ Constructors for the {\normalfont \ttfamily impulsiveOutflow} mass distribution class.
+ !!}
+ module procedure impulsiveOutflowConstructorParameters
+ module procedure impulsiveOutflowConstructorInternal
+ end interface massDistributionHeatingImpulsiveOutflow
+
+contains
+
+ function impulsiveOutflowConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily impulsiveOutflow} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (massDistributionHeatingImpulsiveOutflow) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: energyImpulsiveOutflowDisk, energyImpulsiveOutflowSpheroid, &
+ & impulsiveEnergyFactor
+
+ !![
+
+ energyImpulsiveOutflowDisk
+
+ The impulsive energy of outflows from the disk.
+
+
+ energyImpulsiveOutflowSpheroid
+
+ The impulsive energy of outflows from the spheroid.
+
+
+ impulsiveEnergyFactor
+ 1.0d0
+ The parameter $\alpha$ appearing in the impulsive outflow heating rate.
+
+
+ !!]
+ self=massDistributionHeatingImpulsiveOutflow(energyImpulsiveOutflowDisk,energyImpulsiveOutflowSpheroid,impulsiveEnergyFactor)
+ !![
+
+ !!]
+ return
+ end function impulsiveOutflowConstructorParameters
+
+ function impulsiveOutflowConstructorInternal(energyImpulsiveOutflowDisk,energyImpulsiveOutflowSpheroid,impulsiveEnergyFactor) result(self)
+ !!{
+ Constructor for ``impulsiveOutflow'' dark matter profile heating class.
+ !!}
+ implicit none
+ type (massDistributionHeatingImpulsiveOutflow) :: self
+ double precision , intent(in ) :: energyImpulsiveOutflowDisk, energyImpulsiveOutflowSpheroid, &
+ & impulsiveEnergyFactor
+ !![
+
+ !!]
+
+ return
+ end function impulsiveOutflowConstructorInternal
+
+ double precision function impulsiveOutflowSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingImpulsiveOutflow), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ class (massDistributionClass ), pointer :: massDistributionDisk, massDistributionSpheroid
+ double precision :: massTotalDisk , massTotalSpheroid , &
+ & fractionMassDisk , fractionMassSpheroid
+
+ massDistributionDisk => massDistribution_%subset(componentType=componentTypeDisk )
+ massDistributionSpheroid => massDistribution_%subset(componentType=componentTypeSpheroid)
+ fractionMassDisk = 0.0d0
+ fractionMassSpheroid = 0.0d0
+ if (associated(massDistributionDisk )) then
+ massTotalDisk = massDistributionDisk %massTotal()
+ if (massTotalDisk > 0.0d0) then
+ fractionMassDisk =+massDistributionDisk %massEnclosedBySphere(radius) &
+ & / massTotalDisk
+ end if
+ end if
+ if (associated(massDistributionSpheroid)) then
+ massTotalSpheroid = massDistributionSpheroid%massTotal()
+ if (massTotalSpheroid > 0.0d0) then
+ fractionMassSpheroid=+massDistributionSpheroid%massEnclosedBySphere(radius) &
+ & / massTotalSpheroid
+ end if
+ end if
+ energySpecific=+ self%impulsiveEnergyFactor &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +self%energyImpulsiveOutflowDisk &
+ & *fractionMassDisk &
+ & +self%energyImpulsiveOutflowSpheroid &
+ & *fractionMassSpheroid &
+ & ) &
+ & /radius
+ !![
+
+
+ !!]
+ return
+ end function impulsiveOutflowSpecificEnergy
+
+ double precision function impulsiveOutflowSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionHeatingImpulsiveOutflow), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ class (massDistributionClass ), pointer :: massDistributionDisk, massDistributionSpheroid
+ double precision :: massTotalDisk , massTotalSpheroid , &
+ & fractionMassDisk , fractionMassSpheroid , &
+ & fractionDensityDisk , fractionDensitySpheroid
+ type (coordinatespherical ) :: coordinates
+
+ massDistributionDisk => massDistribution_%subset(componentType=componentTypeDisk )
+ massDistributionSpheroid => massDistribution_%subset(componentType=componentTypeSpheroid)
+ coordinates = [radius,0.0d0,0.0d0]
+ fractionMassDisk = 0.0d0
+ fractionMassSpheroid = 0.0d0
+ fractionDensityDisk = 0.0d0
+ fractionDensitySpheroid = 0.0d0
+ if (associated(massDistributionDisk )) then
+ massTotalDisk = massDistributionDisk %massTotal()
+ if (massTotalDisk > 0.0d0) then
+ fractionMassDisk =+massDistributionDisk %massEnclosedBySphere(radius ) &
+ & / massTotalDisk
+ fractionDensityDisk =+massDistributionDisk %density (coordinates) &
+ & / massTotalDisk
+ end if
+ end if
+ if (associated(massDistributionSpheroid)) then
+ massTotalSpheroid = massDistributionSpheroid%massTotal()
+ if (massTotalSpheroid > 0.0d0) then
+ fractionMassSpheroid =+massDistributionSpheroid%massEnclosedBySphere(radius ) &
+ & / massTotalSpheroid
+ fractionDensitySpheroid=+massDistributionSpheroid%density (coordinates) &
+ & / massTotalSpheroid
+ end if
+ end if
+ energySpecificGradient=+self%impulsiveEnergyFactor &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +( &
+ & +self%energyImpulsiveOutflowDisk &
+ & *fractionDensityDisk &
+ & +self%energyImpulsiveOutflowSpheroid &
+ & *fractionDensitySpheroid &
+ & ) &
+ & *4.0d0 &
+ & *Pi &
+ & *radius &
+ & -( &
+ & +self%energyImpulsiveOutflowDisk &
+ & *fractionMassDisk &
+ & +self%energyImpulsiveOutflowSpheroid &
+ & *fractionMassSpheroid &
+ & ) &
+ & /radius**2 &
+ & )
+ !![
+
+
+ !!]
+ return
+ end function impulsiveOutflowSpecificEnergyGradient
+
+ logical function impulsiveOutflowSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingImpulsiveOutflow), intent(inout) :: self
+
+ energySpecificIsEverywhereZero= self%energyImpulsiveOutflowDisk <= 0.0d0 &
+ & .and. &
+ & self%energyImpulsiveOutflowSpheroid <= 0.0d0
+ return
+ end function impulsiveOutflowSpecificEnergyIsEverywhereZero
diff --git a/source/mass_distributions.spherical.heating.monotonic.F90 b/source/mass_distributions.spherical.heating.monotonic.F90
new file mode 100644
index 0000000000..06d50adcad
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.monotonic.F90
@@ -0,0 +1,320 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !+ Contributions to this file made by: Xiaolong Du.
+
+ !!{
+ Implements a mass distribution heating class which takes another heating source and enforces monotonic heating energy perturbation.
+ !!}
+
+ !![
+
+
+ A mass distribution heating class which takes another heating source and enforces monotonic heating energy perturbation.
+
+
+ !!]
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingMonotonic
+ !!{
+ Implementation of a mass distribution heating class which takes another heating source and enforces monotonic heating energy perturbation.
+ !!}
+ private
+ class (massDistributionHeatingClass), pointer :: massDistributionHeating_ => null()
+ type (rootFinder ) :: finder
+ double precision :: radiusShellCrossing , energyPerturbationShellCrossing
+ contains
+ !![
+
+
+
+
+ !!]
+ procedure :: specificEnergy => monotonicSpecificEnergy
+ procedure :: specificEnergyGradient => monotonicSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => monotonicSpecificEnergyIsEverywhereZero
+ procedure :: noShellCrossingIsValid => monotonicNoShellCrossingIsValid
+ procedure :: computeRadiusShellCrossing => monotonicComputeRadiusShellCrossing
+ end type massDistributionHeatingMonotonic
+
+ interface massDistributionHeatingMonotonic
+ !!{
+ Constructors for the {\normalfont \ttfamily monotonic} mass distribution class.
+ !!}
+ module procedure monotonicConstructorParameters
+ module procedure monotonicConstructorInternal
+ end interface massDistributionHeatingMonotonic
+
+ ! Global variables used in root solving.
+ type (massDistributionHeatingMonotonic), pointer :: self_
+ class(massDistributionClass ), pointer :: massDistribution__
+ !$omp threadprivate(self_,massDistribution__)
+
+contains
+
+ function monotonicConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily monotonic} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (massDistributionHeatingMonotonic) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class(massDistributionHeatingClass ), pointer :: massDistributionHeating_
+
+ !![
+
+ !!]
+ self=massDistributionHeatingMonotonic(massDistributionHeating_)
+ !![
+
+
+ !!]
+ return
+ end function monotonicConstructorParameters
+
+ function monotonicConstructorInternal(massDistributionHeating_) result(self)
+ !!{
+ Constructor for ``monotonic'' mass distribution heating class.
+ !!}
+ implicit none
+ type (massDistributionHeatingMonotonic) :: self
+ class (massDistributionHeatingClass ), target, intent(in ) :: massDistributionHeating_
+ double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
+ !![
+
+ !!]
+
+ self%radiusShellCrossing =-1.0d0
+ self%energyPerturbationShellCrossing=-1.0d0
+ self%finder =rootFinder( &
+ & rootFunction =monotonicRadiusShellCrossingRoot, &
+ & toleranceAbsolute=toleranceAbsolute , &
+ & toleranceRelative=toleranceRelative &
+ & )
+ return
+ end function monotonicConstructorInternal
+
+ subroutine monotonicDestructor(self)
+ !!{
+ Destructor for the ``monotonic'' mass distribution heating class.
+ !!}
+ implicit none
+ type(massDistributionHeatingMonotonic), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine monotonicDestructor
+
+ double precision function monotonicSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Compute the specific energy in a monotonicly-heated mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingMonotonic), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+
+ if (self%noShellCrossingIsValid(radius,massDistribution_)) then
+ energySpecific=self%massDistributionHeating_%specificEnergy( &
+ & radius , &
+ & massDistribution_ &
+ & )
+ else
+ if (self%energyPerturbationShellCrossing < 0.0d0) &
+ call self%computeRadiusShellCrossing( &
+ & radius , &
+ & massDistribution_ &
+ & )
+ energySpecific=+self%energyPerturbationShellCrossing &
+ & *0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *massDistribution_ %massEnclosedBySphere(radius) &
+ & /radius
+ end if
+ return
+ end function monotonicSpecificEnergy
+
+ double precision function monotonicSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionHeatingMonotonic), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
+
+ if (self%noShellCrossingIsValid(radius,massDistribution_)) then
+ energySpecificGradient=self%massDistributionHeating_%specificEnergyGradient( &
+ & radius , &
+ & massDistribution_ &
+ & )
+ else
+ if (self%energyPerturbationShellCrossing < 0.0d0) &
+ & call self%computeRadiusShellCrossing( &
+ & radius , &
+ & massDistribution_ &
+ & )
+ coordinates =[radius,0.0d0,0.0d0]
+ energySpecificGradient=+self%energyPerturbationShellCrossing &
+ & *0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *( &
+ & +4.0d0 &
+ & *Pi &
+ & * radius &
+ & *massDistribution_%density (coordinates) &
+ & -massDistribution_%massEnclosedBySphere(radius ) &
+ & /radius**2 &
+ & )
+ end if
+ return
+ end function monotonicSpecificEnergyGradient
+
+ logical function monotonicSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingMonotonic), intent(inout) :: self
+
+ energySpecificIsEverywhereZero=self%massDistributionHeating_%specificEnergyIsEverywhereZero()
+ return
+ end function monotonicSpecificEnergyIsEverywhereZero
+
+ logical function monotonicNoShellCrossingIsValid(self,radius,massDistribution_)
+ !!{
+ Determines if the no shell crossing assumption is valid.
+ !!}
+ use :: Coordinates , only : coordinateSpherical, assignment(=)
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionHeatingMonotonic), intent(inout) :: self
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision , intent(in ) :: radius
+ double precision :: massEnclosed
+ type (coordinateSpherical ) :: coordinates
+
+ massEnclosed = massDistribution_%massEnclosedBySphere(radius)
+ if (massEnclosed > 0.0d0) then
+ coordinates =[radius,0.0d0,0.0d0]
+ monotonicNoShellCrossingIsValid=+self%massDistributionHeating_%specificEnergyGradient( &
+ & radius , &
+ & massDistribution_ &
+ & ) &
+ & * radius &
+ & +self%massDistributionHeating_%specificEnergy ( &
+ & radius , &
+ & massDistribution_ &
+ & ) &
+ & *( &
+ & +1.0d0 &
+ & -4.0d0 &
+ & *Pi &
+ & * radius**3 &
+ & *massDistribution_ %density ( &
+ & coordinates &
+ & ) &
+ & /massEnclosed &
+ & ) &
+ & >=0.0d0
+ else
+ monotonicNoShellCrossingIsValid=.true.
+ end if
+ return
+ end function monotonicNoShellCrossingIsValid
+
+ subroutine monotonicComputeRadiusShellCrossing(self,radius,massDistribution_)
+ !!{
+ Determines if the no shell crossing assumption is valid.
+ !!}
+ use :: Root_Finder , only : rangeExpandMultiplicative , rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingMonotonic), intent(inout), target :: self
+ class (massDistributionClass ), intent(inout), target :: massDistribution_
+ double precision , intent(in ) :: radius
+
+ if (self%energyPerturbationShellCrossing < 0.0d0) then
+ self_ => self
+ massDistribution__ => massDistribution_
+ call self%finder%rangeExpand( &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =1.0d0 , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ self%radiusShellCrossing =+self%finder%find(rootGuess=radius)
+ self%energyPerturbationShellCrossing =+self%massDistributionHeating_%specificEnergy (self%radiusShellCrossing,massDistribution_) &
+ & /( &
+ & +0.5d0 &
+ & *gravitationalConstantGalacticus &
+ & *massDistribution_ %massEnclosedBySphere(self%radiusShellCrossing ) &
+ & / self%radiusShellCrossing &
+ & )
+ end if
+ return
+ end subroutine monotonicComputeRadiusShellCrossing
+
+ double precision function monotonicRadiusShellCrossingRoot(radius)
+ !!{
+ Root function used in finding the radius where shell crossing happens.
+ !!}
+ use :: Coordinates , only : coordinateSpherical, assignment(=)
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision , intent(in ) :: radius
+ double precision :: massEnclosed
+ type (coordinateSpherical) :: coordinates
+
+ massEnclosed = massDistribution__%massEnclosedBySphere(radius)
+ if (massEnclosed > 0.0d0) then
+ coordinates =[radius,0.0d0,0.0d0]
+ monotonicRadiusShellCrossingRoot=+self_%massDistributionHeating_%specificEnergyGradient( &
+ & radius , &
+ & massDistribution__ &
+ & ) &
+ & * radius &
+ & +self_%massDistributionHeating_%specificEnergy ( &
+ & radius , &
+ & massDistribution__ &
+ & ) &
+ & *( &
+ & +1.0d0 &
+ & -4.0d0 &
+ & *Pi &
+ & * radius**3 &
+ & *massDistribution__ %density ( &
+ & coordinates &
+ & ) &
+ & /massEnclosed &
+ & )
+ else
+ monotonicRadiusShellCrossingRoot=0.0d0
+ end if
+ return
+ end function monotonicRadiusShellCrossingRoot
diff --git a/source/mass_distributions.spherical.heating.null.F90 b/source/mass_distributions.spherical.heating.null.F90
new file mode 100644
index 0000000000..505ce2dbda
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.null.F90
@@ -0,0 +1,103 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a null mass distribution heating class.
+ !!}
+
+ !![
+
+
+ A null mass distribution heating class. The heating energy is always zero.
+
+
+ !!]
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingNull
+ !!{
+ Implementation of a null mass distribution heating class.
+ !!}
+ private
+ contains
+ procedure :: specificEnergy => nullSpecificEnergy
+ procedure :: specificEnergyGradient => nullSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => nullSpecificEnergyIsEverywhereZero
+ end type massDistributionHeatingNull
+
+ interface massDistributionHeatingNull
+ !!{
+ Constructors for the {\normalfont \ttfamily null} mass distribution class.
+ !!}
+ module procedure nullConstructorParameters
+ end interface massDistributionHeatingNull
+
+contains
+
+ function nullConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily null} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type(massDistributionHeatingNull) :: self
+ type(inputParameters ), intent(inout) :: parameters
+
+ self=massDistributionHeatingNull()
+ !![
+
+ !!]
+ return
+ end function nullConstructorParameters
+
+ double precision function nullSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Compute the specific energy in a zero-heating mass distribution.
+ !!}
+ implicit none
+ class (massDistributionHeatingNull), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+
+ energySpecific=+0.0d0
+ return
+ end function nullSpecificEnergy
+
+ double precision function nullSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ implicit none
+ class (massDistributionHeatingNull), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+
+ energySpecificGradient=+0.0d0
+ return
+ end function nullSpecificEnergyGradient
+
+ logical function nullSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingNull), intent(inout) :: self
+
+ energySpecificIsEverywhereZero=.true.
+ return
+ end function nullSpecificEnergyIsEverywhereZero
diff --git a/source/mass_distributions.spherical.heating.summation.F90 b/source/mass_distributions.spherical.heating.summation.F90
new file mode 100644
index 0000000000..9d4a2870cc
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.summation.F90
@@ -0,0 +1,188 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a mass distribution heating class that sums heating over other classes.
+ !!}
+
+ !![
+
+ A mass distribution heating class that sums heating over other classes.
+
+
+ !!]
+
+ type, public :: massDistributionHeatingList
+ class(massDistributionHeatingClass), pointer :: massDistributionHeating_ => null()
+ type (massDistributionHeatingList ), pointer :: next => null()
+ end type massDistributionHeatingList
+
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingSummation
+ !!{
+ Implementation of a mass distribution heating class that sums heating over other classes.
+ !!}
+ private
+ type(massDistributionHeatingList), pointer :: massDistributionHeatings => null()
+ contains
+ procedure :: specificEnergy => summationSpecificEnergy
+ procedure :: specificEnergyGradient => summationSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => summationSpecificEnergyIsEverywhereZero
+ end type massDistributionHeatingSummation
+
+ interface massDistributionHeatingSummation
+ !!{
+ Constructors for the {\normalfont \ttfamily summation} mass distribution class.
+ !!}
+ module procedure summationConstructorParameters
+ module procedure summationConstructorInternal
+ end interface massDistributionHeatingSummation
+
+contains
+
+ function summationConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily summation} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (massDistributionHeatingSummation) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ type (massDistributionHeatingList ), pointer :: massDistributionHeating_
+ integer :: i
+
+ massDistributionHeating_ => null()
+ do i=1,parameters%copiesCount('massDistributionHeating',zeroIfNotPresent=.true.)
+ if (associated(massDistributionHeating_)) then
+ allocate(massDistributionHeating_%next)
+ massDistributionHeating_ => massDistributionHeating_%next
+ else
+ allocate(self%massDistributionHeatings)
+ massDistributionHeating_ => self %massDistributionHeatings
+ end if
+ !![
+
+ !!]
+ end do
+ !![
+
+ !!]
+ return
+ end function summationConstructorParameters
+
+ function summationConstructorInternal(massDistributionHeatings) result(self)
+ !!{
+ Constructor for ``summation'' dark matter profile heating class.
+ !!}
+ implicit none
+ type(massDistributionHeatingSummation) :: self
+ type(massDistributionHeatingList ), pointer, intent(in ) :: massDistributionHeatings
+ type(massDistributionHeatingList ), pointer :: massDistributionHeating_
+
+ self %massDistributionHeatings => massDistributionHeatings
+ massDistributionHeating_ => massDistributionHeatings
+ do while (associated(massDistributionHeating_))
+ !![
+
+ !!]
+ massDistributionHeating_ => massDistributionHeating_%next
+ end do
+ return
+ end function summationConstructorInternal
+
+ subroutine summationDestructor(self)
+ !!{
+ Destructor for composite mass distributions.
+ !!}
+ implicit none
+ type(massDistributionHeatingSummation), intent(inout) :: self
+ type(massDistributionHeatingList ), pointer :: massDistributionHeating_, massDistributionHeatingNext
+
+ if (associated(self%massDistributionHeatings)) then
+ massDistributionHeating_ => self%massDistributionHeatings
+ do while (associated(massDistributionHeating_))
+ massDistributionHeatingNext => massDistributionHeating_%next
+ !![
+
+ !!]
+ deallocate(massDistributionHeating_)
+ massDistributionHeating_ => massDistributionHeatingNext
+ end do
+ end if
+ return
+ end subroutine summationDestructor
+
+ double precision function summationSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Returns the specific energy of heating in the given {\normalfont \ttfamily node}.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingSummation), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ type (massDistributionHeatingList ), pointer :: massDistributionHeating_
+
+ energySpecific = 0.0d0
+ massDistributionHeating_ => self%massDistributionHeatings
+ do while (associated(massDistributionHeating_))
+ energySpecific = + energySpecific &
+ & +massDistributionHeating_%massDistributionHeating_%specificEnergy(radius,massDistribution_)
+ massDistributionHeating_ => massDistributionHeating_%next
+ end do
+ return
+ end function summationSpecificEnergy
+
+ double precision function summationSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ implicit none
+ class (massDistributionHeatingSummation), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ type (massDistributionHeatingList ), pointer :: massDistributionHeating_
+
+ energySpecificGradient = 0.0d0
+ massDistributionHeating_ => self%massDistributionHeatings
+ do while (associated(massDistributionHeating_))
+ energySpecificGradient = + energySpecificGradient &
+ & +massDistributionHeating_%massDistributionHeating_%specificEnergyGradient(radius,massDistribution_)
+ massDistributionHeating_ => massDistributionHeating_%next
+ end do
+ return
+ end function summationSpecificEnergyGradient
+
+ logical function summationSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingSummation), intent(inout) :: self
+ type (massDistributionHeatingList ), pointer :: massDistributionHeating_
+
+ energySpecificIsEverywhereZero = .true.
+ massDistributionHeating_ => self%massDistributionHeatings
+ do while (associated(massDistributionHeating_))
+ energySpecificIsEverywhereZero=massDistributionHeating_%massDistributionHeating_%specificEnergyIsEverywhereZero()
+ if (.not.energySpecificIsEverywhereZero) return
+ massDistributionHeating_ => massDistributionHeating_%next
+ end do
+ return
+ end function summationSpecificEnergyIsEverywhereZero
diff --git a/source/mass_distributions.spherical.heating.tidal.F90 b/source/mass_distributions.spherical.heating.tidal.F90
new file mode 100644
index 0000000000..bbc1911fbe
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.tidal.F90
@@ -0,0 +1,250 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a tidal mass distribution heating class.
+ !!}
+
+ !![
+
+
+ A mass distribution heating model which accounts for heating due to tidal shocking. The model follows the general
+ approach of \cite{gnedin_tidal_1999}. The change in the specific energy of particles at radius $r$ in a halo is given by
+ $\Delta \epsilon = \Delta \epsilon_1 + \Delta \epsilon_2$, where $\Delta \epsilon_1$, and $\Delta \epsilon_2$ are the first
+ and second order perturbations respectively. The first order term is given by $\Delta \epsilon_1 = Q r^2$ where $Q$ is the
+ tidal tensor integrated along the orbital path (see, for example, \citealt{taylor_dynamics_2001}), while the second order
+ term is given by $\Delta \epsilon_2 = (2/3) f \sigma_\mathrm{rms} (1+\chi_\mathrm{r,v}) \sqrt{\Delta \epsilon_1}$
+ \citep[][eqn.~20, see also \protect\citealt{gnedin_self-consistent_1999}; eqn.~18a,b]{gnedin_tidal_1999}. For the particle
+ velocity dispersion, $v_\mathrm{rms}$, we use $\sqrt{3} \sigma_\mathrm{r}(r)$, the radial velocity dispersion in the dark
+ matter profile scaled to the total velocity dispersion assuming an isotropic velocity distribution. The position-velocity
+ correlation function, $\chi_\mathrm{r,v}$, is taken to be a constant given by the parameter {\normalfont \ttfamily
+ [correlationVelocityRadius]}. The coefficient, $f=${\normalfont \ttfamily [coefficientSecondOrder]} is introduced to allow
+ some freedom to adjust the contribution of the second order term. It is degenerate with the value of $\chi_\mathrm{r,v}$
+ but is introduced to allow for possible future promotion of $\chi_\mathrm{r,v}$ from a constant to a function of the dark
+ matter profile potential \citep[see, for example,][appendix~B]{gnedin_self-consistent_1999}.
+
+
+ !!]
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingTidal
+ !!{
+ Implementation of a tidal mass distribution heating class.
+ !!}
+ private
+ double precision :: correlationVelocityRadius, coefficientSecondOrder0, &
+ & coefficientSecondOrder1 , coefficientSecondOrder2, &
+ & heatSpecificNormalized
+ contains
+ !![
+
+
+
+ !!]
+ procedure :: specificEnergy => tidalSpecificEnergy
+ procedure :: specificEnergyGradient => tidalSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => tidalSpecificEnergyIsEverywhereZero
+ procedure :: specificEnergyTerms => tidalSpecificEnergyTerms
+ end type massDistributionHeatingTidal
+
+ interface massDistributionHeatingTidal
+ !!{
+ Constructors for the {\normalfont \ttfamily tidal} mass distribution class.
+ !!}
+ module procedure tidalConstructorParameters
+ module procedure tidalConstructorInternal
+ end interface massDistributionHeatingTidal
+
+contains
+
+ function tidalConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily tidal} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (massDistributionHeatingTidal) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: heatSpecificNormalized , correlationVelocityRadius, &
+ & coefficientSecondOrder0, coefficientSecondOrder1 , &
+ & coefficientSecondOrder2
+
+ !![
+
+ heatSpecificNormalized
+ The normalized specific tidal heating, $Q = \epsilon / r^2$.
+
+
+
+ coefficientSecondOrder0
+ 0.0d0
+
+ The coefficient, $a_0$, appearing in the second-order heating term, $f_2 = a_0 + a_1 \mathrm{d}\log \rho/\mathrm{d} \log r + a_2 (\mathrm{d}\log \rho/\mathrm{d} \log r)^2$.
+
+
+ coefficientSecondOrder1
+ 0.0d0
+
+ The coefficient, $a_1$, appearing in the second-order heating term, $f_2 = a_0 + a_1 \mathrm{d}\log \rho/\mathrm{d} \log r + a_2 (\mathrm{d}\log \rho/\mathrm{d} \log r)^2$.
+
+
+ coefficientSecondOrder2
+ 0.0d0
+
+ The coefficient, $a_2$, appearing in the second-order heating term, $f_2 = a_0 + a_1 \mathrm{d}\log \rho/\mathrm{d} \log r + a_2 (\mathrm{d}\log \rho/\mathrm{d} \log r)^2$.
+
+
+ correlationVelocityRadius
+ -1.0d0
+
+ The velocity-position correlation function, $\chi_\mathrm{r,v}$, as defined by \cite[][eqn.~B1]{gnedin_self-consistent_1999} which controls the strength of the second order heating term.
+
+ !!]
+ self=massDistributionHeatingTidal(heatSpecificNormalized,coefficientSecondOrder0,coefficientSecondOrder1,coefficientSecondOrder2,correlationVelocityRadius)
+ !![
+
+ !!]
+ return
+ end function tidalConstructorParameters
+
+ function tidalConstructorInternal(heatSpecificNormalized,coefficientSecondOrder0,coefficientSecondOrder1,coefficientSecondOrder2,correlationVelocityRadius) result(self)
+ !!{
+ Constructor for ``tidal'' convergence class.
+ !!}
+ implicit none
+ type (massDistributionHeatingTidal) :: self
+ double precision , intent(in ) :: heatSpecificNormalized , coefficientSecondOrder0, &
+ & coefficientSecondOrder1 , coefficientSecondOrder2, &
+ & correlationVelocityRadius
+ !![
+
+ !!]
+
+ return
+ end function tidalConstructorInternal
+
+ double precision function tidalSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Compute the specific energy in a tidally-heated mass distribution.
+ !!}
+ implicit none
+ class (massDistributionHeatingTidal), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
+
+ call self%specificEnergyTerms(radius,massDistribution_,energyPerturbationFirstOrder,energyPerturbationSecondOrder)
+ energySpecific=+energyPerturbationFirstOrder &
+ & +energyPerturbationSecondOrder
+ return
+ end function tidalSpecificEnergy
+
+ double precision function tidalSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingTidal), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
+ type (coordinateSpherical ) :: coordinates
+
+ if (radius > 0.0d0) then
+ call self%specificEnergyTerms(radius,massDistribution_,energyPerturbationFirstOrder,energyPerturbationSecondOrder)
+ if (energyPerturbationSecondOrder > 0.0d0) then
+ coordinates=[radius,0.0d0,0.0d0]
+ energySpecificGradient=+( &
+ & +energyPerturbationFirstOrder * 2.0d0 & ! dlog[r² ]/dlog(r) term
+ & +energyPerturbationSecondOrder*( &
+ & -0.5d0 & ! ⎧ dlog[σ_r(r)]/dlog[r] term
+ & *massDistribution_%densityGradientRadial (coordinates,logarithmic=.true. ) & ! ⎥
+ & -0.5d0 & ! ⎥ Assumes the Jeans equation in
+ & *gravitationalConstantGalacticus & ! ⎥ spherical symmetry with anisotropy
+ & *massDistribution_%massEnclosedBySphere (radius ) & ! ⎥ parameter β=0. Would be better to
+ & / radius & ! ⎥ have this provided by the
+ & /massDistribution_%kinematicsDistribution_%velocityDispersion1D(coordinates, massDistribution_)**2 & ! ⎩ darkMatterProfileDMO class.
+ & +1.0d0 & ! dlog[r ]/dlog(r) term
+ & ) &
+ & ) &
+ & /radius
+ else
+ energySpecificGradient=+ energyPerturbationFirstOrder * 2.0d0 & ! dlog[r² ]/dlog(r) term
+ & /radius
+ end if
+ else
+ energySpecificGradient =+0.0d0
+ end if
+ return
+ end function tidalSpecificEnergyGradient
+
+ subroutine tidalSpecificEnergyTerms(self,radius,massDistribution_,energyPerturbationFirstOrder,energyPerturbationSecondOrder)
+ !!{
+ Compute the first and second order perturbations to the energy.
+ !!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
+ implicit none
+ class (massDistributionHeatingTidal), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision , intent( out) :: energyPerturbationFirstOrder, energyPerturbationSecondOrder
+ double precision :: coefficientSecondOrder , densityLogSlope
+ type (coordinateSpherical ) :: coordinates
+
+ energyPerturbationFirstOrder=+self%heatSpecificNormalized &
+ & *radius **2
+ if ( &
+ & self%coefficientSecondOrder0 /= 0.0d0 &
+ & .or. &
+ & self%coefficientSecondOrder1 /= 0.0d0 &
+ & .or. &
+ & self%coefficientSecondOrder2 /= 0.0d0 &
+ & ) then
+ ! Compute the coefficient for the second order term.
+ coordinates=[radius,0.0d0,0.0d0]
+ densityLogSlope =+massDistribution_%densityGradientRadial(coordinates,logarithmic=.true.)
+ coefficientSecondOrder=+self %coefficientSecondOrder0 &
+ & +self %coefficientSecondOrder1*densityLogSlope &
+ & +self %coefficientSecondOrder2*densityLogSlope**2
+ ! Compute the second order energy perturbation.
+ energyPerturbationSecondOrder=+sqrt(2.0d0) &
+ & *coefficientSecondOrder &
+ & *( &
+ & +1.0d0 &
+ & +self%correlationVelocityRadius &
+ & ) &
+ & *sqrt(energyPerturbationFirstOrder) &
+ & *massDistribution_%kinematicsDistribution_%velocityDispersion1D(coordinates,massDistribution_)
+ else
+ energyPerturbationSecondOrder=+0.0d0
+ end if
+ return
+ end subroutine tidalSpecificEnergyTerms
+
+ logical function tidalSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingTidal), intent(inout) :: self
+
+ energySpecificIsEverywhereZero=self%heatSpecificNormalized <= 0.0d0
+ return
+ end function tidalSpecificEnergyIsEverywhereZero
diff --git a/source/mass_distributions.spherical.heating.two_body_relaxation.F90 b/source/mass_distributions.spherical.heating.two_body_relaxation.F90
new file mode 100644
index 0000000000..9167e8b5d8
--- /dev/null
+++ b/source/mass_distributions.spherical.heating.two_body_relaxation.F90
@@ -0,0 +1,244 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a mass distribution heating class that computes heating due to two-body relaxation.
+ !!}
+
+ !![
+
+ A mass distribution heating class that computes heating due to two-body relaxation.
+
+ !!]
+ type, extends(massDistributionHeatingClass) :: massDistributionHeatingTwoBodyRelaxation
+ !!{
+ Implementation of a mass distribution heating class that computes heating due to two-body relaxation.
+ !!}
+ private
+ double precision :: massParticle, lengthSoftening, &
+ & timeRelaxing, efficiency
+ contains
+ procedure :: specificEnergy => twoBodyRelaxationSpecificEnergy
+ procedure :: specificEnergyGradient => twoBodyRelaxationSpecificEnergyGradient
+ procedure :: specificEnergyIsEveryWhereZero => twoBodyRelaxationSpecificEnergyIsEverywhereZero
+ end type massDistributionHeatingTwoBodyRelaxation
+
+ interface massDistributionHeatingTwoBodyRelaxation
+ !!{
+ Constructors for the {\normalfont \ttfamily twoBodyRelaxation} mass distribution class.
+ !!}
+ module procedure twoBodyRelaxationConstructorParameters
+ module procedure twoBodyRelaxationConstructorInternal
+ end interface massDistributionHeatingTwoBodyRelaxation
+
+contains
+
+ function twoBodyRelaxationConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily twoBodyRelaxation} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ implicit none
+ type (massDistributionHeatingTwoBodyRelaxation) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: massParticle, lengthSoftening, &
+ & timeRelaxing, efficiency
+
+ !![
+
+ massParticle
+
+ The particle mass to use for two-body relaxation calculations.
+
+
+ lengthSoftening
+
+ The softening length to use for two-body relaxation calculations.
+
+
+ timeRelaxing
+
+ The time for which the system has been relaxing.
+
+
+ efficiency
+
+ The fractional efficiency of two-body relaxation heating.
+
+ !!]
+ self=massDistributionHeatingTwoBodyRelaxation(massParticle,lengthSoftening,timeRelaxing,efficiency)
+ !![
+
+ !!]
+ return
+ end function twoBodyRelaxationConstructorParameters
+
+ function twoBodyRelaxationConstructorInternal(massParticle,lengthSoftening,timeRelaxing,efficiency) result(self)
+ !!{
+ Constructor for ``twoBodyRelaxation'' dark matter profile heating class.
+ !!}
+ implicit none
+ type (massDistributionHeatingTwoBodyRelaxation) :: self
+ double precision , intent(in ) :: massParticle, lengthSoftening, &
+ & timeRelaxing, efficiency
+ !![
+
+ !!]
+
+ return
+ end function twoBodyRelaxationConstructorInternal
+
+ double precision function twoBodyRelaxationSpecificEnergy(self,radius,massDistribution_) result(energySpecific)
+ !!{
+ Returns the specific energy of heating in the given {\normalfont \ttfamily node}. The assumption here is that the mean
+ fractional change in energy for a particle per crossing time is $8 \log \Lambda / N$ where $N$ is the number of particles
+ within radius $r=${\normalfont \ttfamily radius}. The crossing time is approximated by $r/V(r)$ where $V(r)$ is the
+ circular velocity at $r$. The Coulomb logarithm is given by $\log\Lambda=\hbox{max}(\epsilon,b_{90})$ where $\epsilon$ is
+ the softening length, $b_{90}=2\mathrm{G}m_\mathrm{p}/V^2(r)$, and $m_\mathrm{p}$ is the particle mass. Finally, the
+ specific energy is assumed to be $\sigma^2(r)/2\approx V^2(r)/4$.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gigaYear, megaParsec, gravitationalConstantGalacticus
+ use :: Numerical_Constants_Prefixes , only : kilo
+ implicit none
+ class (massDistributionHeatingTwoBodyRelaxation), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision :: particleCount , velocity , &
+ & logarithmCoulomb , impactParameterCritical
+
+ if (self%timeRelaxing > 0.0d0) then
+ velocity =+massDistribution_ %rotationCurve (radius)
+ impactParameterCritical=+2.0d0 &
+ & *gravitationalConstantGalacticus &
+ & *self %massParticle &
+ & /velocity **2
+ logarithmCoulomb =+0.5d0 &
+ & *log( &
+ & +1.0d0 &
+ & +( &
+ & +radius &
+ & /max( &
+ & self %lengthSoftening , &
+ & impactParameterCritical &
+ & ) &
+ & ) **2 &
+ & )
+ particleCount =+massDistribution_ %massEnclosedBySphere(radius) &
+ & /self %massParticle
+ energySpecific =+2.0d0 &
+ & *self %efficiency &
+ & *logarithmCoulomb &
+ & *self %timeRelaxing &
+ & *velocity **3 &
+ & /radius &
+ & /particleCount &
+ & *kilo &
+ & *gigaYear &
+ & /megaParsec
+ else
+ energySpecific =+0.0d0
+ end if
+ return
+ end function twoBodyRelaxationSpecificEnergy
+
+ double precision function twoBodyRelaxationSpecificEnergyGradient(self,radius,massDistribution_) result(energySpecificGradient)
+ !!{
+ Returns the gradient of the specific energy of heating.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionHeatingTwoBodyRelaxation), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ class (massDistributionClass ), intent(inout) :: massDistribution_
+ double precision :: particleCount , velocity , &
+ & logarithmCoulomb , impactParameterCritical, &
+ & gradientCoulomb
+ type (coordinateSpherical ) :: coordinates
+
+ if (self%timeRelaxing > 0.0d0) then
+ coordinates =[radius,0.0d0,0.0d0]
+ velocity =+massDistribution_ %rotationCurve (radius)
+ impactParameterCritical =+2.0d0 &
+ & *gravitationalConstantGalacticus &
+ & *self %massParticle &
+ & /velocity **2
+ logarithmCoulomb =+0.5d0 &
+ & *log( &
+ & +1.0d0 &
+ & +( &
+ & +radius &
+ & /max( &
+ & self %lengthSoftening , &
+ & impactParameterCritical &
+ & ) &
+ & ) **2 &
+ & )
+ particleCount =+massDistribution_ %massEnclosedBySphere(radius) &
+ & /self %massParticle
+ if (self%lengthSoftening > impactParameterCritical) then
+ gradientCoulomb=+radius &
+ & /self %lengthSoftening
+ else
+ gradientCoulomb=+2.0d0 &
+ & *radius &
+ & /impactParameterCritical &
+ & *( &
+ & -1.0d0 &
+ & +8.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *radius **2 &
+ & *massDistribution_ %density (coordinates) &
+ & /velocity **2 &
+ & )
+ end if
+ energySpecificGradient=+self %specificEnergy(radius ,massDistribution_) &
+ & / radius &
+ & *( &
+ & -2.5d0 &
+ & +6.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *massDistribution_ %density (coordinates ) &
+ & *radius **2 &
+ & /velocity **2 &
+ & - radius &
+ & * gradientCoulomb &
+ & / logarithmCoulomb &
+ & *sqrt(exp(2.0d0*logarithmCoulomb)-1.0d0) &
+ & / exp(2.0d0*logarithmCoulomb) &
+ & )
+ else
+ energySpecificGradient=+0.0d0
+ end if
+ return
+ end function twoBodyRelaxationSpecificEnergyGradient
+
+ logical function twoBodyRelaxationSpecificEnergyIsEverywhereZero(self) result(energySpecificIsEverywhereZero)
+ !!{
+ Returns true if the specific energy is everywhere zero.
+ !!}
+ implicit none
+ class(massDistributionHeatingTwoBodyRelaxation), intent(inout) :: self
+
+ energySpecificIsEverywhereZero=self%timeRelaxing <= 0.0d0
+ return
+ end function twoBodyRelaxationSpecificEnergyIsEverywhereZero
diff --git a/source/mass_distributions.spherical.isothermal.F90 b/source/mass_distributions.spherical.isothermal.F90
new file mode 100644
index 0000000000..dc435c1357
--- /dev/null
+++ b/source/mass_distributions.spherical.isothermal.F90
@@ -0,0 +1,648 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of an isothermal mass distribution class.
+ !!}
+
+ !![
+
+
+ An isothermal mass distribution class in which the density profile is given by:
+ \begin{equation}
+ \rho(r) \propto r^{-2}.
+ \end{equation}
+
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionIsothermal
+ !!{
+ The isothermal mass distribution.
+ !!}
+ private
+ double precision :: densityNormalization, lengthReference, &
+ & velocityRotation
+ contains
+ procedure :: massTotal => isothermalMassTotal
+ procedure :: density => isothermalDensity
+ procedure :: densityGradientRadial => isothermalDensityGradientRadial
+ procedure :: densityRadialMoment => isothermalDensityRadialMoment
+ procedure :: massEnclosedBySphere => isothermalMassEnclosedBySphere
+ procedure :: rotationCurve => isothermalRotationCurve
+ procedure :: rotationCurveGradient => isothermalRotationCurveGradient
+ procedure :: velocityRotationCurveMaximum => isothermalVelocityRotationCurveMaximum
+ procedure :: radiusRotationCurveMaximum => isothermalRadiusRotationCurveMaximum
+ procedure :: radiusEnclosingMass => isothermalRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => isothermalRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => isothermalRadiusFromSpecificAngularMomentum
+ procedure :: fourierTransform => isothermalFourierTransform
+ procedure :: radiusFreefall => isothermalRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => isothermalRadiusFreefallIncreaseRate
+ procedure :: energyPotential => isothermalEnergyPotential
+ procedure :: energyKinetic => isothermalEnergyKinetic
+ procedure :: potentialIsAnalytic => isothermalPotentialIsAnalytic
+ procedure :: potential => isothermalPotential
+ procedure :: positionSample => isothermalPositionSample
+ procedure :: descriptor => isothermalDescriptor
+ end type massDistributionIsothermal
+
+ interface massDistributionIsothermal
+ !!{
+ Constructors for the {\normalfont \ttfamily isothermal} mass distribution class.
+ !!}
+ module procedure massDistributionIsothermalConstructorParameters
+ module procedure massDistributionIsothermalConstructorInternal
+ end interface massDistributionIsothermal
+
+contains
+
+ function massDistributionIsothermalConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily isothermal} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ type (massDistributionIsothermal) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ double precision :: mass , lengthReference, &
+ & densityNormalization
+ logical :: dimensionless
+ type (varying_string ) :: componentType
+ type (varying_string ) :: massType
+
+ !![
+
+ densityNormalization
+ 0.25d0/Pi
+ The density normalization of the isothermal profile.
+
+
+
+ lengthReference
+ 1.0d0
+ The scale radius of the isothermal profile.
+
+
+
+ mass
+ 1.0d0
+ The mass of the isothermal profile.
+
+
+
+ dimensionless
+ .true.
+ If true the isothermal profile is considered to be dimensionless.
+
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ self=massDistributionIsothermal(componentType=enumerationComponentTypeEncode(componentType,includesPrefix=.false.),massType=enumerationMassTypeEncode(massType,includesPrefix=.false.){conditions})
+
+
+
+
+
+
+ !!]
+ return
+ end function massDistributionIsothermalConstructorParameters
+
+ function massDistributionIsothermalConstructorInternal(densityNormalization,mass,lengthReference,dimensionless,componentType,massType) result(self)
+ !!{
+ Internal constructor for ``isothermal'' mass distribution class.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Comparison , only : Values_Differ
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ type (massDistributionIsothermal ) :: self
+ double precision , intent(in ), optional :: densityNormalization, mass, &
+ & lengthReference
+ logical , intent(in ), optional :: dimensionless
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ ! Determine if profile is dimensionless.
+ self%dimensionless=.false.
+ if (present(dimensionless)) self%dimensionless=dimensionless
+ ! If dimensionless, then set scale length and mass to unity.
+ if (self%dimensionless) then
+ if (present(lengthReference )) then
+ if (Values_Differ(lengthReference ,1.0d0 ,absTol=1.0d-6)) call Error_Report('scaleLength should be unity for a dimensionless profile (or simply do not specify a scale length)' //{introspection:location})
+ end if
+ if (present(mass )) then
+ if (Values_Differ(mass ,1.0d0 ,absTol=1.0d-6)) call Error_Report('mass should be unity for a dimensionless profile (or simply do not specify a mass)' //{introspection:location})
+ end if
+ if (present(densityNormalization)) then
+ if (Values_Differ(densityNormalization,0.25d0/Pi,absTol=1.0d-6)) call Error_Report('densityNormalization should be π/4 for a dimensionless profile (or simply do not specify a densityNormalization)'//{introspection:location})
+ end if
+ self%lengthReference =1.00d0
+ self%densityNormalization=0.25d0/Pi
+ else
+ if (present(lengthReference )) then
+ self%lengthReference=lengthReference
+ else
+ call Error_Report('"lengthReference" must be specified'//{introspection:location})
+ end if
+ if (present(densityNormalization)) then
+ self%densityNormalization=densityNormalization
+ else if (present(mass )) then
+ self%densityNormalization=mass /4.0d0/Pi/lengthReference**3
+ else
+ call Error_Report('one of "densityNormalization" or "mass" must be specified'//{introspection:location})
+ end if
+ end if
+ ! Compute the rotation velocity.
+ if (self%isDimensionless()) then
+ self%velocityRotation=+1.0d0
+ else
+ self%velocityRotation=+sqrt( &
+ & +4.0d0 &
+ & *Pi &
+ & *gravitationalConstantGalacticus &
+ & *self%lengthReference **2 &
+ & *self%densityNormalization &
+ & )
+ end if
+ return
+ end function massDistributionIsothermalConstructorInternal
+
+ double precision function isothermalMassTotal(self)
+ !!{
+ Return the total mass in an isothermal mass distribution.
+ !!}
+ implicit none
+ class(massDistributionIsothermal), intent(inout) :: self
+
+ isothermalMassTotal=huge(0.0d0)
+ return
+ end function isothermalMassTotal
+
+ double precision function isothermalDensity(self,coordinates)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an isothermal mass distribution.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateSpherical
+ implicit none
+ class(massDistributionIsothermal), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ isothermalDensity=+ self %densityNormalization &
+ & /( &
+ & +coordinates%rSpherical () &
+ & /self %lengthReference &
+ & )**2
+ return
+ end function isothermalDensity
+
+ double precision function isothermalDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an isothermal mass distribution.
+ !!}
+ implicit none
+ class (massDistributionIsothermal), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: radius
+ logical :: logarithmicActual
+
+ ! Set default options.
+ logarithmicActual=.false.
+ if (present(logarithmic)) logarithmicActual=logarithmic
+ ! Get position in spherical coordinate system.
+ radius=coordinates%rSpherical()
+ ! Compute density gradient.
+ if (logarithmicActual) then
+ isothermalDensityGradientRadial=-2.0d0
+ else
+ isothermalDensityGradientRadial=-2.0d0 &
+ & *self%densityNormalization &
+ & *self%lengthReference **2 &
+ & /radius **3
+ end if
+ return
+ end function isothermalDensityGradientRadial
+
+ double precision function isothermalMassEnclosedBySphere(self,radius)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for isothermal mass distributions.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionIsothermal), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ isothermalMassEnclosedBySphere=+4.0d0 &
+ & *Pi &
+ & *self%densityNormalization &
+ & *self%lengthReference **2 &
+ & *radius
+ return
+ end function isothermalMassEnclosedBySphere
+
+ double precision function isothermalRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for isothermal mass distributions.
+ !!}
+ use :: Error , only : Error_Report
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionIsothermal), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision :: mass_
+
+ mass_=0.0d0
+ if (present(mass)) then
+ mass_=mass
+ else if (present(massFractional)) then
+ call Error_Report('mass is unbounded, so mass fraction is undefined'//{introspection:location})
+ else
+ call Error_Report('either mass or massFractional must be supplied'//{introspection:location})
+ end if
+ radius=+ mass_ &
+ & / 4.0d0 &
+ & / Pi &
+ & /self%densityNormalization &
+ & /self%lengthReference **2
+ return
+ end function isothermalRadiusEnclosingMass
+
+ double precision function isothermalRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for isothermal mass distributions.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ class (massDistributionIsothermal), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ radius=+ self%lengthReference &
+ & *sqrt( &
+ & +3.0d0 &
+ & *self%densityNormalization &
+ & / density &
+ & )
+ return
+ end function isothermalRadiusEnclosingDensity
+
+ double precision function isothermalRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for isothermal mass distributions.
+ !!}
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionIsothermal), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+
+ radius=+angularMomentumSpecific &
+ & /sqrt( &
+ & +4.0d0 &
+ & *Pi &
+ & *self%densityNormalization &
+ & *self%lengthReference**2 &
+ & )
+ if (.not.self%isDimensionless()) radius=+radius &
+ & /sqrt(gravitationalConstantGalacticus)
+ return
+ end function isothermalRadiusFromSpecificAngularMomentum
+
+ double precision function isothermalDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
+ !!{
+ Returns a radial density moment for the Isothermal mass distribution.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ double precision :: momentMinimum, momentMaximum
+
+ isothermalDensityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ if (present(radiusMinimum)) then
+ if (moment == 1.0d0) then
+ momentMinimum=+log(+radiusMinimum )
+ else
+ momentMinimum= +radiusMinimum**(moment-1.0d0) &
+ & / (moment-1.0d0)
+ end if
+ else if (moment <= 1.0d0) then
+ momentMinimum=+0.0d0
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ return
+ else
+ call Error_Report('radial moment is infinite'//{introspection:location})
+ end if
+ else
+ momentMinimum=0.0d0
+ end if
+ if (present(radiusMaximum)) then
+ if (moment == 1.0d0) then
+ momentMaximum=+log(+radiusMaximum )
+ else
+ momentMaximum= +radiusMaximum**(moment-1.0d0) &
+ & / (moment-1.0d0)
+ end if
+ else if (moment >= 1.0d0) then
+ momentMaximum=+0.0d0
+ if (present(isInfinite)) then
+ isInfinite=.true.
+ return
+ else
+ call Error_Report('radial moment is infinite'//{introspection:location})
+ end if
+ else
+ momentMaximum=0.0d0
+ end if
+ isothermalDensityRadialMoment=+self%densityNormalization &
+ & *self%lengthReference **2 &
+ & *( &
+ & +momentMaximum &
+ & -momentMinimum &
+ & )
+ return
+ end function isothermalDensityRadialMoment
+
+ double precision function isothermalRotationCurve(self,radius) result(rotationCurve)
+ !!{
+ Return the rotation curve for an isothermal mass distribution.
+ !!}
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ rotationCurve=self%velocityRotation
+ return
+ end function isothermalRotationCurve
+
+ double precision function isothermalRotationCurveGradient(self,radius) result(rotationCurveGradient)
+ !!{
+ Return the rotation curve gradient (specifically, $\mathrm{d}V^2_\mathrm{c}/\mathrm{d}r$) for an isothermal mass distribution.
+ !!}
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
+
+ rotationCurveGradient=0.0d0
+ return
+ end function isothermalRotationCurveGradient
+
+ double precision function isothermalVelocityRotationCurveMaximum(self) result(velocity)
+ !!{
+ Return the peak velocity in the rotation curve for an isothermal mass distribution.
+ !!}
+ implicit none
+ class(massDistributionIsothermal), intent(inout) :: self
+
+ velocity=self%velocityRotation
+ return
+ end function isothermalVelocityRotationCurveMaximum
+
+ double precision function isothermalRadiusRotationCurveMaximum(self) result(radius)
+ !!{
+ Return the peak velocity in the rotation curve for an isothermal mass distribution.
+ !!}
+ implicit none
+ class(massDistributionIsothermal), intent(inout), target :: self
+ !$GLC attributes unused :: self
+
+ radius=1.0d0
+ return
+ end function isothermalRadiusRotationCurveMaximum
+
+ logical function isothermalPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionIsothermal), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function isothermalPotentialIsAnalytic
+
+ double precision function isothermalPotential(self,coordinates,status)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} in an isothermal mass distribution.
+ !!}
+ use :: Coordinates , only : assignment(=)
+ use :: Galactic_Structure_Options , only : structureErrorCodeSuccess , structureErrorCodeInfinite
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Error , only : Error_Report
+ implicit none
+ class(massDistributionIsothermal ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+
+ if (present(status)) status=structureErrorCodeSuccess
+ ! Compute the potential at this position.
+ if (coordinates%rSpherical() <= 0.0d0) then
+ isothermalPotential=0.0d0
+ if (present(status)) then
+ status=structureErrorCodeInfinite
+ return
+ else
+ call Error_Report('potential is divergent at zero radius'//{introspection:location})
+ end if
+ end if
+ isothermalPotential=+ 4.0d0 &
+ & *Pi &
+ & * self %densityNormalization &
+ & * self %lengthReference **2 &
+ & *log( &
+ & +coordinates%rSpherical () &
+ & /self %lengthReference &
+ & )
+ if (.not.self%isDimensionless()) isothermalPotential=+gravitationalConstantGalacticus &
+ & *isothermalPotential
+ return
+ end function isothermalPotential
+
+ double precision function isothermalFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in an isothermal mass distribution.
+ !!}
+ use :: Exponential_Integrals, only : Sine_Integral
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter , wavenumber
+ double precision :: wavenumberScaleFree
+
+ waveNumberScaleFree=+waveNumber &
+ & *radiusOuter
+ fourierTransform =+Sine_Integral(waveNumberScaleFree) &
+ & / waveNumberScaleFree
+ return
+ end function isothermalFourierTransform
+
+ double precision function isothermalRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in an isothermal mass distribution. For an isothermal
+ potential, the freefall radius, $r_\mathrm{ff}(t)$, is:
+ \begin{equation}
+ r_\mathrm{ff}(t) = \sqrt{{2 \over \pi}} V_\mathrm{virial} t.
+ \end{equation}
+ !!}
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radius=+sqrt( &
+ & +2.0d0 &
+ & /Pi &
+ & ) &
+ & *self%velocityRotation &
+ & * time &
+ & /Mpc_per_km_per_s_To_Gyr
+ return
+ end function isothermalRadiusFreefall
+
+ double precision function isothermalRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an isothermal mass
+ distribution. For an isothermal potential, the rate of increase of the freefall radius, $\dot{r}_\mathrm{ff}(t)$, is:
+ \begin{equation}
+ \dot{r}_\mathrm{ff}(t) = \sqrt{{2 \over \pi}} V_\mathrm{virial}.
+ \end{equation}
+ !!}
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : Mpc_per_km_per_s_To_Gyr
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: time
+ !$GLC attributes unused :: time
+
+ radiusIncreaseRate=+sqrt( &
+ & +2.0d0 &
+ & /Pi &
+ & ) &
+ & *self%velocityRotation &
+ & /Mpc_per_km_per_s_To_Gyr
+ return
+ end function isothermalRadiusFreefallIncreaseRate
+
+ double precision function isothermalEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in an isothermal mass distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Numerical_Constants_Math , only : Pi
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ energy=-16.0d0 &
+ & *Pi **2 &
+ & * gravitationalConstantGalacticus &
+ & *self%lengthReference **4 &
+ & *self%densityNormalization **2 &
+ & * radiusOuter
+ return
+ end function isothermalEnergyPotential
+
+ double precision function isothermalEnergyKinetic(self,radiusOuter,massDistributionEmbedding) result(energy)
+ !!{
+ Compute the kinetic energy within a given {\normalfont \ttfamily radius} in an isothermal mass distribution.
+ !!}
+ use :: Coordinates, only : assignment(=), coordinateSpherical
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+ class (massDistributionClass ), intent(inout) :: massDistributionEmbedding
+ logical :: analytic
+ type (coordinateSpherical ) :: coordinates
+
+ analytic=.false.
+ select type (massDistributionEmbedding)
+ class is (massDistributionIsothermal)
+ select type (kinematicsDistribution_ => massDistributionEmbedding%kinematicsDistribution_)
+ class is (kinematicsDistributionIsothermal)
+ analytic =.true.
+ coordinates=[radiusOuter,0.0d0,0.0d0]
+ energy =+1.5d0 &
+ & *self %massEnclosedBySphere(radiusOuter ) &
+ & *kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionEmbedding)
+ end select
+ end select
+ if (.not.analytic) energy=self%energyKineticNumerical(radiusOuter,massDistributionEmbedding)
+ return
+ end function isothermalEnergyKinetic
+
+ function isothermalPositionSample(self,randomNumberGenerator_) result(position)
+ !!{
+ Computes the half-mass radius of a spherically symmetric mass distribution using numerical root finding.
+ !!}
+ use :: Numerical_Constants_Math, only : Pi
+ implicit none
+ double precision , dimension(3) :: position
+ class (massDistributionIsothermal), intent(inout) :: self
+ class (randomNumberGeneratorClass), intent(inout) :: randomNumberGenerator_
+
+ position=0.0d0
+ call Error_Report('can not sample positions, mass is unbounded'//{introspection:location})
+ return
+ end function isothermalPositionSample
+
+ subroutine isothermalDescriptor(self,descriptor,includeClass,includeFileModificationTimes)
+ !!{
+ Return an input parameter list descriptor which could be used to recreate this object.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ class (massDistributionIsothermal), intent(inout) :: self
+ type (inputParameters ), intent(inout) :: descriptor
+ logical , intent(in ), optional :: includeClass , includeFileModificationTimes
+ character(len=18 ) :: parameterLabel
+ type (inputParameters ) :: parameters
+
+ if (.not.present(includeClass).or.includeClass) call descriptor%addParameter('massDistribution','isothermal')
+ parameters=descriptor%subparameters('massDistribution')
+ write (parameterLabel,'(e17.10)') self%densityNormalization
+ call parameters%addParameter('densityNormalization',trim(adjustl(parameterLabel)))
+ write (parameterLabel,'(e17.10)') self%lengthReference
+ call parameters%addParameter('lengthReference' ,trim(adjustl(parameterLabel)))
+ return
+ end subroutine isothermalDescriptor
diff --git a/source/mass_distributions.spherical.null.F90 b/source/mass_distributions.spherical.null.F90
new file mode 100644
index 0000000000..2fa7e680e5
--- /dev/null
+++ b/source/mass_distributions.spherical.null.F90
@@ -0,0 +1,217 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implementation of a zero mass distribution class.
+ !!}
+
+ !![
+
+ A zero mass distribution class.
+
+ !!]
+ type, public, extends(massDistributionSpherical) :: massDistributionZero
+ !!{
+ A zero mass distribution.
+ !!}
+ contains
+ procedure :: massTotal => zeroMassTotal
+ procedure :: density => zeroDensity
+ procedure :: densityGradientRadial => zeroDensityGradientRadial
+ procedure :: densityRadialMoment => zeroDensityRadialMoment
+ procedure :: massEnclosedBySphere => zeroMassEnclosedBySphere
+ procedure :: rotationCurve => zeroRotationCurve
+ procedure :: rotationCurveGradient => zeroRotationCurveGradient
+ procedure :: potentialIsAnalytic => zeroPotentialIsAnalytic
+ procedure :: potential => zeroPotential
+ end type massDistributionZero
+
+ interface massDistributionZero
+ !!{
+ Constructors for the {\normalfont \ttfamily zero} mass distribution class.
+ !!}
+ module procedure zeroConstructorParameters
+ module procedure zeroConstructorInternal
+ end interface massDistributionZero
+
+contains
+
+ function zeroConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily zero} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (massDistributionZero) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ logical :: dimensionless
+
+ !![
+
+ dimensionless
+ .true.
+ If true the null profile is considered to be dimensionless.
+
+
+ !!]
+ self=massDistributionZero(dimensionless)
+ !![
+
+ !!]
+ return
+ end function zeroConstructorParameters
+
+ function zeroConstructorInternal(dimensionless) result(self)
+ !!{
+ Constructor for {\normalfont \ttfamily zero} mass distribution class.
+ !!}
+ use :: Galactic_Structure_Options, only : componentTypeUnknown, massTypeUnknown
+ implicit none
+ type (massDistributionZero) :: self
+ logical , intent(in ) :: dimensionless
+ !![
+
+ !!]
+
+ self%componentType=componentTypeUnknown
+ self%massType =massTypeUnknown
+ return
+ end function zeroConstructorInternal
+
+ double precision function zeroMassTotal(self)
+ !!{
+ Return the total mass in the zero distribution.
+ !!}
+ implicit none
+ class(massDistributionZero), intent(inout) :: self
+ !$GLC attributes unused :: self
+
+ zeroMassTotal=0.0d0
+ return
+ end function zeroMassTotal
+
+ double precision function zeroDensity(self,coordinates)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a zero distribution.
+ !!}
+ implicit none
+ class(massDistributionZero), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ !$GLC attributes unused :: self, coordinates
+
+ zeroDensity=0.0d0
+ return
+ end function zeroDensity
+
+ double precision function zeroDensityGradientRadial(self,coordinates,logarithmic)
+ !!{
+ Return the density gradient in the radial direction for a point mass.
+ !!}
+ implicit none
+ class (massDistributionZero), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ !$GLC attributes unused :: self, coordinates, logarithmic
+
+ zeroDensityGradientRadial=0.0d0
+ return
+ end function zeroDensityGradientRadial
+
+ double precision function zeroMassEnclosedBySphere(self,radius)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a zero distribution.
+ !!}
+ implicit none
+ class (massDistributionZero), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
+
+ zeroMassEnclosedBySphere=0.0d0
+ return
+ end function zeroMassEnclosedBySphere
+
+ double precision function zeroRotationCurve(self,radius)
+ !!{
+ Return the rotation curve for a zero mass distribution.
+ !!}
+ implicit none
+ class (massDistributionZero), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
+
+ zeroRotationCurve=0.0d0
+ return
+ end function zeroRotationCurve
+
+ double precision function zeroRotationCurveGradient(self,radius)
+ !!{
+ Return the rotation curve gradient for a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionZero), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ !$GLC attributes unused :: self, radius
+
+ zeroRotationCurveGradient=0.0d0
+ return
+ end function zeroRotationCurveGradient
+
+ logical function zeroPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionZero), intent(inout) :: self
+
+ isAnalytic=.true.
+ return
+ end function zeroPotentialIsAnalytic
+
+ double precision function zeroPotential(self,coordinates,status)
+ !!{
+ Return the potential at the specified {\normalfont \ttfamily coordinates} for a point mass.
+ !!}
+ use :: Galactic_Structure_Options, only : structureErrorCodeSuccess
+ implicit none
+ class(massDistributionZero ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ !$GLC attributes unused :: self, coordinates
+
+ if (present(status)) status=structureErrorCodeSuccess
+ zeroPotential=0.0d0
+ return
+ end function zeroPotential
+
+ double precision function zeroDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
+ !!{
+ Computes radial moments of the density for a point mass.
+ !!}
+ implicit none
+ class (massDistributionZero), intent(inout) :: self
+ double precision , intent(in ) :: moment
+ double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
+ logical , intent( out), optional :: isInfinite
+ !$GLC attributes unused :: self, moment, radiusMinimum, radiusMaximum
+
+ zeroDensityRadialMoment=0.0d0
+ if (present(isInfinite)) isInfinite=.false.
+ return
+ end function zeroDensityRadialMoment
diff --git a/source/mass_distributions.spherical.scaler.F90 b/source/mass_distributions.spherical.scaler.F90
index 1b0abb8111..8a19b96698 100644
--- a/source/mass_distributions.spherical.scaler.F90
+++ b/source/mass_distributions.spherical.scaler.F90
@@ -27,7 +27,7 @@
A mass distribution class for scaling spherical mass distributions. Specifically, the density at position $\mathbf{x}$ is
given by
\begin{equation}
- \rho(\mathbf{x}) = f_\mathrm{M} \rho^\prime(\mathbf{x}/f_\mathrm{r}),
+ \rho(\mathbf{x}) = \frac{f_\mathrm{M}}{f_\mathrm{r}^3} \rho^\prime(\mathbf{x}/f_\mathrm{r}),
\end{equation}
where $\rho^\prime(\mathbf{x})$ is the original mass distribution, and $f_\mathrm{r}=${\normalfont \ttfamily
[factorScalingLength]}, and $f_\mathrm{M}=${\normalfont \ttfamily [factorScalingMass]}.
@@ -38,20 +38,36 @@
!!{
A mass distribution class for scaling spherical mass distributions.
!!}
- class (massDistributionSpherical), pointer :: massDistribution_ => null()
- double precision :: factorScalingLength , factorScalingMass
+ class (massDistributionSpherical ), pointer :: massDistribution_ => null()
+ double precision :: factorScalingLength , factorScalingMass
+ ! Memoized results.
+ double precision , dimension(3) :: positionTidalTensorPrevious
+ type (tensorRank2Dimension3Symmetric) :: tidalTensorPrevious
contains
- final :: sphericalScalerDestructor
- procedure :: density => sphericalScalerDensity
- procedure :: densityGradientRadial => sphericalScalerDensityGradientRadial
- procedure :: densityRadialMoment => sphericalScalerDensityRadialMoment
- procedure :: massEnclosedBySphere => sphericalScalerMassEnclosedBySphere
- procedure :: potential => sphericalScalerPotential
- procedure :: radiusHalfMass => sphericalScalerRadiusHalfMass
- procedure :: tidalTensor => sphericalScalerTidalTensor
- procedure :: radiusEnclosingMass => sphericalScalerRadiusEnclosingMass
- procedure :: positionSample => sphericalScalerPositionSample
- procedure :: isDimensionless => sphericalScalerIsDimensionless
+ final :: sphericalScalerDestructor
+ procedure :: massTotal => sphericalScalerMassTotal
+ procedure :: density => sphericalScalerDensity
+ procedure :: densityGradientRadial => sphericalScalerDensityGradientRadial
+ procedure :: densityRadialMoment => sphericalScalerDensityRadialMoment
+ procedure :: massEnclosedBySphere => sphericalScalerMassEnclosedBySphere
+ procedure :: velocityRotationCurveMaximum => sphericalScalerVelocityRotationCurveMaximum
+ procedure :: radiusRotationCurveMaximum => sphericalScalerRadiusRotationCurveMaximum
+ procedure :: radiusEnclosingMass => sphericalScalerRadiusEnclosingMass
+ procedure :: radiusEnclosingDensity => sphericalScalerRadiusEnclosingDensity
+ procedure :: radiusFromSpecificAngularMomentum => sphericalScalerRadiusFromSpecificAngularMomentum
+ procedure :: fourierTransform => sphericalScalerFourierTransform
+ procedure :: radiusFreefall => sphericalScalerRadiusFreefall
+ procedure :: radiusFreefallIncreaseRate => sphericalScalerRadiusFreefallIncreaseRate
+ procedure :: energyPotential => sphericalScalerEnergyPotential
+ procedure :: densitySphericalAverage => sphericalScalerDensitySphericalAverage
+ procedure :: rotationCurve => sphericalScalerRotationCurve
+ procedure :: rotationCurveGradient => sphericalScalerRotationCurveGradient
+ procedure :: potentialIsAnalytic => sphericalScalerPotentialIsAnalytic
+ procedure :: potential => sphericalScalerPotential
+ procedure :: radiusHalfMass => sphericalScalerRadiusHalfMass
+ procedure :: tidalTensor => sphericalScalerTidalTensor
+ procedure :: acceleration => sphericalScalerAcceleration
+ procedure :: positionSample => sphericalScalerPositionSample
end type massDistributionSphericalScaler
interface massDistributionSphericalScaler
@@ -115,8 +131,10 @@ function sphericalScalerConstructorInternal(factorScalingLength,factorScalingMas
!!]
- self%componentType=self%massDistribution_%componentType
- self% massType=self%massDistribution_% massType
+ self%componentType =self%massDistribution_%componentType
+ self% massType =self%massDistribution_% massType
+ self%dimensionless =.false.
+ self%positionTidalTensorPrevious=-huge(0.0d0)
return
end function sphericalScalerConstructorInternal
@@ -133,58 +151,55 @@ subroutine sphericalScalerDestructor(self)
return
end subroutine sphericalScalerDestructor
- logical function sphericalScalerIsDimensionless(self)
+ double precision function sphericalScalerMassTotal(self)
!!{
- Return the dimensional status.
+ Return the total mass in a scaled spherical distribution.
!!}
implicit none
class(massDistributionSphericalScaler), intent(inout) :: self
- sphericalScalerIsDimensionless=.false.
+ sphericalScalerMassTotal=+self%massDistribution_%massTotal () &
+ & *self %factorScalingMass
return
- end function sphericalScalerIsDimensionless
+ end function sphericalScalerMassTotal
- double precision function sphericalScalerDensity(self,coordinates,componentType,massType)
+ double precision function sphericalScalerDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
!!}
implicit none
- class(massDistributionSphericalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerDensity=+self%massDistribution_%density ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
- & /self %factorScalingLength**3
+ class(massDistributionSphericalScaler), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(coordinate ), allocatable :: coordinatesScaled
+
+ if (self%factorScalingMass > 0.0d0) then
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ sphericalScalerDensity=+self%massDistribution_%density (coordinatesScaled) &
+ & *self %factorScalingMass &
+ & /self %factorScalingLength**3
+ else
+ sphericalScalerDensity=+0.0d0
+ end if
return
end function sphericalScalerDensity
- double precision function sphericalScalerDensityGradientRadial(self,coordinates,logarithmic,componentType,massType)
+ double precision function sphericalScalerDensityGradientRadial(self,coordinates,logarithmic)
!!{
Return the density gradient in the radial direction in a scaled spherical mass distribution.
!!}
implicit none
- class (massDistributionSphericalScaler), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- logical , intent(in ), optional :: logarithmic
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class (massDistributionSphericalScaler), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ class (coordinate ) , allocatable :: coordinatesScaled
!![
!!]
- sphericalScalerDensityGradientRadial=+self%massDistribution_%densityGradientRadial( &
- & coordinates &
- & /self%factorScalingLength, &
- & logarithmic , &
- & componentType , &
- & massType &
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ sphericalScalerDensityGradientRadial=+self%massDistribution_%densityGradientRadial( &
+ & coordinatesScaled, &
+ & logarithmic &
& )
if (.not.logarithmic) &
& sphericalScalerDensityGradientRadial=+sphericalScalerDensityGradientRadial &
@@ -193,48 +208,58 @@ double precision function sphericalScalerDensityGradientRadial(self,coordinates,
return
end function sphericalScalerDensityGradientRadial
- double precision function sphericalScalerMassEnclosedBySphere(self,radius,componentType,massType)
+ double precision function sphericalScalerMassEnclosedBySphere(self,radius)
!!{
Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for a scaled spherical mass distribution.
!!}
implicit none
- class (massDistributionSphericalScaler), intent(inout), target :: self
- double precision , intent(in ) :: radius
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerMassEnclosedBySphere=+self%massDistribution_%massEnclosedBySphere( &
- & radius &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
+ class (massDistributionSphericalScaler), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ sphericalScalerMassEnclosedBySphere=+self%massDistribution_%massEnclosedBySphere( &
+ & radius &
+ & /self%factorScalingLength &
+ & ) &
& *self %factorScalingMass
return
end function sphericalScalerMassEnclosedBySphere
- double precision function sphericalScalerPotential(self,coordinates,componentType,massType)
+ logical function sphericalScalerPotentialIsAnalytic(self) result(isAnalytic)
+ !!{
+ Return that the potential has an analytic form.
+ !!}
+ implicit none
+ class(massDistributionSphericalScaler), intent(inout) :: self
+
+ isAnalytic=self%massDistribution_%potentialIsAnalytic()
+ return
+ end function sphericalScalerPotentialIsAnalytic
+
+ double precision function sphericalScalerPotential(self,coordinates,status)
!!{
Return the potential at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- class(massDistributionSphericalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerPotential=+self%massDistribution_%potential ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
+ class(massDistributionSphericalScaler ), intent(inout), target :: self
+ class(coordinate ), intent(in ) :: coordinates
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ class(coordinate ) , allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ sphericalScalerPotential=+self%massDistribution_%potential ( &
+ & coordinatesScaled, &
+ & status &
+ & ) &
+ & *self %factorScalingMass &
& /self %factorScalingLength
+ if (self%massDistribution_%isDimensionless()) &
+ & sphericalScalerPotential=+sphericalScalerPotential &
+ & *gravitationalConstantGalacticus
return
end function sphericalScalerPotential
- double precision function sphericalScalerDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite,componentType,massType)
+ double precision function sphericalScalerDensityRadialMoment(self,moment,radiusMinimum,radiusMaximum,isInfinite)
!!{
Computes radial moments of the density in a scaled spherical mass distribution.
!!}
@@ -243,13 +268,11 @@ double precision function sphericalScalerDensityRadialMoment(self,moment,radiusM
double precision , intent(in ) :: moment
double precision , intent(in ), optional :: radiusMinimum, radiusMaximum
logical , intent( out), optional :: isInfinite
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
sphericalScalerDensityRadialMoment=0.0d0
!![
- sphericalScalerDensityRadialMoment=self%massDistribution_%densityRadialMoment(moment=moment,isInfinite=isInfinite,componentType=componentType,massType=massType{conditions})
+ sphericalScalerDensityRadialMoment=self%massDistribution_%densityRadialMoment(moment=moment,isInfinite=isInfinite{conditions})
@@ -260,97 +283,320 @@ double precision function sphericalScalerDensityRadialMoment(self,moment,radiusM
return
end function sphericalScalerDensityRadialMoment
- double precision function sphericalScalerRadiusEnclosingMass(self,mass,componentType,massType)
- !!{
- Computes the radius enclosing a given mass in a scaled spherical mass distribution.
- !!}
- implicit none
- class (massDistributionSphericalScaler), intent(inout), target :: self
- double precision , intent(in ) :: mass
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerRadiusEnclosingMass=+self%massDistribution_%radiusEnclosingMass( &
- & + mass &
- & /self%factorScalingMass, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingLength
- return
- end function sphericalScalerRadiusEnclosingMass
-
- double precision function sphericalScalerRadiusHalfMass(self,componentType,massType)
+ double precision function sphericalScalerRadiusHalfMass(self)
!!{
Computes the half-mass radius in a scaled spherical mass distribution.
!!}
implicit none
- class(massDistributionSphericalScaler), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ class(massDistributionSphericalScaler), intent(inout) :: self
- sphericalScalerRadiusHalfMass=+self%massDistribution_%radiusHalfMass (componentType,massType) &
+ sphericalScalerRadiusHalfMass=+self%massDistribution_%radiusHalfMass () &
& *self %factorScalingLength
return
end function sphericalScalerRadiusHalfMass
- function sphericalScalerAcceleration(self,coordinates,componentType,massType)
+ function sphericalScalerAcceleration(self,coordinates)
!!{
Computes the gravitational acceleration at {\normalfont \ttfamily coordinates} for spherically-symmetric mass
distributions.
!!}
+ use :: Numerical_Constants_Astronomical, only : gigaYear, gravitationalConstantGalacticus, megaParsec
+ use :: Numerical_Constants_Prefixes , only : kilo
implicit none
double precision , dimension(3) :: sphericalScalerAcceleration
class (massDistributionSphericalScaler), intent(inout) :: self
class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerAcceleration=+self%massDistribution_%acceleration ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
- & /self %factorScalingLength**2
+ class (coordinate ), allocatable :: coordinatesScaled
+
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ sphericalScalerAcceleration=+self%massDistribution_%acceleration ( &
+ & coordinatesScaled &
+ & ) &
+ & *self %factorScalingMass &
+ & /self %factorScalingLength**2
+ if (self%massDistribution_%isDimensionless()) &
+ & sphericalScalerAcceleration=+sphericalScalerAcceleration &
+ & *kilo &
+ & *gigaYear &
+ & /megaParsec &
+ & *gravitationalConstantGalacticus
return
end function sphericalScalerAcceleration
- function sphericalScalerTidalTensor(self,coordinates,componentType,massType)
+ double precision function sphericalScalerDensitySphericalAverage(self,radius)
+ !!{
+ Return the spherically-averaged density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass
+ distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ sphericalScalerDensitySphericalAverage=+self%massDistribution_%densitySphericalAverage( &
+ & + radius &
+ & /self%factorScalingLength &
+ & ) &
+ & *self %factorScalingMass &
+ & /self %factorScalingLength**3
+ return
+ end function sphericalScalerDensitySphericalAverage
+
+ double precision function sphericalScalerRotationCurve(self,radius)
+ !!{
+ Return the mid-plane rotation curve for a scaled spherical distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ sphericalScalerRotationCurve=+ self%massDistribution_%rotationCurve ( &
+ & + radius &
+ & /self%factorScalingLength &
+ & ) &
+ & *sqrt( &
+ & +self %factorScalingMass &
+ & /self %factorScalingLength &
+ & )
+ if (self%massDistribution_%isDimensionless()) &
+ & sphericalScalerRotationCurve=+sphericalScalerRotationCurve &
+ & *sqrt(gravitationalConstantGalacticus)
+ return
+ end function sphericalScalerRotationCurve
+
+ double precision function sphericalScalerRotationCurveGradient(self,radius)
+ !!{
+ Return the mid-plane rotation curve gradient (specifically, $\mathrm{d}V^2_\mathrm{c}/\mathrm{d}r$) for a scaled spherical distribution.
+ !!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radius
+
+ sphericalScalerRotationCurveGradient=+self%massDistribution_%rotationCurveGradient( &
+ & + radius &
+ & /self%factorScalingLength &
+ & ) &
+ & *self%factorScalingMass &
+ & /self%factorScalingLength**2
+ if (self%massDistribution_%isDimensionless()) &
+ & sphericalScalerRotationCurveGradient=+sphericalScalerRotationCurveGradient &
+ & *gravitationalConstantGalacticus
+ return
+ end function sphericalScalerRotationCurveGradient
+
+ function sphericalScalerTidalTensor(self,coordinates) result(tidalTensor)
!!{
Computes the gravitational tidal tensor at {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
!!}
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
implicit none
- type (tensorRank2Dimension3Symmetric ) :: sphericalScalerTidalTensor
- class(massDistributionSphericalScaler), intent(inout) :: self
- class(coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
-
- sphericalScalerTidalTensor=+self%massDistribution_%tidalTensor ( &
- & coordinates &
- & /self%factorScalingLength, &
- & componentType , &
- & massType &
- & ) &
- & *self %factorScalingMass &
- & /self %factorScalingLength**3
+ type (tensorRank2Dimension3Symmetric ) :: tidalTensor
+ class(massDistributionSphericalScaler), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+ class(coordinate ), allocatable :: coordinatesScaled
+ type (coordinateCartesian ) :: position
+
+ position=coordinates
+ if (any(position%position /= self%positionTidalTensorPrevious)) then
+ call coordinates%scale(1.0d0/self%factorScalingLength,coordinatesScaled)
+ self%tidalTensorPrevious=+self%massDistribution_%tidalTensor ( &
+ & coordinatesScaled &
+ & ) &
+ & *self %factorScalingMass &
+ & /self %factorScalingLength**3
+ if (self%massDistribution_%isDimensionless()) &
+ & self%tidalTensorPrevious=+self%tidalTensorPrevious &
+ & *gravitationalConstantGalacticus
+ self%positionTidalTensorPrevious=position%position
+ end if
+ tidalTensor=self%tidalTensorPrevious
return
end function sphericalScalerTidalTensor
- function sphericalScalerPositionSample(self,randomNumberGenerator_,componentType,massType)
+ function sphericalScalerPositionSample(self,randomNumberGenerator_)
!!{
- Computes the half-mass radius of a spherically symmetric mass distribution in a scaled spherical mass distribution.
+ Sample a position from a scaled spherical mass distribution.
!!}
implicit none
- double precision , dimension(3) :: sphericalScalerPositionSample
- class (massDistributionSphericalScaler), intent(inout) :: self
- class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision , dimension(3) :: sphericalScalerPositionSample
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ class (randomNumberGeneratorClass ), intent(inout) :: randomNumberGenerator_
- sphericalScalerPositionSample=+self%massDistribution_%positionSample (randomNumberGenerator_,componentType,massType) &
+ sphericalScalerPositionSample=+self%massDistribution_%positionSample (randomNumberGenerator_) &
& *self %factorScalingLength
return
end function sphericalScalerPositionSample
+
+ double precision function sphericalScalerFourierTransform(self,radiusOuter,wavenumber) result(fourierTransform)
+ !!{
+ Compute the Fourier transform of the density profile at the given {\normalfont \ttfamily wavenumber} in a spherical, scaled mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter , wavenumber
+
+ fourierTransform=self%massDistribution_%fourierTransform(radiusOuter/self%factorScalingLength,wavenumber*self%factorScalingLength)
+ return
+ end function sphericalScalerFourierTransform
+
+ double precision function sphericalScalerRadiusFreefall(self,time) result(radius)
+ !!{
+ Compute the freefall radius at the given {\normalfont \ttfamily time} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radius=+self%massDistribution_%radiusFreefall( &
+ & +time &
+ & *sqrt( &
+ & +self%factorScalingMass &
+ & /self%factorScalingLength**3 &
+ & ) &
+ & ) &
+ & * self%factorScalingLength
+ return
+ end function sphericalScalerRadiusFreefall
+
+ double precision function sphericalScalerRadiusFreefallIncreaseRate(self,time) result(radiusIncreaseRate)
+ !!{
+ Compute the rate of increase of the freefall radius at the given {\normalfont \ttfamily time} in an spherical mass
+ distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: time
+
+ radiusIncreaseRate=+self%massDistribution_%radiusFreefallIncreaseRate( &
+ & +time &
+ & *sqrt( &
+ & +self%factorScalingMass &
+ & /self%factorScalingLength**3 &
+ & ) &
+ & ) &
+ & * sqrt( &
+ & +self%factorScalingLength**5 &
+ & /self%factorScalingMass &
+ & )
+ return
+ end function sphericalScalerRadiusFreefallIncreaseRate
+
+ double precision function sphericalScalerEnergyPotential(self,radiusOuter) result(energy)
+ !!{
+ Compute the potential energy within a given {\normalfont \ttfamily radius} in a spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout) :: self
+ double precision , intent(in ) :: radiusOuter
+
+ energy =+self%massDistribution_%energyPotential(radiusOuter/self%factorScalingLength ) &
+ & * self%factorScalingMass **2 &
+ & / self%factorScalingLength
+ return
+ end function sphericalScalerEnergyPotential
+
+ double precision function sphericalScalerVelocityRotationCurveMaximum(self) result(velocity)
+ !!{
+ Return the peak velocity in the rotation curve for an spherical scaled mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalScaler), intent(inout) :: self
+
+ velocity=+self%massDistribution_%velocityRotationCurveMaximum() &
+ & *sqrt( &
+ & +self%factorScalingMass &
+ & /self%factorScalingLength &
+ & )
+ return
+ end function sphericalScalerVelocityRotationCurveMaximum
+
+ double precision function sphericalScalerRadiusRotationCurveMaximum(self) result(radius)
+ !!{
+ Return the peak velocity in the rotation curve for an spherical scaled mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalScaler), intent(inout), target :: self
+
+ radius=+self%massDistribution_%radiusRotationCurveMaximum() &
+ & *self%factorScalingLength
+ return
+ end function sphericalScalerRadiusRotationCurveMaximum
+
+ double precision function sphericalScalerRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for spherical scaled mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass, massFractional
+
+ if (present(massFractional)) then
+ if (massFractional <= 0.0d0) then
+ radius=+0.0d0
+ else
+ radius=+self%massDistribution_%radiusEnclosingMass(massFractional=massFractional )
+ end if
+ else if (present(mass)) then
+ if (mass <= 0.0d0) then
+ radius=+0.0d0
+ else
+ radius=+self%massDistribution_%radiusEnclosingMass(mass =mass *self%factorScalingMass)
+ end if
+ else
+ radius=+0.0d0
+ call Error_Report('either "mass" or "massFractional" must be provided'//{introspection:location})
+ end if
+ radius=+radius &
+ & *self%factorScalingLength
+ return
+ end function sphericalScalerRadiusEnclosingMass
+
+ double precision function sphericalScalerRadiusEnclosingDensity(self,density,radiusGuess) result(radius)
+ !!{
+ Computes the radius enclosing a given mean density for spherical scaled mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout), target :: self
+ double precision , intent(in ) :: density
+ double precision , intent(in ), optional :: radiusGuess
+
+ if (present(radiusGuess)) then
+ radius=+self%massDistribution_%radiusEnclosingDensity( &
+ & +density &
+ & *self%factorScalingLength**3 &
+ & /self%factorScalingMass , &
+ & +radiusGuess &
+ & /self%factorScalingLength &
+ & ) &
+ & * self%factorScalingLength
+ else
+ radius=+self%massDistribution_%radiusEnclosingDensity( &
+ & +density &
+ & *self%factorScalingLength**3 &
+ & /self%factorScalingMass &
+ & ) &
+ & * self%factorScalingLength
+ end if
+ return
+ end function sphericalScalerRadiusEnclosingDensity
+
+ double precision function sphericalScalerRadiusFromSpecificAngularMomentum(self,angularMomentumSpecific) result(radius)
+ !!{
+ Computes the radius corresponding to a given specific angular momentum for sphericalScaler mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalScaler), intent(inout), target :: self
+ double precision , intent(in ) :: angularMomentumSpecific
+
+ radius=+self%massDistribution_%radiusFromSpecificAngularMomentum( &
+ & +angularMomentumSpecific &
+ & /sqrt( &
+ & +self%factorScalingMass &
+ & *self%factorScalingLength &
+ & ) &
+ & ) &
+ & * self%factorScalingLength
+ return
+ end function sphericalScalerRadiusFromSpecificAngularMomentum
diff --git a/source/mass_distributions.spherical.truncated.F90 b/source/mass_distributions.spherical.truncated.F90
new file mode 100644
index 0000000000..fdd7605aa0
--- /dev/null
+++ b/source/mass_distributions.spherical.truncated.F90
@@ -0,0 +1,293 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+ !!{
+ Implements a truncated spherical mass distribution.
+ !!}
+
+ !![
+
+
+ Implements a mass distribution in which the density is given by
+ \begin{equation}
+ \rho(r) = \rho^\prime(r) \left\{ \begin{array}{ll} 1 & \hbox{ if } r < r_\mathrm{min}, \\ 0 & \hbox{ if } r > r_\mathrm{max}, \\ 1-3 x^2 + 2x^3 & \hbox{otherwise,} \end{array} \right.
+ \end{equation}
+ where
+ \begin{equation}
+ x=\frac{r-r_\mathrm{min}}{r_\mathrm{max}-r_\mathrm{min}},
+ \end{equation}
+ $\rho^\prime(r)$ is some other density profile, $r_\mathrm{min}=${\normalfont \ttfamily [radiusTruncateMinimum]}, and $r_\mathrm{max}=${\normalfont \ttfamily [radiusTruncateMaximum]}.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalTruncated
+ !!{
+ Implementation of a truncated spherical mass distribution.
+ !!}
+ private
+ double precision :: radiusTruncateMinimum, radiusTruncateMaximum, &
+ & massAtTruncation , massTotal_
+ contains
+ !![
+
+
+
+ !!]
+ final :: sphericalTruncatedDestructor
+ procedure :: density => sphericalTruncatedDensity
+ procedure :: densityGradientRadial => sphericalTruncatedDensityGradientRadial
+ procedure :: massTotal => sphericalTruncatedMassTotal
+ procedure :: massEnclosedBySphere => sphericalTruncatedMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => sphericalTruncatedRadiusEnclosingMass
+ procedure :: truncationFunction => sphericalTruncatedTruncationFunction
+ end type massDistributionSphericalTruncated
+
+ interface massDistributionSphericalTruncated
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalTruncated} mass distribution class.
+ !!}
+ module procedure sphericalTruncatedConstructorParameters
+ module procedure sphericalTruncatedConstructorInternal
+ end interface massDistributionSphericalTruncated
+
+contains
+
+ function sphericalTruncatedConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalTruncated} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalTruncated) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (varying_string ) :: nonAnalyticSolver
+ double precision :: radiusTruncateMinimum, radiusTruncateMaximum
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ radiusTruncateMinimum
+
+ The minimum radius to begin truncating the density profile.
+
+
+ radiusTruncateMaximum
+
+ The maximum radius to finish truncating the density profile.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalTruncated(radiusTruncateMinimum,radiusTruncateMaximum,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ return
+ end function sphericalTruncatedConstructorParameters
+
+ function sphericalTruncatedConstructorInternal(radiusTruncateMinimum,radiusTruncateMaximum,nonAnalyticSolver,massDistribution_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalTruncated'' mass distribution class.
+ !!}
+ implicit none
+ type (massDistributionSphericalTruncated) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ double precision , intent(in ) :: radiusTruncateMinimum, radiusTruncateMaximum
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ !![
+
+ !!]
+
+ self%massAtTruncation=self%massDistribution_%massEnclosedBySphere(radiusTruncateMinimum)
+ self%massTotal_ =self %massEnclosedBySphere(radiusTruncateMaximum)
+ self%dimensionless =self%massDistribution_%isDimensionless ( )
+ return
+ end function sphericalTruncatedConstructorInternal
+
+ subroutine sphericalTruncatedDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalTruncated} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalTruncated), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine sphericalTruncatedDestructor
+
+ subroutine sphericalTruncatedTruncationFunction(self,radius,x,multiplier,multiplierGradient)
+ !!{
+ Return the scaled truncation radial coordinate, and the truncation multiplier.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncated), intent(inout) :: self
+ double precision , intent(in ) :: radius
+ double precision , intent( out), optional :: x , multiplier, &
+ & multiplierGradient
+ double precision :: x_
+
+ if (radius <= self%radiusTruncateMinimum) then
+ if (present(x )) x =+0.0d0
+ if (present(multiplier )) multiplier =+1.0d0
+ if (present(multiplierGradient)) multiplierGradient=+0.0d0
+ else if (radius >= self%radiusTruncateMaximum) then
+ if (present(x )) x =+1.0d0
+ if (present(multiplier )) multiplier =+0.0d0
+ if (present(multiplierGradient)) multiplierGradient=+0.0d0
+ else
+ x_ =+(+ radius -self%radiusTruncateMinimum) &
+ & /(+self%radiusTruncateMaximum-self%radiusTruncateMinimum)
+ if (present(x )) x =+x_
+ if (present(multiplier )) multiplier = +1.0d0 &
+ & -3.0d0*x_**2 &
+ & +2.0d0*x_**3
+ if (present(multiplierGradient)) multiplierGradient=+( &
+ & -6.0d0*x_ &
+ & +6.0d0*x_**2 &
+ & ) &
+ & /(+self%radiusTruncateMaximum-self%radiusTruncateMinimum)
+ end if
+ return
+ end subroutine sphericalTruncatedTruncationFunction
+
+ double precision function sphericalTruncatedDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a scaled spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncated), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ double precision :: multiplier
+
+ call self%truncationFunction(radius=coordinates%rSpherical(),multiplier=multiplier)
+ density=+self%massDistribution_%density(coordinates) &
+ & * multiplier
+ return
+ end function sphericalTruncatedDensity
+
+ double precision function sphericalTruncatedDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in a truncated spherical mass distribution.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncated), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: multiplier , multiplierGradient
+ !![
+
+ !!]
+
+ call self%truncationFunction(radius=coordinates%rSpherical(),multiplier=multiplier,multiplierGradient=multiplierGradient)
+ if (multiplier > 0.0d0) then
+ densityGradient=+self%massDistribution_%densityGradientRadial(coordinates,logarithmic=.false.) &
+ & * multiplier &
+ & +self%massDistribution_%density (coordinates ) &
+ & * multiplierGradient
+ if (logarithmic_) densityGradient=+ densityGradient &
+ & *coordinates%rSpherical ( ) &
+ & /self %density (coordinates)
+ else
+ densityGradient=+0.0d0
+ end if
+ return
+ end function sphericalTruncatedDensityGradientRadial
+
+ double precision function sphericalTruncatedMassTotal(self) result(mass)
+ !!{
+ Return the total mass in a truncated mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalTruncated), intent(inout) :: self
+
+ mass=self%massTotal_
+ return
+ end function sphericalTruncatedMassTotal
+
+ double precision function sphericalTruncatedMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for truncated mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncated), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ if (radius <= self%radiusTruncateMinimum) then
+ mass=self%massDistribution_%massEnclosedBySphere (radius)
+ else
+ mass=self %massEnclosedBySphereNonAnalytic(radius)
+ end if
+ return
+ end function sphericalTruncatedMassEnclosedBySphere
+
+ double precision function sphericalTruncatedRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for truncated spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncated), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision :: mass_
+
+ if (present(mass)) then
+ mass_ =+ mass
+ else if (present(massFractional)) then
+ mass_ =+ massFractional &
+ & *self%massTotal ()
+ else
+ mass_ =+0.0d0
+ call Error_Report('either `mass` or `massFractional` must be provided'//{introspection:location})
+ end if
+ if (mass_ <= self%massAtTruncation) then
+ radius=self%massDistribution_%radiusEnclosingMass (mass=mass_)
+ else
+ radius=self %radiusEnclosingMassNonAnalytic(mass=mass_)
+ end if
+ return
+ end function sphericalTruncatedRadiusEnclosingMass
diff --git a/source/mass_distributions.spherical_shell_overdensities.F90 b/source/mass_distributions.spherical_shell_overdensities.F90
index 92d36a4d5e..9ba1dd1bc9 100644
--- a/source/mass_distributions.spherical_shell_overdensities.F90
+++ b/source/mass_distributions.spherical_shell_overdensities.F90
@@ -238,25 +238,19 @@ subroutine sphericalShellOverdensitiesDestructor(self)
return
end subroutine sphericalShellOverdensitiesDestructor
- double precision function sphericalShellOverdensitiesDensity(self,coordinates,componentType,massType)
+ double precision function sphericalShellOverdensitiesDensity(self,coordinates)
!!{
Return the density at the specified {\normalfont \ttfamily coordinates} in a cloud overdensities mass distribution.
!!}
use :: Arrays_Search, only : searchArrayClosest
use :: Coordinates , only : assignment(=) , coordinateSpherical
implicit none
- class (massDistributionSphericalShellOverdensities), intent(inout) :: self
- class (coordinate ), intent(in ) :: coordinates
- type (enumerationComponentTypeType ), intent(in ), optional :: componentType
- type (enumerationMassTypeType ), intent(in ), optional :: massType
- integer (c_size_t ) :: i
- type (coordinateSpherical ) :: position
- double precision :: densityContrast, radius
+ class (massDistributionSphericalShellOverdensities), intent(inout) :: self
+ class (coordinate ), intent(in ) :: coordinates
+ integer (c_size_t ) :: i
+ type (coordinateSpherical ) :: position
+ double precision :: densityContrast, radius
- if (.not.self%matches(componentType,massType)) then
- sphericalShellOverdensitiesDensity=0.0d0
- return
- end if
! Extract the position.
position=coordinates
radius =position %r()
diff --git a/source/mass_distributions.truncated.exponential.F90 b/source/mass_distributions.truncated.exponential.F90
new file mode 100644
index 0000000000..61dfef3d4b
--- /dev/null
+++ b/source/mass_distributions.truncated.exponential.F90
@@ -0,0 +1,327 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023
+!! 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 .
+
+!+ Contributions to this file made by: Xiaolong Du, Andrew Benson.
+
+ !!{
+ Implements an exponentially truncated spherical mass distribution \cite{kazantzidis_2006}.
+ !!}
+
+ !![
+
+
+ Implements an exponentially truncated mass distribution \cite{kazantzidis_2006} in which the density is given by
+ \begin{equation}
+ \rho(r) = \rho^\prime(r_\mathrm{min}) \left\{ \begin{array}{ll} 1 & \hbox{ if } r < r_\mathrm{min}, \\ \rho^\prime(r_\mathrm{min} x^\kappa \exp\left(-\frac{x-1}{x_\mathrm{max}}\right) & \hbox{otherwise,} \end{array} \right.
+ \end{equation}
+ where $x = r/r_\mathrm{min}$, $x_\mathrm{decay} = r_\mathrm{decay}/r_\mathrm{min}$, $\rho^\prime(r)$ is some other density
+ profile, $r_\mathrm{min}=${\normalfont \ttfamily [radiusTruncateMinimum]}, $r_\mathrm{decay}=${\normalfont \ttfamily
+ [radiusTruncateDecay]}, and
+ \begin{equation}
+ \kappa = \frac{r_\mathrm{min}}{r_\mathrm{decay}} + \frac{\mathrm{d}\log \rho^\prime}{\mathrm{d}\log r}(r_\mathrm{min})
+ \end{equation}
+ is chosen to ensure that the logarithmic gradient of the density profile is continuous across $r=r_\mathrm{min}$.
+
+
+ !!]
+ type, extends(massDistributionSphericalDecorator) :: massDistributionSphericalTruncatedExponential
+ !!{
+ Implementation of an exponentially-truncated spherical mass distribution.
+ !!}
+ private
+ double precision :: radiusTruncateMinimum , radiusTruncateDecay , &
+ & massAtTruncation , massTotal_ , &
+ & densityAtTruncation , kappa , &
+ & massEnclosedExponentialTerm, massEnclosedGammaFunctionTerm
+ contains
+ final :: sphericalTruncatedExponentialDestructor
+ procedure :: density => sphericalTruncatedExponentialDensity
+ procedure :: densityGradientRadial => sphericalTruncatedExponentialDensityGradientRadial
+ procedure :: massTotal => sphericalTruncatedExponentialMassTotal
+ procedure :: massEnclosedBySphere => sphericalTruncatedExponentialMassEnclosedBySphere
+ procedure :: radiusEnclosingMass => sphericalTruncatedExponentialRadiusEnclosingMass
+ end type massDistributionSphericalTruncatedExponential
+
+ interface massDistributionSphericalTruncatedExponential
+ !!{
+ Constructors for the {\normalfont \ttfamily sphericalTruncatedExponential} mass distribution class.
+ !!}
+ module procedure sphericalTruncatedExponentialConstructorParameters
+ module procedure sphericalTruncatedExponentialConstructorInternal
+ end interface massDistributionSphericalTruncatedExponential
+
+contains
+
+ function sphericalTruncatedExponentialConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily sphericalTruncatedExponential} mass distribution class which builds the object from a parameter
+ set.
+ !!}
+ use :: Input_Parameters , only : inputParameters
+ use :: Galactic_Structure_Options, only : enumerationComponentTypeEncode, enumerationMassTypeEncode
+ implicit none
+ type (massDistributionSphericalTruncatedExponential) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (varying_string ) :: nonAnalyticSolver
+ double precision :: radiusTruncateMinimum, radiusTruncateDecay
+ type (varying_string ) :: componentType , massType
+
+ !![
+
+ radiusTruncateMinimum
+
+ The minimum radius to begin truncating the density profile.
+
+
+ radiusTruncateDecay
+
+ The exponential decay scale for truncating the density profile.
+
+
+ nonAnalyticSolver
+ var_str('fallThrough')
+
+ Selects how solutions are computed when no analytic solution is available.
+
+
+ componentType
+ var_str('unknown')
+ The component type that this mass distribution represents.
+
+
+
+ massType
+ var_str('unknown')
+ The mass type that this mass distribution represents.
+
+
+
+ !!]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ self=massDistributionSphericalTruncatedExponential(radiusTruncateMinimum,radiusTruncateDecay,enumerationNonAnalyticSolversEncode(char(nonAnalyticSolver),includesPrefix=.false.),massDistribution_,enumerationComponentTypeEncode(componentType,includesPrefix=.false.),enumerationMassTypeEncode(massType,includesPrefix=.false.))
+ class default
+ call Error_Report('a spherically-symmetric mass distribution is required'//{introspection:location})
+ end select
+ !![
+
+
+ !!]
+ return
+ end function sphericalTruncatedExponentialConstructorParameters
+
+ function sphericalTruncatedExponentialConstructorInternal(radiusTruncateMinimum,radiusTruncateDecay,nonAnalyticSolver,massDistribution_,componentType,massType) result(self)
+ !!{
+ Constructor for ``sphericalTruncatedExponential'' mass distribution class.
+ !!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Gamma_Functions, only : Gamma_Function_Incomplete_Unnormalized
+ implicit none
+ type (massDistributionSphericalTruncatedExponential) :: self
+ class (massDistributionSpherical ), intent(in ), target :: massDistribution_
+ type (enumerationNonAnalyticSolversType ), intent(in ) :: nonAnalyticSolver
+ double precision , intent(in ) :: radiusTruncateMinimum , radiusTruncateDecay
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ double precision , parameter :: fractionRadialDecayMaximum=50.0d0
+ type (coordinateSpherical ) :: coordinatesTruncateMinimum
+ !![
+
+ !!]
+
+ coordinatesTruncateMinimum =[self%radiusTruncateMinimum,0.0d0,0.0d0]
+ self%kappa =+self%radiusTruncateMinimum &
+ & /self%radiusTruncateDecay &
+ & +self%massDistribution_%densityGradientRadial( &
+ & coordinatesTruncateMinimum , &
+ & logarithmic=.true. &
+ & )
+ self%massEnclosedExponentialTerm =+exp ( self%radiusTruncateMinimum/self%radiusTruncateDecay) &
+ & / ( self%radiusTruncateMinimum/self%radiusTruncateDecay)**self%kappa
+ self%massEnclosedGammaFunctionTerm=+Gamma_Function_Incomplete_Unnormalized(3.0d0+self%kappa,self%radiusTruncateMinimum/self%radiusTruncateDecay)
+ self%densityAtTruncation =+self%massDistribution_%density ( &
+ & coordinatesTruncateMinimum &
+ & )
+ self%massAtTruncation =+self%massDistribution_%massEnclosedBySphere ( &
+ & +self%radiusTruncateMinimum &
+ & )
+ self%massTotal_ =+self %massEnclosedBySphere ( &
+ & +self%radiusTruncateMinimum &
+ & + fractionRadialDecayMaximum &
+ & *self%radiusTruncateDecay &
+ & )
+ self%dimensionless = self%massDistribution_%isDimensionless ( &
+ & )
+ self%componentType = self%massDistribution_%componentType
+ self%massType = self%massDistribution_%massType
+ return
+ end function sphericalTruncatedExponentialConstructorInternal
+
+ subroutine sphericalTruncatedExponentialDestructor(self)
+ !!{
+ Destructor for the abstract {\normalfont \ttfamily massDistributionSphericalTruncatedExponential} class.
+ !!}
+ implicit none
+ type(massDistributionSphericalTruncatedExponential), intent(inout) :: self
+
+ !![
+
+ !!]
+ return
+ end subroutine sphericalTruncatedExponentialDestructor
+
+ double precision function sphericalTruncatedExponentialDensity(self,coordinates) result(density)
+ !!{
+ Return the density at the specified {\normalfont \ttfamily coordinates} in an exponentially-truncated spherical mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalTruncatedExponential), intent(inout) :: self
+ class(coordinate ), intent(in ) :: coordinates
+
+ if (coordinates%rSpherical() <= self%radiusTruncateMinimum) then
+ density=+self%massDistribution_%density(coordinates)
+ else
+ density=+self%densityAtTruncation &
+ & *( &
+ & +coordinates%rSpherical () &
+ & /self %radiusTruncateMinimum &
+ & )**self%kappa &
+ & *exp( &
+ & -( &
+ & +coordinates%rSpherical () &
+ & -self %radiusTruncateMinimum &
+ & ) &
+ & / self %radiusTruncateDecay &
+ & )
+ end if
+ return
+ end function sphericalTruncatedExponentialDensity
+
+ double precision function sphericalTruncatedExponentialDensityGradientRadial(self,coordinates,logarithmic) result(densityGradient)
+ !!{
+ Return the density gradient at the specified {\normalfont \ttfamily coordinates} in an exponentially-truncated spherical mass distribution.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+ class (massDistributionSphericalTruncatedExponential), intent(inout), target :: self
+ class (coordinate ), intent(in ) :: coordinates
+ logical , intent(in ), optional :: logarithmic
+ double precision :: density
+ !![
+
+ !!]
+
+ if (coordinates%rSpherical() <= self%radiusTruncateMinimum) then
+ densityGradient=+self%massDistribution_%densityGradientRadial(coordinates,logarithmic)
+ else
+ densityGradient=+self%densityAtTruncation &
+ & *( &
+ & +coordinates%rSpherical () &
+ & /self %radiusTruncateMinimum &
+ & )**self%kappa &
+ & *exp( &
+ & -( &
+ & +coordinates%rSpherical () &
+ & -self %radiusTruncateMinimum &
+ & ) &
+ & / self %radiusTruncateDecay &
+ & ) &
+ & *( &
+ & +self %kappa &
+ & -coordinates%rSpherical () &
+ & /self %radiusTruncateDecay &
+ & ) &
+ & / coordinates%rSpherical ()
+ if (logarithmic_) then
+ density=self%density(coordinates)
+ if (density > 0.0d0) then
+ densityGradient=+ densityGradient &
+ & *coordinates%rSpherical ( ) &
+ & /self %density (coordinates)
+ else if (densityGradient /= 0.0d0) then
+ call Error_Report('density is zero, but gradient is non-zero - logarithmic gradient is undefined'//{introspection:location})
+ end if
+ end if
+ end if
+ return
+ end function sphericalTruncatedExponentialDensityGradientRadial
+
+ double precision function sphericalTruncatedExponentialMassTotal(self) result(mass)
+ !!{
+ Return the total mass in a truncated mass distribution.
+ !!}
+ implicit none
+ class(massDistributionSphericalTruncatedExponential), intent(inout) :: self
+
+ mass=self%massTotal_
+ return
+ end function sphericalTruncatedExponentialMassTotal
+
+ double precision function sphericalTruncatedExponentialMassEnclosedBySphere(self,radius) result(mass)
+ !!{
+ Computes the mass enclosed within a sphere of given {\normalfont \ttfamily radius} for truncatedExponential mass distributions.
+ !!}
+ use :: Gamma_Functions, only : Gamma_Function_Incomplete_Unnormalized
+ implicit none
+ class (massDistributionSphericalTruncatedExponential), intent(inout), target :: self
+ double precision , intent(in ) :: radius
+
+ if (radius <= self%radiusTruncateMinimum) then
+ mass =+self%massDistribution_%massEnclosedBySphere(radius)
+ else
+ mass =+self%massAtTruncation &
+ & +4.0d0 &
+ & *Pi &
+ & *self%densityAtTruncation &
+ & *self%radiusTruncateDecay**3 &
+ & *self%massEnclosedExponentialTerm &
+ & *( &
+ & +self%massEnclosedGammaFunctionTerm &
+ & -Gamma_Function_Incomplete_Unnormalized(3.0d0+self%kappa,radius/self%radiusTruncateDecay) &
+ & )
+ end if
+ return
+ end function sphericalTruncatedExponentialMassEnclosedBySphere
+
+ double precision function sphericalTruncatedExponentialRadiusEnclosingMass(self,mass,massFractional) result(radius)
+ !!{
+ Computes the radius enclosing a given mass or mass fraction for truncatedExponential spherical mass distributions.
+ !!}
+ implicit none
+ class (massDistributionSphericalTruncatedExponential), intent(inout), target :: self
+ double precision , intent(in ), optional :: mass , massFractional
+ double precision :: mass_
+
+ if (present(mass)) then
+ mass_ =+ mass
+ else if (present(massFractional)) then
+ mass_ =+ massFractional &
+ & *self%massTotal ()
+ else
+ mass_ =+0.0d0
+ call Error_Report('either `mass` or `massFractional` must be provided'//{introspection:location})
+ end if
+ if (mass_ <= self%massAtTruncation) then
+ radius=self%massDistribution_%radiusEnclosingMass (mass=mass_)
+ else
+ radius=self %radiusEnclosingMassNonAnalytic(mass=mass_)
+ end if
+ return
+ end function sphericalTruncatedExponentialRadiusEnclosingMass
diff --git a/source/math.dilogarithm.F90 b/source/math.dilogarithm.F90
index 94f573fb28..750439bf5d 100644
--- a/source/math.dilogarithm.F90
+++ b/source/math.dilogarithm.F90
@@ -28,7 +28,8 @@ module Dilogarithms
!!{
Implements dilogarithms.
!!}
- use, intrinsic :: ISO_C_Binding, only : c_double
+ use, intrinsic :: ISO_C_Binding, only : c_double, c_int
+ use :: Interface_GSL, only : gsl_sf_result
implicit none
private
public :: Dilogarithm
@@ -42,19 +43,51 @@ function gsl_sf_dilog(x) bind(c,name='gsl_sf_dilog')
real(c_double) :: gsl_sf_dilog
real(c_double), value :: x
end function gsl_sf_dilog
+
+ function gsl_sf_complex_dilog_e(x,y,a,b) bind(c,name='gsl_sf_complex_dilog_e')
+ !!{
+ Template for the GSL complex dilogarithm C function.
+ !!}
+ import
+ integer(c_int ) :: gsl_sf_complex_dilog_e
+ real (c_double ), value :: x , y
+ type (gsl_sf_result) :: a , b
+ end function gsl_sf_complex_dilog_e
end interface
+ interface Dilogarithm
+ module procedure Dilogarithm_Real
+ module procedure Dilogarithm_Complex
+ end interface Dilogarithm
+
contains
- double precision function Dilogarithm(x)
+ double precision function Dilogarithm_Real(x)
!!{
Evaluate the $\hbox{Si}(x)\equiv\int_0^x \d t \sin(t)/t$ sine integral.
!!}
implicit none
double precision, intent(in ) :: x
- Dilogarithm=GSL_SF_Dilog(x)
+ Dilogarithm_Real=GSL_SF_Dilog(x)
+ return
+ end function Dilogarithm_Real
+
+ double complex function Dilogarithm_Complex(x)
+ !!{
+ Evaluate the dilogarithm for complex argument.
+ !!}
+ implicit none
+ double complex , intent(in ) :: x
+ type (gsl_sf_result) :: a , b
+ real (c_double ) :: r , theta
+ integer (c_int ) :: status
+
+ r =sqrt (imag(x)**2+real(x)**2)
+ theta =atan2(imag(x) ,real(x) )
+ status =GSL_SF_Complex_Dilog_E(r,theta,a,b)
+ Dilogarithm_Complex=dcmplx(a%val,b%val)
return
- end function Dilogarithm
+ end function Dilogarithm_Complex
end module Dilogarithms
diff --git a/source/math.gamma_function.F90 b/source/math.gamma_function.F90
index 5f4c973360..8fc929fa7d 100644
--- a/source/math.gamma_function.F90
+++ b/source/math.gamma_function.F90
@@ -114,25 +114,25 @@ double precision function Gamma_Function_Incomplete_Complementary(exponent,argum
return
end function Gamma_Function_Incomplete_Complementary
- double precision function Gamma_Function(argument)
+ double precision function Gamma_Function(exponent)
!!{
Computes the Gamma function.
!!}
implicit none
- double precision, intent(in ) :: argument
+ double precision, intent(in ) :: exponent
- Gamma_Function=GSL_SF_Gamma(argument)
+ Gamma_Function=GSL_SF_Gamma(exponent)
return
end function Gamma_Function
- double precision function Gamma_Function_Logarithmic(argument)
+ double precision function Gamma_Function_Logarithmic(exponent)
!!{
Computes the logarithm of the Gamma function.
!!}
implicit none
- double precision, intent(in ) :: argument
+ double precision, intent(in ) :: exponent
- Gamma_Function_Logarithmic=GSL_SF_lnGamma(argument)
+ Gamma_Function_Logarithmic=GSL_SF_lnGamma(exponent)
return
end function Gamma_Function_Logarithmic
diff --git a/source/math.hypergeometric_functions.F90 b/source/math.hypergeometric_functions.F90
index c5d7f9ddc3..43195b7564 100644
--- a/source/math.hypergeometric_functions.F90
+++ b/source/math.hypergeometric_functions.F90
@@ -35,7 +35,8 @@ module Hypergeometric_Functions
use :: Interface_GSL, only : gsl_sf_result, gsl_success
implicit none
private
- public :: Hypergeometric_1F1, Hypergeometric_2F1, Hypergeometric_pFq, Hypergeometric_pFq_Regularized
+ public :: Hypergeometric_1F1 , Hypergeometric_2F1, Hypergeometric_pFq, Hypergeometric_pFq_Regularized, &
+ & Hypergeometric_2F1_Regularized
interface Hypergeometric_pFq
module procedure :: Hypergeometric_pFq_Real
@@ -222,6 +223,20 @@ double precision function Hypergeometric_2F1(a,b,x,status,error,toleranceRelativ
return
end function Hypergeometric_2F1
+ double precision function Hypergeometric_2F1_Regularized(a,b,x)
+ !!{
+ Evaluate the regularized generalized hypergeometric function
+ $_2F_1(a_1,a_2;b_1;x)/\Gamma(b_1)$ for real arguments.
+ !!}
+ implicit none
+ double precision, intent(in ), dimension(2) :: a
+ double precision, intent(in ), dimension(1) :: b
+ double precision, intent(in ) :: x
+
+ Hypergeometric_2F1_Regularized=Hypergeometric_2F1(a,b,x)/Gamma(b(1))
+ return
+ end function Hypergeometric_2F1_Regularized
+
double complex function Hypergeometric_pFq_Complex(a,b,x,toleranceRelative)
!!{
Evaluate the generalized hypergeometric function $_pF_q(a_1,\ldots,a_p;b_1,\ldots,b_q;x)$, using the algorithm of
diff --git a/source/merger_trees.construct.build.masses.fixed_mass.F90 b/source/merger_trees.construct.build.masses.fixed_mass.F90
index e64c9c3882..e5ec6737c2 100644
--- a/source/merger_trees.construct.build.masses.fixed_mass.F90
+++ b/source/merger_trees.construct.build.masses.fixed_mass.F90
@@ -22,7 +22,6 @@
!!}
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Nodes_Operators , only : nodeOperatorClass
use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
@@ -39,7 +38,6 @@
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (nodeOperatorClass ), pointer :: nodeOperator_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (randomNumberGeneratorClass), pointer :: randomNumberGenerator_ => null()
double precision , allocatable, dimension(:) :: massTree , radiusTree
integer , allocatable, dimension(:) :: treeCount
@@ -72,7 +70,6 @@ function fixedMassConstructorParameters(parameters) result(self)
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (nodeOperatorClass ), pointer :: nodeOperator_
class (randomNumberGeneratorClass ), pointer :: randomNumberGenerator_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: massIntervalFractional
integer :: fixedHalosCount
@@ -146,21 +143,19 @@ function fixedMassConstructorParameters(parameters) result(self)
-
!!]
- self=mergerTreeBuildMassesFixedMass(massTree,radiusTree,treeCount,massIntervalFractional,cosmologyParameters_,darkMatterHaloScale_,nodeOperator_,randomNumberGenerator_,galacticStructure_)
+ self=mergerTreeBuildMassesFixedMass(massTree,radiusTree,treeCount,massIntervalFractional,cosmologyParameters_,darkMatterHaloScale_,nodeOperator_,randomNumberGenerator_)
!![
-
!!]
return
end function fixedMassConstructorParameters
- function fixedMassConstructorInternal(massTree,radiusTree,treeCount,massIntervalFractional,cosmologyParameters_,darkMatterHaloScale_,nodeOperator_,randomNumberGenerator_,galacticStructure_) result(self)
+ function fixedMassConstructorInternal(massTree,radiusTree,treeCount,massIntervalFractional,cosmologyParameters_,darkMatterHaloScale_,nodeOperator_,randomNumberGenerator_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily fixedMass} merger tree masses class.
!!}
@@ -173,9 +168,8 @@ function fixedMassConstructorInternal(massTree,radiusTree,treeCount,massInterval
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (nodeOperatorClass ), intent(in ), target :: nodeOperator_
class (randomNumberGeneratorClass ), intent(in ), target :: randomNumberGenerator_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -193,7 +187,6 @@ subroutine fixedMassDestructor(self)
-
!!]
return
end subroutine fixedMassDestructor
@@ -286,24 +279,26 @@ double precision function massEnclosed(massTree)
Root finding function used to set the halo mass given the halo radius.
!!}
use :: Galactic_Structure_Options, only : massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- double precision, intent(in ) :: massTree
+ double precision , intent(in ) :: massTree
+ class (massDistributionClass), pointer :: massDistribution_
call basic %massSet (massTree)
call self %nodeOperator_%nodeTreeInitialize(node )
call self %nodeOperator_%nodeInitialize (node )
call Calculations_Reset(node)
- massEnclosed=+ self%galacticStructure_ %massEnclosed( &
- & node , &
- & self%radiusTree(i), &
- & massType=massTypeDark &
- & ) &
- & * self%cosmologyParameters_%OmegaMatter() &
- & /( &
- & +self%cosmologyParameters_%OmegaMatter() &
- & -self%cosmologyParameters_%OmegaBaryon() &
- & ) &
- & -self%massTree(i)
+ massDistribution_ => node %massDistribution (massType= massTypeDark )
+ massEnclosed = + massDistribution_ %massEnclosedBySphere( self%radiusTree (i)) &
+ & * self %cosmologyParameters_%OmegaMatter() &
+ & /( &
+ & +self %cosmologyParameters_%OmegaMatter() &
+ & -self %cosmologyParameters_%OmegaBaryon() &
+ & ) &
+ & - self%massTree (i)
+ !![
+
+ !!]
return
end function massEnclosed
diff --git a/source/merger_trees.construct.read.F90 b/source/merger_trees.construct.read.F90
index 0f2458e8c0..602f393417 100644
--- a/source/merger_trees.construct.read.F90
+++ b/source/merger_trees.construct.read.F90
@@ -2207,15 +2207,17 @@ subroutine readAssignAngularMomenta(self,nodes,nodeList)
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentSpin, treeNodeList
use :: Merger_Tree_Read_Importers, only : nodeData
- implicit none
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
class (mergerTreeConstructorRead) , intent(inout) :: self
class (nodeData ) , dimension(:), intent(inout) :: nodes
type (treeNodeList ) , dimension(:), intent(inout) :: nodeList
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSpin ), pointer :: spin
+ class (massDistributionClass ), pointer :: massDistribution_
integer :: iNode
integer (c_size_t ) :: iIsolatedNode
- double precision :: angularMomentum
+ double precision :: angularMomentum , radiusVirial
double precision , dimension(3) :: angularMomentum3D
do iNode=1,size(nodes)
@@ -2223,8 +2225,10 @@ subroutine readAssignAngularMomenta(self,nodes,nodeList)
if (nodes(iNode)%isolatedNodeIndex /= nodeReachabilityUnreachable%ID) then
iIsolatedNode=nodes(iNode)%isolatedNodeIndex
! Get basic and spin components.
- basic => nodeList(iIsolatedNode)%node%basic( )
- spin => nodeList(iIsolatedNode)%node%spin (autoCreate=.true.)
+ basic => nodeList(iIsolatedNode)%node%basic( )
+ spin => nodeList(iIsolatedNode)%node%spin (autoCreate=.true.)
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial(nodeList(iIsolatedNode)%node )
+ massDistribution_ => self%darkMatterProfileDMO_%get (nodeList(iIsolatedNode)%node )
if (self%presetAngularMomenta ) then
if (self%mergerTreeImporter_%angularMomentaAvailable()) then
! If angular momenta are available directly, use them.
@@ -2251,6 +2255,9 @@ subroutine readAssignAngularMomenta(self,nodes,nodeList)
call Error_Report('no method exists to set vector angular momenta'//{introspection:location})
end if
end if
+ !![
+
+ !!]
end if
end do
return
@@ -2263,9 +2270,9 @@ double precision function spinNormalization()
!!}
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- spinNormalization=+gravitationalConstantGalacticus &
- & *basic%mass()**2.5d0 &
- & /sqrt(abs(self%darkMatterProfileDMO_%energy(nodeList(iIsolatedNode)%node)))
+ spinNormalization=+gravitationalConstantGalacticus &
+ & *basic%mass()**2.5d0 &
+ & /sqrt(abs(massDistribution_%energy(radiusVirial,massDistribution_)))
return
end function spinNormalization
@@ -2310,13 +2317,21 @@ double precision function readRadiusHalfMassRoot(radius)
!!{
Function used to find scale radius of dark matter halos given their half-mass radius.
!!}
+ use :: Calculations_Resets, only : Calculations_Reset
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- double precision, intent(in ) :: radius
+ double precision , intent(in ) :: radius
+ class (massDistributionClass), pointer :: massDistribution_
! Set scale radius to current guess.
call darkMatterProfile_%scaleSet(radius)
+ call Calculations_Reset(node_)
! Compute difference between mass fraction enclosed at half mass radius and one half.
- readRadiusHalfMassRoot=self_%darkMatterProfileDMO_%enclosedMass(node_,radiusHalfMass_)/basic_%mass()-0.50d0
+ massDistribution_ => self_ %darkMatterProfileDMO_%get(node_ )
+ readRadiusHalfMassRoot = massDistribution_%massEnclosedBySphere (radiusHalfMass_)/basic_%mass()-0.5d0
+ !![
+
+ !!]
return
end function readRadiusHalfMassRoot
@@ -3363,6 +3378,7 @@ subroutine readTimeUntilMergingSubresolution(self,lastSeenNode,nodes,nodeList,iN
use :: Merger_Tree_Read_Importers, only : nodeData
use :: String_Handling , only : operator(//)
use :: Vectors , only : Vector_Magnitude
+ use :: Calculations_Resets , only : Calculations_Reset
implicit none
class (mergerTreeConstructorRead) , intent(inout) :: self
class (nodeData ) , intent(in ) :: lastSeenNode
@@ -3457,6 +3473,10 @@ subroutine readTimeUntilMergingSubresolution(self,lastSeenNode,nodes,nodeList,iN
end if
satelliteNode%parent => hostNode
hostNode %firstSatellite => satelliteNode
+ ! Perform a calculation reset as technically these nodes have changed. (Specifically, they may have the same unique
+ ! ID as the prior time this function was called, and yet be new copies. Not resetting calculations could result in
+ ! the old - now destroyed - copies of these nodes being accessed.)
+ call Calculations_Reset(satelliteNode)
! Determine the time until merging.
timeUntilMerging=self%satelliteMergingTimescales_%timeUntilMerging(satelliteNode,orbit)
! Clean up.
diff --git a/source/merger_trees.evolve.timesteps.history.F90 b/source/merger_trees.evolve.timesteps.history.F90
index 359572e913..f1a0e9ddb8 100644
--- a/source/merger_trees.evolve.timesteps.history.F90
+++ b/source/merger_trees.evolve.timesteps.history.F90
@@ -22,7 +22,6 @@
!!}
use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Numerical_Interpolation , only : interpolator
use :: Star_Formation_Rates_Disks , only : starFormationRateDisksClass
use :: Star_Formation_Rates_Spheroids, only : starFormationRateSpheroidsClass
@@ -67,7 +66,6 @@ function of time. Timesteps are enforced such that:
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: historyCount
double precision :: timeBegin , timeEnd
double precision , allocatable, dimension(:) :: rateStarFormationDisk , densityStellarDisk , &
@@ -104,7 +102,6 @@ function historyConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_
- class (galacticStructureClass ), pointer :: galacticStructure_
integer :: historyCount
double precision :: timeBegin , timeEnd, &
& ageUniverse
@@ -113,7 +110,6 @@ function historyConstructorParameters(parameters) result(self)
-
!!]
ageUniverse=cosmologyFunctions_%cosmicTime(1.0d0)
!![
@@ -136,18 +132,17 @@ function historyConstructorParameters(parameters) result(self)
!!]
- self=mergerTreeEvolveTimestepHistory(historyCount,timeBegin,timeEnd,cosmologyFunctions_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=mergerTreeEvolveTimestepHistory(historyCount,timeBegin,timeEnd,cosmologyFunctions_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
-
!!]
return
end function historyConstructorParameters
- function historyConstructorInternal(historyCount,timeBegin,timeEnd,cosmologyFunctions_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function historyConstructorInternal(historyCount,timeBegin,timeEnd,cosmologyFunctions_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Constructor for the {\normalfont \ttfamily history} merger tree evolution timestep class which takes a parameter set as input.
!!}
@@ -160,10 +155,9 @@ function historyConstructorInternal(historyCount,timeBegin,timeEnd,cosmologyFunc
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer (c_size_t ) :: timeIndex
!![
-
+
!!]
! Allocate storage arrays.
@@ -221,7 +215,6 @@ subroutine historyDestructor(self)
-
!!]
return
end subroutine historyDestructor
@@ -271,11 +264,12 @@ subroutine historyStore(self,tree,node,deadlockStatus)
!!{
Store various properties in global arrays.
!!}
- use :: Galactic_Structure_Options, only : componentTypeDisk, componentTypeHotHalo, componentTypeSpheroid, massTypeGaseous , &
+ use :: Galactic_Structure_Options, only : componentTypeDisk , componentTypeHotHalo, componentTypeSpheroid, massTypeGaseous , &
& massTypeStellar
use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : mergerTree , nodeComponentBasic , nodeComponentDisk , nodeComponentSpheroid, &
- & treeNode
+ use :: Galacticus_Nodes , only : mergerTree , nodeComponentBasic , nodeComponentDisk , nodeComponentSpheroid, &
+ & treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use, intrinsic :: ISO_C_Binding , only : c_size_t
implicit none
class (* ), intent(inout) :: self
@@ -285,9 +279,12 @@ subroutine historyStore(self,tree,node,deadlockStatus)
class (nodeComponentBasic ) , pointer :: basic
class (nodeComponentDisk ) , pointer :: disk
class (nodeComponentSpheroid ) , pointer :: spheroid
+ class (massDistributionClass ) , pointer :: massDistributionHotHalo_ , massDistributionGaseous_ , &
+ & massDistributionStellarDisk_, massDistributionStellarSpheroid_, &
+ & massDistributionStellar_
integer (c_size_t ) :: timeIndex
- double precision :: rateStarFormationDisk , massHotGas, &
- & rateStarFormationSpheroid, time
+ double precision :: rateStarFormationDisk , massHotGas, &
+ & rateStarFormationSpheroid , time
!$GLC attributes unused :: deadlockStatus
select type (self)
@@ -309,42 +306,57 @@ subroutine historyStore(self,tree,node,deadlockStatus)
rateStarFormationSpheroid=self%starFormationRateSpheroids_%rate(node)
! Accumulate the properties.
! Star formation rate:
- self%rateStarFormation (timeIndex)=+ self %rateStarFormation (timeIndex ) &
- & +(+rateStarFormationDisk+rateStarFormationSpheroid) &
- & * tree %volumeWeight
- self%rateStarFormationDisk (timeIndex)=+ self %rateStarFormationDisk (timeIndex ) &
- & + rateStarFormationDisk &
- & * tree %volumeWeight
- self%rateStarFormationSpheroid (timeIndex)=+ self %rateStarFormationSpheroid(timeIndex ) &
- & + rateStarFormationSpheroid &
- & * tree %volumeWeight
+ self%rateStarFormation (timeIndex) = + self %rateStarFormation (timeIndex ) &
+ & +(+rateStarFormationDisk+rateStarFormationSpheroid) &
+ & * tree %volumeWeight
+ self%rateStarFormationDisk (timeIndex) = + self %rateStarFormationDisk (timeIndex ) &
+ & + rateStarFormationDisk &
+ & * tree %volumeWeight
+ self%rateStarFormationSpheroid (timeIndex) = + self %rateStarFormationSpheroid(timeIndex ) &
+ & + rateStarFormationSpheroid &
+ & * tree %volumeWeight
! Stellar densities.
- self%densityStellar (timeIndex)=+ self %densityStellar (timeIndex ) &
- & + self%galacticStructure_%massEnclosed (node, massType=massTypeStellar) &
- & * tree %volumeWeight
- self%densityStellarDisk (timeIndex)=+ self %densityStellarDisk (timeIndex ) &
- & + self%galacticStructure_%massEnclosed (node,componentType=componentTypeDisk ,massType=massTypeStellar) &
- & * tree %volumeWeight
- self%densityStellarSpheroid (timeIndex)=+ self %densityStellarSpheroid (timeIndex ) &
- & + self%galacticStructure_%massEnclosed (node,componentType=componentTypeSpheroid,massType=massTypeStellar) &
- & * tree %volumeWeight
-
+ massDistributionStellar_ => node %massDistribution ( massType=massTypeStellar)
+ massDistributionStellarDisk_ => node %massDistribution (componentType=componentTypeDisk ,massType=massTypeStellar)
+ massDistributionStellarSpheroid_ => node %massDistribution (componentType=componentTypeSpheroid,massType=massTypeStellar)
+ self%densityStellar (timeIndex) = + self %densityStellar (timeIndex ) &
+ & + massDistributionStellar_ %massTotal ( ) &
+ & * tree %volumeWeight
+ self%densityStellarDisk (timeIndex) = + self %densityStellarDisk (timeIndex ) &
+ & + massDistributionStellarDisk_ %massTotal ( ) &
+ & * tree %volumeWeight
+ self%densityStellarSpheroid (timeIndex) = + self %densityStellarSpheroid(timeIndex ) &
+ & + massDistributionStellarSpheroid_%massTotal ( ) &
+ & * tree %volumeWeight
+ !![
+
+
+
+ !!]
! Hot gas density.
- massHotGas =+ self%galacticStructure_%massEnclosed (node,componentType=componentTypeHotHalo )
- self%densityHotHaloGas (timeIndex)=+ self %densityHotHaloGas (timeIndex ) &
- & +massHotGas &
- & * tree %volumeWeight
+ massDistributionHotHalo_ => node %massDistribution (componentType=componentTypeHotHalo )
+ massHotGas = + massDistributionHotHalo_ %massTotal ( )
+ self%densityHotHaloGas (timeIndex) = + self %densityHotHaloGas (timeIndex ) &
+ & + massHotGas &
+ & * tree %volumeWeight
+ !![
+
+ !!]
! Galactic gas density.
- self%densityColdGas (timeIndex)=+ self %densityColdGas (timeIndex ) &
- & +( &
- & +self%galacticStructure_%massEnclosed (node,massType=massTypeGaseous ) &
- & -massHotGas &
- & ) &
- & *tree %volumeWeight
+ massDistributionGaseous_ => node %massDistribution ( massType=massTypeGaseous)
+ self%densityColdGas (timeIndex) = + self %densityColdGas (timeIndex ) &
+ & +( &
+ & +massDistributionGaseous_ %massTotal ( ) &
+ & -massHotGas &
+ & ) &
+ & * tree %volumeWeight
+ !![
+
+ !!]
! Node density
- if (.not.node%isSatellite()) self%densityNode(timeIndex)=+ self %densityNode (timeIndex ) &
- & + basic %mass ( ) &
- & * tree %volumeWeight
+ if (.not.node%isSatellite()) self%densityNode(timeIndex)=+ self %densityNode (timeIndex ) &
+ & + basic %mass ( ) &
+ & * tree %volumeWeight
class default
call Error_Report('incorrect class'//{introspection:location})
end select
diff --git a/source/merger_trees.evolve.timesteps.record_evolution.F90 b/source/merger_trees.evolve.timesteps.record_evolution.F90
index ea97fc416d..10fd25f4c1 100644
--- a/source/merger_trees.evolve.timesteps.record_evolution.F90
+++ b/source/merger_trees.evolve.timesteps.record_evolution.F90
@@ -23,7 +23,6 @@
!!}
use :: Cosmology_Functions , only : cosmologyFunctions, cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Numerical_Interpolation, only : interpolator
use :: Output_Times , only : outputTimes , outputTimesClass
@@ -58,7 +57,6 @@
private
class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
logical :: oneTimeDatasetsWritten
integer :: countSteps
double precision :: timeBegin , timeEnd
@@ -97,7 +95,6 @@ function recordEvolutionConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: timeBegin , timeEnd, &
& ageUniverse
integer :: countSteps
@@ -105,7 +102,6 @@ function recordEvolutionConstructorParameters(parameters) result(self)
!![
-
!!]
ageUniverse=cosmologyFunctions_%cosmicTime(1.0d0)
!![
@@ -128,17 +124,16 @@ function recordEvolutionConstructorParameters(parameters) result(self)
!!]
- self=mergerTreeEvolveTimestepRecordEvolution(timeBegin,timeEnd,countSteps,cosmologyFunctions_,outputTimes_,galacticStructure_)
+ self=mergerTreeEvolveTimestepRecordEvolution(timeBegin,timeEnd,countSteps,cosmologyFunctions_,outputTimes_)
!![
-
!!]
return
end function recordEvolutionConstructorParameters
- function recordEvolutionConstructorInternal(timeBegin,timeEnd,countSteps,cosmologyFunctions_,outputTimes_,galacticStructure_) result(self)
+ function recordEvolutionConstructorInternal(timeBegin,timeEnd,countSteps,cosmologyFunctions_,outputTimes_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily recordEvolution} merger tree evolution timestep class.
!!}
@@ -148,12 +143,11 @@ function recordEvolutionConstructorInternal(timeBegin,timeEnd,countSteps,cosmolo
type (mergerTreeEvolveTimestepRecordEvolution) :: self
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(in ), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: timeBegin , timeEnd
integer , intent(in ) :: countSteps
integer (c_size_t ) :: timeIndex
!![
-
+
!!]
allocate(self%time (self%countSteps))
@@ -193,7 +187,6 @@ subroutine recordEvolutionDestructor(self)
!![
-
!!]
if (mergerTreeExtraOutputEvent%isAttached(self,recordEvolutionOutput)) call mergerTreeExtraOutputEvent%detach(self,recordEvolutionOutput)
return
@@ -246,9 +239,10 @@ subroutine recordEvolutionStore(self,tree,node,deadlockStatus)
!!{
Store properties of the main progenitor galaxy.
!!}
- use :: Galactic_Structure_Options, only : massTypeGalactic, massTypeStellar
+ use :: Galactic_Structure_Options, only : massTypeGalactic , massTypeStellar
use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : mergerTree , nodeComponentBasic, treeNode
+ use :: Galacticus_Nodes , only : mergerTree , nodeComponentBasic, treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use, intrinsic :: ISO_C_Binding , only : c_size_t
implicit none
class (* ), intent(inout) :: self
@@ -256,6 +250,7 @@ subroutine recordEvolutionStore(self,tree,node,deadlockStatus)
type (treeNode ), intent(inout), pointer :: node
type (enumerationDeadlockStatusType), intent(inout) :: deadlockStatus
class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass ) , pointer :: massDistributionGalactic, massDistributionStellar
integer (c_size_t ) :: indexTime
double precision :: time
!$GLC attributes unused :: deadlockStatus, tree
@@ -269,8 +264,14 @@ subroutine recordEvolutionStore(self,tree,node,deadlockStatus)
else
indexTime=self%interpolator_%locate(time)
end if
- self%massStellar(indexTime)=self%galacticStructure_%massEnclosed(node,massType=massTypeStellar )
- self%massTotal (indexTime)=self%galacticStructure_%massEnclosed(node,massType=massTypeGalactic)
+ massDistributionStellar => node %massDistribution(massType=massTypeStellar )
+ massDistributionGalactic => node %massDistribution(massType=massTypeGalactic)
+ self%massStellar (indexTime) = massDistributionStellar %massTotal ( )
+ self%massTotal (indexTime) = massDistributionGalactic%massTotal ( )
+ !![
+
+
+ !!]
class default
call Error_Report('incorrect class'//{introspection:location})
end select
diff --git a/source/merger_trees.evolver.standard.F90 b/source/merger_trees.evolver.standard.F90
index ed1eb4007e..97a85cfbce 100644
--- a/source/merger_trees.evolver.standard.F90
+++ b/source/merger_trees.evolver.standard.F90
@@ -29,7 +29,6 @@
use :: Merger_Tree_Initialization , only : mergerTreeInitializorClass
use :: Merger_Tree_Timesteps , only : mergerTreeEvolveTimestep , mergerTreeEvolveTimestepClass
use :: Merger_Trees_Evolve_Node , only : mergerTreeNodeEvolver , mergerTreeNodeEvolverClass
- use :: Galactic_Structure , only : galacticStructureClass
! Structure used to store list of nodes for deadlock reporting.
type :: deadlockList
@@ -122,7 +121,6 @@
class (galacticStructureSolverClass ), pointer :: galacticStructureSolver_ => null()
class (mergerTreeNodeEvolverClass ), pointer :: mergerTreeNodeEvolver_ => null()
class (mergerTreeInitializorClass ), pointer :: mergerTreeInitializor_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (mergerTreeEvolveProfilerClass), pointer :: mergerTreeEvolveProfiler_ => null()
logical :: allTreesExistAtFinalTime , dumpTreeStructure , &
& backtrackToSatellites , profileSteps
@@ -171,7 +169,6 @@ function standardConstructorParameters(parameters) result(self)
class (galacticStructureSolverClass ), pointer :: galacticStructureSolver_
class (mergerTreeNodeEvolverClass ), pointer :: mergerTreeNodeEvolver_
class (mergerTreeInitializorClass ), pointer :: mergerTreeInitializor_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (mergerTreeEvolveProfilerClass), pointer :: mergerTreeEvolveProfiler_
logical :: allTreesExistAtFinalTime , dumpTreeStructure , &
& backtrackToSatellites , profileSteps
@@ -222,19 +219,14 @@ function standardConstructorParameters(parameters) result(self)
Specifies whether or not to profile the ODE evolver.
- !!]
- ! A galacticStructureSolver is built here. Even though this is not called explicitly by this mergerTreeEvolver, the
- ! galacticStructureSolver is expected to hook itself to any events which will trigger a change in galactic structure.
- !![
-
!!]
- self=mergerTreeEvolverStandard(allTreesExistAtFinalTime,dumpTreeStructure,timestepHostRelative,timestepHostAbsolute,fractionTimestepSatelliteMinimum,backtrackToSatellites,profileSteps,cosmologyFunctions_,mergerTreeNodeEvolver_,mergerTreeEvolveTimestep_,mergerTreeInitializor_,galacticStructureSolver_,galacticStructure_,mergerTreeEvolveProfiler_)
+ self=mergerTreeEvolverStandard(allTreesExistAtFinalTime,dumpTreeStructure,timestepHostRelative,timestepHostAbsolute,fractionTimestepSatelliteMinimum,backtrackToSatellites,profileSteps,cosmologyFunctions_,mergerTreeNodeEvolver_,mergerTreeEvolveTimestep_,mergerTreeInitializor_,galacticStructureSolver_,mergerTreeEvolveProfiler_)
!![
@@ -242,13 +234,12 @@ function standardConstructorParameters(parameters) result(self)
-
!!]
return
end function standardConstructorParameters
- function standardConstructorInternal(allTreesExistAtFinalTime,dumpTreeStructure,timestepHostRelative,timestepHostAbsolute,fractionTimestepSatelliteMinimum,backtrackToSatellites,profileSteps,cosmologyFunctions_,mergerTreeNodeEvolver_,mergerTreeEvolveTimestep_,mergerTreeInitializor_,galacticStructureSolver_,galacticStructure_,mergerTreeEvolveProfiler_) result(self)
+ function standardConstructorInternal(allTreesExistAtFinalTime,dumpTreeStructure,timestepHostRelative,timestepHostAbsolute,fractionTimestepSatelliteMinimum,backtrackToSatellites,profileSteps,cosmologyFunctions_,mergerTreeNodeEvolver_,mergerTreeEvolveTimestep_,mergerTreeInitializor_,galacticStructureSolver_,mergerTreeEvolveProfiler_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily standard} merger tree evolver class.
!!}
@@ -259,14 +250,13 @@ function standardConstructorInternal(allTreesExistAtFinalTime,dumpTreeStructure,
class (galacticStructureSolverClass ), intent(in ), target :: galacticStructureSolver_
class (mergerTreeNodeEvolverClass ), intent(in ), target :: mergerTreeNodeEvolver_
class (mergerTreeInitializorClass ), intent(in ), target :: mergerTreeInitializor_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (mergerTreeEvolveProfilerClass), intent(in ), target :: mergerTreeEvolveProfiler_
logical , intent(in ) :: allTreesExistAtFinalTime , dumpTreeStructure , &
& backtrackToSatellites , profileSteps
double precision , intent(in ) :: timestepHostRelative , timestepHostAbsolute, &
& fractionTimestepSatelliteMinimum
!![
-
+
!!]
self%deadlockHeadNode => null()
@@ -286,7 +276,6 @@ subroutine standardDestructor(self)
-
!!]
return
diff --git a/source/merger_trees.node_evolver.standard.F90 b/source/merger_trees.node_evolver.standard.F90
index d855a86b60..c810dd1259 100644
--- a/source/merger_trees.node_evolver.standard.F90
+++ b/source/merger_trees.node_evolver.standard.F90
@@ -959,11 +959,11 @@ Function which evaluates the set of ODEs for the evolution of a specific node.
propertyValues1 =+propertyValues0
propertyValues1(i)=+propertyValues1 (i) &
& +propertyValueDelta
- call self_%activeNode%deserializeValues (propertyValues1 ,self_%propertyTypeODE)
- call self_%activeNode%odeStepRatesInitialize( )
- call self_%galacticStructureSolver_%revert (self_%activeNode )
- call standardDerivativesCompute (self_%activeNode ,interrupt,functionInterrupt,self_%propertyTypeODE)
- call self_%activeNode%serializeRates (propertyRates1 ,self_%propertyTypeODE)
+ call self_%activeNode%deserializeValues (propertyValues1 ,self_%propertyTypeODE)
+ call self_%activeNode%odeStepRatesInitialize( )
+ call self_%galacticStructureSolver_%revert (self_%activeNode )
+ call standardDerivativesCompute (self_%activeNode,interrupt,functionInterrupt,self_%propertyTypeODE)
+ call self_%activeNode%serializeRates (propertyRates1 ,self_%propertyTypeODE)
jacobian(i,:)=+( &
& +propertyRates1 &
& -propertyRates0 &
diff --git a/source/merger_trees.operators.mass_accretion_history.F90 b/source/merger_trees.operators.mass_accretion_history.F90
index 14b03ecb8d..f5903a99cd 100644
--- a/source/merger_trees.operators.mass_accretion_history.F90
+++ b/source/merger_trees.operators.mass_accretion_history.F90
@@ -22,9 +22,9 @@
histories.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: IO_HDF5 , only : hdf5Object
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: IO_HDF5 , only : hdf5Object
!![
@@ -48,9 +48,9 @@ as a function of time). Histories are written into the \glc\ output file in a gr
private
type (hdf5Object ) :: outputGroup
type (varying_string ) :: outputGroupName
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- logical :: includeSpin , includeSpinVector
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ logical :: includeSpin , includeSpinVector
contains
final :: massAccretionHistoryDestructor
procedure :: operatePreEvolution => massAccretionHistoryOperatePreEvolution
@@ -73,12 +73,12 @@ function massAccretionHistoryConstructorParameters(parameters) result(self)
parameter set as input.
!!}
implicit none
- type (mergerTreeOperatorMassAccretionHistory) :: self
- type (inputParameters ), intent(inout) :: parameters
- type (varying_string ) :: outputGroupName
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- logical :: includeSpin , includeSpinVector
+ type (mergerTreeOperatorMassAccretionHistory) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ type (varying_string ) :: outputGroupName
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ logical :: includeSpin , includeSpinVector
!![
@@ -100,18 +100,18 @@ function massAccretionHistoryConstructorParameters(parameters) result(self)
If true, include the spin vector of the halo in the output.
-
+
!!]
- self=mergerTreeOperatorMassAccretionHistory(char(outputGroupName),includeSpin,includeSpinVector,cosmologyFunctions_,darkMatterProfileDMO_)
+ self=mergerTreeOperatorMassAccretionHistory(char(outputGroupName),includeSpin,includeSpinVector,cosmologyFunctions_,darkMatterHaloScale_)
!![
-
-
+
+
!!]
return
end function massAccretionHistoryConstructorParameters
- function massAccretionHistoryConstructorInternal(outputGroupName,includeSpin,includeSpinVector,cosmologyFunctions_,darkMatterProfileDMO_) result(self)
+ function massAccretionHistoryConstructorInternal(outputGroupName,includeSpin,includeSpinVector,cosmologyFunctions_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the mass accretion history merger tree operator class.
!!}
@@ -120,11 +120,11 @@ function massAccretionHistoryConstructorInternal(outputGroupName,includeSpin,inc
implicit none
type (mergerTreeOperatorMassAccretionHistory) :: self
character(len=* ), intent(in ) :: outputGroupName
- logical , intent(in ) :: includeSpin , includeSpinVector
+ logical , intent(in ) :: includeSpin , includeSpinVector
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
if (self%includeSpin .and..not.defaultSpinComponent%angularMomentumIsGettable ()) &
@@ -158,8 +158,8 @@ Destructor for the mass accretion history merger tree operator function class.
type(mergerTreeOperatorMassAccretionHistory), intent(inout) :: self
!![
-
-
+
+
!!]
return
end subroutine massAccretionHistoryDestructor
@@ -226,10 +226,10 @@ subroutine massAccretionHistoryOperatePreEvolution(self,tree)
nodeTime (accretionHistoryCount ) = basic%time ( )
nodeMass (accretionHistoryCount ) = basic%mass ( )
nodeExpansionFactor (accretionHistoryCount ) = self%cosmologyFunctions_%expansionFactor(basic%time ( ))
- if (self%includeSpin ) nodeSpin (accretionHistoryCount ) = spin %angularMomentum ( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
- if (self%includeSpinVector) nodeSpinVector(accretionHistoryCount,:) = spin %angularMomentumVector( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ if (self%includeSpin ) nodeSpin (accretionHistoryCount ) = spin %angularMomentum ( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
+ if (self%includeSpinVector) nodeSpinVector(accretionHistoryCount,:) = spin %angularMomentumVector( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
node => node %firstChild
end do
! Create the output group if necessary.
diff --git a/source/merger_trees.operators.particulate.F90 b/source/merger_trees.operators.particulate.F90
index 953ce35958..cfa48c9963 100644
--- a/source/merger_trees.operators.particulate.F90
+++ b/source/merger_trees.operators.particulate.F90
@@ -27,7 +27,6 @@
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Galacticus_Nodes , only : treeNode
use :: HDF5 , only : hsize_t
use :: ISO_Varying_String , only : varying_string
@@ -75,12 +74,11 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (varying_string ) :: outputFileName
double precision :: massParticle , radiusTruncateOverRadiusVirial , &
& timeSnapshot , energyDistributionPointsPerDecade, &
& lengthSoftening , toleranceRelativeSmoothing , &
- & toleranceMass
+ & toleranceMass , tolerancePotential
logical :: satelliteOffset , nonCosmological , &
& positionOffset , addHubbleFlow , &
& haloIdToParticleType , sampleParticleNumber , &
@@ -138,7 +136,7 @@ function particulateConstructorParameters(parameters) result(self)
double precision :: massParticle , radiusTruncateOverRadiusVirial , &
& timeSnapshot , energyDistributionPointsPerDecade, &
& lengthSoftening , toleranceRelativeSmoothing , &
- & toleranceMass
+ & toleranceMass , tolerancePotential
logical :: satelliteOffset , nonCosmological , &
& positionOffset , addHubbleFlow , &
& haloIdToParticleType , sampleParticleNumber , &
@@ -150,7 +148,6 @@ function particulateConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
type (inputParameters ), pointer :: parametersRoot => null()
type (varying_string ) :: selection , kernelSoftening
@@ -217,6 +214,12 @@ function particulateConstructorParameters(parameters) result(self)
1.0d-8
The relative tolerance to use in the integrals over the mass distribution used in finding the smoothed density profile defined by \cite{barnes_gravitational_2012} to account for gravitational softening.
+
+ tolerancePotential
+
+ 1.0d-9
+ The relative tolerance to use in the integrals over the potential used in finding the smoothed density profile defined by \cite{barnes_gravitational_2012} to account for gravitational softening.
+
selection
@@ -274,16 +277,15 @@ function particulateConstructorParameters(parameters) result(self)
-
!!]
if (associated(parameters%parent)) then
parametersRoot => parameters%parent
do while (associated(parametersRoot%parent))
parametersRoot => parametersRoot%parent
end do
- self=mergerTreeOperatorParticulate(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection_,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening_,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_,parametersRoot)
+ self=mergerTreeOperatorParticulate(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection_,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening_,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,tolerancePotential,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,parametersRoot)
else
- self=mergerTreeOperatorParticulate(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection_,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening_,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_,parameters )
+ self=mergerTreeOperatorParticulate(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection_,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening_,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,tolerancePotential,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,parameters )
end if
!![
@@ -291,12 +293,11 @@ function particulateConstructorParameters(parameters) result(self)
-
!!]
return
end function particulateConstructorParameters
- function particulateConstructorInternal(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_,parameters) result(self)
+ function particulateConstructorInternal(outputFileName,idMultiplier,massParticle,radiusTruncateOverRadiusVirial,timeSnapshot,satelliteOffset,positionOffset,subtractRandomOffset,energyDistributionPointsPerDecade,selection,nonCosmological,addHubbleFlow,haloIdToParticleType,sampleParticleNumber,kernelSoftening,lengthSoftening,toleranceRelativeSmoothing,toleranceMass,tolerancePotential,chunkSize,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,parameters) result(self)
!!{
Internal constructor for the particulate merger tree operator class.
!!}
@@ -308,7 +309,7 @@ function particulateConstructorInternal(outputFileName,idMultiplier,massParticle
double precision , intent(in ) :: massParticle , radiusTruncateOverRadiusVirial , &
& timeSnapshot , energyDistributionPointsPerDecade, &
& lengthSoftening , toleranceRelativeSmoothing , &
- & toleranceMass
+ & toleranceMass , tolerancePotential
logical , intent(in ) :: satelliteOffset , nonCosmological , &
& positionOffset , addHubbleFlow , &
& haloIdToParticleType , sampleParticleNumber , &
@@ -320,10 +321,9 @@ function particulateConstructorInternal(outputFileName,idMultiplier,massParticle
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (inputParameters ), intent(in ), target :: parameters
!![
-
+
!!]
self%parameters=inputParameters(parameters)
@@ -344,7 +344,6 @@ subroutine particulateDestructor(self)
-
!!]
return
end subroutine particulateDestructor
@@ -365,6 +364,7 @@ subroutine particulateOperatePreEvolution(self,tree)
use :: HDF5_Access , only : hdf5Access
use :: IO_HDF5 , only : hdf5Object
use :: ISO_Varying_String , only : varying_string , var_str
+ use :: Mass_Distributions , only : massDistributionClass
use :: Merger_Tree_Walkers , only : mergerTreeWalkerAllNodes
use :: Node_Components , only : Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize
use :: Numerical_Comparison , only : Values_Agree
@@ -377,6 +377,7 @@ subroutine particulateOperatePreEvolution(self,tree)
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentPosition ), pointer :: position
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: tolerance =1.0d-06
double precision , parameter :: unitGadgetMass =1.0d+10
double precision , parameter :: unitGadgetLength =1.0d-03
@@ -477,11 +478,11 @@ subroutine particulateOperatePreEvolution(self,tree)
radiusTruncate=+self%radiusTruncateOverRadiusVirial &
& *radiusVirial
! Determine the mass within the truncation radius.
- massTruncate=self%galacticStructure_%massEnclosed( &
- & node , &
- & radiusTruncate , &
- & massType =massTypeDark &
- & )
+ massDistribution_ => node %massDistribution (massType=massTypeDark )
+ massTruncate = massDistribution_%massEnclosedBySphere( radiusTruncate)
+ !![
+
+ !!]
! Determine the mean number of particles required to represent this node.
particleCountMean =+massTruncate &
& /self%massParticle
@@ -541,16 +542,9 @@ subroutine particulateOperatePreEvolution(self,tree)
end do
end do
!$omp end critical (mergerTreeOperatorParticulateSample)
- call positionSpherical% phiSet( 2.0d0*Pi*randomDeviates(1) )
- call positionSpherical%thetaSet(acos(2.0d0 *randomDeviates(2)-1.0d0))
- call positionSpherical% rSet( &
- & self_%galacticStructure_%radiusEnclosingMass( &
- & node , &
- & mass =+massTruncate &
- & *randomDeviates(3), &
- & massType =massTypeDark &
- & ) &
- & )
+ call positionSpherical% phiSet( 2.0d0*Pi *randomDeviates(1) )
+ call positionSpherical%thetaSet(acos(2.0d0 *randomDeviates(2)-1.0d0))
+ call positionSpherical% rSet( radiusTruncate_*randomDeviates(3) )
! Get the corresponding cartesian coordinates.
positionCartesian=positionSpherical
! Construct the energy distribution function encompassing this radius.
@@ -560,9 +554,12 @@ subroutine particulateOperatePreEvolution(self,tree)
& positionSpherical%r() , &
& table=energyDistributionTablePotential &
& )
- speedEscape =+sqrt( &
- & +2.0d0 &
- & *energyPotential &
+ speedEscape =+sqrt( &
+ & +2.0d0 &
+ & *max( &
+ & energyPotential, &
+ & 0.0d0 &
+ & ) &
& )
! Estimate the maximum of the speed distribution function.
distributionFunctionMaximum=+0.0d0
@@ -771,38 +768,42 @@ Construct the energy distribution function assuming a spherical dark matter halo
\end{equation}
which we can then take the derivative of numerically to obtain the distribution function.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Integration, only : integrator
use :: Table_Labels , only : extrapolationTypeFix
implicit none
- double precision , intent(in ) :: radius
- double precision , intent(in ) :: energyDistributionPointsPerDecade
- class (nodeComponentBasic), pointer :: basic
- double precision , parameter :: toleranceTabulation =1.0d-6
- double precision , parameter :: toleranceGradient =1.0d-6
+ double precision , intent(in ) :: radius
+ double precision , intent(in ) :: energyDistributionPointsPerDecade
+ class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision , parameter :: toleranceTabulation =1.0d-6
+ double precision , parameter :: toleranceGradient =1.0d-6
! The largest (absolute) logarithmic gradient dlog(Φ)/dlog(r) at which it is acceptable to have a non-monotonic distribution
! function. This allows for numerical inaccuracies that arise in cored density profiles where the central potential has the
! form Φ(r) = Φ₀ + k r², such that the potential is very weakly dependent on r at small radii.
- double precision , parameter :: derivativeLogarithmicPotentialTolerance =1.0d-5
- double precision :: radiusMinimum , energyPotentialTruncate , &
- & particulateSmoothingIntegrationRangeLower , particulateSmoothingIntegrationRangeUpper, &
- & radiusFactorAsymptote , integralAsymptotic , &
- & gradientDensityPotentialLower , gradientDensityPotentialUpper , &
- & densitySmoothedIntegralLower , densitySmoothedIntegralUpper , &
- & derivativeLogarithmicPotential
- logical :: tableRebuild
- integer :: i , j
- integer :: radiusCount
- type (integrator ) :: intergatorSmoothingZ , integratorMass , &
- & integratorPotential , integratorEddington
+ double precision , parameter :: derivativeLogarithmicPotentialTolerance =1.0d-5
+ double precision :: radiusMinimum , derivativeLogarithmicPotential , &
+ & particulateSmoothingIntegrationRangeLower , particulateSmoothingIntegrationRangeUpper, &
+ & radiusFactorAsymptote , integralAsymptotic , &
+ & gradientDensityPotentialLower , gradientDensityPotentialUpper , &
+ & densitySmoothedIntegralLower , densitySmoothedIntegralUpper
+ logical :: tableRebuild
+ integer :: i , j
+ integer :: radiusCount
+ type (coordinateSpherical ) :: coordinates , coordinatesTruncate
+ type (integrator ) :: intergatorSmoothingZ , integratorMass , &
+ & integratorPotential , integratorEddington
! Determine the minimum of the given radius and some small fraction of the virial radius.
- basic => node_%basic()
- radiusMinimum = min( &
- & +0.5d0* radius , &
- & + self_%darkMatterProfileDMO_%radiusEnclosingMass(node_ ,self_%massParticle) &
- & )
+ basic => node_%basic ()
+ massDistribution_ => self_ %darkMatterProfileDMO_%get (node_ )
+ radiusMinimum = min( &
+ & +0.5d0* radius , &
+ & + massDistribution_ %radiusEnclosingMass(self_%massParticle ) &
+ & )
! Rebuild the density vs. potential table to have sufficient range if necessary.
if (energyDistributionInitialized) then
tableRebuild=(radiusMinimum < (1.0d0-toleranceTabulation)*energyDistribution%x(1))
@@ -821,45 +822,30 @@ Construct the energy distribution function assuming a spherical dark matter halo
& tableCount = 5 , &
& extrapolationType= [extrapolationTypeFix,extrapolationTypeFix] &
& )
- select case (softeningKernel%ID)
- case (particulateKernelDelta%ID)
- energyPotentialTruncate=+self_%darkMatterProfileDMO_%potential( &
- & node_ , &
- & +radiusTruncate_ &
- & )
- case default
- ! Potential will be computed directly from the smoothed density profile in these cases.
- energyPotentialTruncate=0.0d0
- end select
do i=1,radiusCount
- radius_=energyDistribution%x(i)
- call energyDistribution%populate( &
- & +self_%darkMatterProfileDMO_%density ( &
- & node_ , &
- & energyDistribution%x(i) &
- & ) , &
- & i , &
- & table =energyDistributionTableDensity , &
- & computeSpline=i==radiusCount &
+ radius_ =energyDistribution%x(i)
+ coordinates=[radius_,0.0d0,0.0d0]
+ call energyDistribution%populate( &
+ & +massDistribution_%density (coordinates) , &
+ & i , &
+ & table =energyDistributionTableDensity , &
+ & computeSpline=i==radiusCount &
& )
select case (softeningKernel%ID)
case (particulateKernelDelta%ID)
! No softening is applied, so use the actual density and potential.
- call energyDistribution%populate( &
- & energyDistribution%y(i,table=energyDistributionTableDensity) , &
- & i , &
- & table =energyDistributionTableDensitySmoothed , &
- & computeSpline=i==radiusCount &
+ coordinatesTruncate=[radiusTruncate_,0.0d0,0.0d0]
+ call energyDistribution%populate( &
+ & energyDistribution%y(i,table=energyDistributionTableDensity) , &
+ & i , &
+ & table =energyDistributionTableDensitySmoothed , &
+ & computeSpline=i==radiusCount &
& )
- call energyDistribution%populate( &
- & -self_%darkMatterProfileDMO_%potential( &
- & node_ , &
- & energyDistribution%x(i) &
- & ) &
- & +energyPotentialTruncate , &
- & i , &
- & table =energyDistributionTablePotential , &
- & computeSpline=i==radiusCount &
+ call energyDistribution%populate( &
+ & massDistribution_%potentialDifference(coordinatesTruncate,coordinates), &
+ & i , &
+ & table =energyDistributionTablePotential , &
+ & computeSpline=i==radiusCount &
& )
case default
! Compute potential from a density field smoothed by the density distribution corresponding to the softened
@@ -905,6 +891,9 @@ Construct the energy distribution function assuming a spherical dark matter halo
& )
end select
end do
+ !![
+
+ !!]
! If necessary, compute the potential from the smoothed density profile.
if (softeningKernel /= particulateKernelDelta) then
integratorMass=integrator(particulateMassIntegrand,toleranceRelative=self_%toleranceMass)
@@ -929,7 +918,7 @@ Construct the energy distribution function assuming a spherical dark matter halo
& computeSpline=i==radiusCount &
& )
end do
- integratorPotential=integrator(particulatePotentialIntegrand,toleranceRelative=1.0d-9)
+ integratorPotential=integrator(particulatePotentialIntegrand,toleranceRelative=self_%tolerancePotential)
do i=1,radiusCount
radius_=energyDistribution%x(i)
call energyDistribution%populate( &
@@ -1077,21 +1066,31 @@ double precision function particulateSmoothingIntegrandR(radiusCylindrical)
The integrand over cylindrical coordinate $R$ used in finding the smoothed density profile defined by
\cite{barnes_gravitational_2012} to account for gravitational softening.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
- double precision, intent(in ) :: radiusCylindrical
- double precision :: radiusSplineKernel, lengthSplineKernel
-
- particulateSmoothingIntegrandR=+radiusCylindrical &
- & *self_%darkMatterProfileDMO_%density( &
- & node_ , &
- & sqrt( &
- & +radiusCylindrical**2 &
- & +( &
- & +height_ &
- & -radius_ &
- & ) **2 &
- & ) &
- & )
+ double precision , intent(in ) :: radiusCylindrical
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: radiusSplineKernel, lengthSplineKernel
+ type (coordinateSpherical ) :: coordinates
+
+ massDistribution_ => self_%darkMatterProfileDMO_%get(node_)
+ coordinates = [ &
+ & +sqrt( &
+ & +radiusCylindrical**2 &
+ & +( &
+ & +height_ &
+ & -radius_ &
+ & ) **2 &
+ & ) , &
+ & +0.0d0 , &
+ & +0.0d0 &
+ & ]
+ particulateSmoothingIntegrandR=+ radiusCylindrical &
+ & *massDistribution_%density (coordinates)
+ !![
+
+ !!]
! Apply the softening kernel density distribution.
select case (softeningKernel%ID)
case (particulateKernelPlummer%ID)
diff --git a/source/merger_trees.outputter.halo_Fourier_profiles.F90 b/source/merger_trees.outputter.halo_Fourier_profiles.F90
index 6f81e12e31..bed420edfc 100644
--- a/source/merger_trees.outputter.halo_Fourier_profiles.F90
+++ b/source/merger_trees.outputter.halo_Fourier_profiles.F90
@@ -22,6 +22,7 @@
!!}
use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
use :: Galactic_Filters , only : galacticFilterClass
use :: IO_HDF5 , only : hdf5Object
@@ -63,6 +64,7 @@
!!}
private
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
class (galacticFilterClass ), pointer :: galacticFilter_ => null()
integer :: wavenumberPointsPerDecade , wavenumberCount
@@ -96,6 +98,7 @@ function haloFourierProfilesConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (galacticFilterClass ), pointer :: galacticFilter_
double precision :: wavenumberMinimum , wavenumberMaximum
integer :: wavenumberPointsPerDecade
@@ -122,18 +125,20 @@ function haloFourierProfilesConstructorParameters(parameters) result(self)
+
!!]
- self=mergerTreeOutputterHaloFourierProfiles(wavenumberPointsPerDecade,wavenumberMinimum,wavenumberMaximum,cosmologyFunctions_,darkMatterProfileDMO_,galacticFilter_)
+ self=mergerTreeOutputterHaloFourierProfiles(wavenumberPointsPerDecade,wavenumberMinimum,wavenumberMaximum,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticFilter_)
!![
+
!!]
return
end function haloFourierProfilesConstructorParameters
- function haloFourierProfilesConstructorInternal(wavenumberPointsPerDecade,wavenumberMinimum,wavenumberMaximum,cosmologyFunctions_,darkMatterProfileDMO_,galacticFilter_) result(self)
+ function haloFourierProfilesConstructorInternal(wavenumberPointsPerDecade,wavenumberMinimum,wavenumberMaximum,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticFilter_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily haloFourierProfiles} merger tree outputter class.
!!}
@@ -141,12 +146,13 @@ function haloFourierProfilesConstructorInternal(wavenumberPointsPerDecade,wavenu
implicit none
type (mergerTreeOutputterHaloFourierProfiles) :: self
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (galacticFilterClass ), intent(in ), target :: galacticFilter_
double precision , intent(in ) :: wavenumberMinimum , wavenumberMaximum
integer , intent(in ) :: wavenumberPointsPerDecade
!![
-
+
!!]
! Build a grid of wavenumbers.
@@ -167,6 +173,7 @@ subroutine haloFourierProfilesDestructor(self)
!![
+
!!]
return
@@ -194,6 +201,7 @@ subroutine haloFourierProfilesOutputTree(self,tree,indexOutput,time)
use :: Galacticus_Nodes , only : treeNode , nodeComponentBasic
!$ use :: HDF5_Access , only : hdf5Access
use :: ISO_Varying_String , only : var_str
+ use :: Mass_Distributions , only : massDistributionClass
use :: Merger_Tree_Walkers , only : mergerTreeWalkerAllNodes
use :: Numerical_Constants_Astronomical, only : megaParsec
use :: String_Handling , only : operator(//)
@@ -204,12 +212,13 @@ subroutine haloFourierProfilesOutputTree(self,tree,indexOutput,time)
double precision , intent(in ) :: time
type (treeNode ) , pointer :: node
class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , allocatable , dimension(:) :: fourierProfile
type (mergerTreeWalkerAllNodes ) :: treeWalker
- type (hdf5Object ) :: outputGroup , treeGroup, &
+ type (hdf5Object ) :: outputGroup , treeGroup , &
& dataset
integer (c_size_t ) :: treeIndexPrevious
- double precision :: expansionFactor
+ double precision :: expansionFactor , radiusVirial
integer :: i
!$GLC attributes unused :: time
@@ -236,11 +245,16 @@ subroutine haloFourierProfilesOutputTree(self,tree,indexOutput,time)
end if
basic => node%basic ( )
expansionFactor = self%cosmologyFunctions_%expansionFactor(basic%time())
- ! Construct profile. (Our wavenumbers are comoving, so we must convert them to physical coordinates before passing them to
+ ! Construct profile. (Our wavenumbers are comoving, so we must convert them to physical coordinates before passing them to
! the dark matter profile k-space routine.)
+ massDistribution_ => self%darkMatterProfileDMO_%get (node)
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial(node)
do i=1,self%waveNumberCount
- fourierProfile(i)=self%darkMatterProfileDMO_%kSpace(node,self%wavenumber(i)/expansionFactor)
+ fourierProfile(i)=massDistribution_%fourierTransform(radiusVirial,self%wavenumber(i)/expansionFactor)
end do
+ !![
+
+ !!]
!$ call hdf5Access%set ()
call treeGroup%writeDataset(fourierProfile,char(var_str('node')//node%index()),"The Fourier-space density profile.")
!$ call hdf5Access%unset()
diff --git a/source/models.likelihoods.spin_distribution.F90 b/source/models.likelihoods.spin_distribution.F90
index 5389862813..5166b4e1d5 100644
--- a/source/models.likelihoods.spin_distribution.F90
+++ b/source/models.likelihoods.spin_distribution.F90
@@ -52,7 +52,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (haloMassFunctionClass ), pointer :: haloMassFunction_ => null()
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_ => null()
double precision , dimension(: ), allocatable :: spin , distribution , &
@@ -92,7 +91,6 @@ function spinDistributionConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (haloMassFunctionClass ), pointer :: haloMassFunction_
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_
type (varying_string ) :: fileName , distributionType
@@ -147,24 +145,22 @@ function spinDistributionConstructorParameters(parameters) result(self)
-
!!]
- self=posteriorSampleLikelihoodSpinDistribution(char(fileName),enumerationSpinDistributionTypeEncode(char(distributionType),includesPrefix=.false.),redshift,logNormalRange,massHaloMinimum,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,cosmologyFunctions_,haloMassFunction_,nbodyHaloMassError_,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileScaleRadius_)
+ self=posteriorSampleLikelihoodSpinDistribution(char(fileName),enumerationSpinDistributionTypeEncode(char(distributionType),includesPrefix=.false.),redshift,logNormalRange,massHaloMinimum,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,cosmologyFunctions_,haloMassFunction_,nbodyHaloMassError_,darkMatterHaloScale_,darkMatterProfileScaleRadius_)
!![
-
!!]
return
end function spinDistributionConstructorParameters
- function spinDistributionConstructorInternal(fileName,distributionType,redshift,logNormalRange,massHaloMinimum,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,cosmologyFunctions_,haloMassFunction_,nbodyHaloMassError_,darkMatterProfileDMO_,darkMatterHaloScale_,darkMatterProfileScaleRadius_) result(self)
+ function spinDistributionConstructorInternal(fileName,distributionType,redshift,logNormalRange,massHaloMinimum,massParticle,particleCountMinimum,energyEstimateParticleCountMaximum,cosmologyFunctions_,haloMassFunction_,nbodyHaloMassError_,darkMatterHaloScale_,darkMatterProfileScaleRadius_) result(self)
!!{
Constructor for ``spinDistribution'' posterior sampling likelihood class.
!!}
@@ -181,14 +177,13 @@ function spinDistributionConstructorInternal(fileName,distributionType,redshift,
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (haloMassFunctionClass ), intent(in ), target :: haloMassFunction_
class (nbodyHaloMassErrorClass ), intent(in ), target :: nbodyHaloMassError_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileScaleRadiusClass ), intent(in ), target :: darkMatterProfileScaleRadius_
type (hdf5Object ) :: spinDistributionFile
double precision :: spinIntervalLogarithmic
integer :: i
!![
-
+
!!]
! Convert redshift to time.
@@ -234,7 +229,6 @@ subroutine spinDistributionDestructor(self)
-
!!]
@@ -299,7 +293,7 @@ double precision function spinDistributionEvaluate(self,simulationState,modelPar
distributionLogNormal=haloSpinDistributionLogNormal ( &
& stateVector(1) , &
& stateVector(2) , &
- & self%darkMatterProfileDMO_ &
+ & self%darkMatterHaloScale_ &
& )
distributionNbody =haloSpinDistributionNbodyErrors( &
& distributionLogNormal , &
@@ -312,7 +306,6 @@ double precision function spinDistributionEvaluate(self,simulationState,modelPar
& self%cosmologyFunctions_ , &
& self%haloMassFunction_ , &
& self%darkMatterHaloScale_ , &
- & self%darkMatterProfileDMO_ , &
& self%darkMatterProfileScaleRadius_ &
& )
end select
@@ -328,7 +321,7 @@ double precision function spinDistributionEvaluate(self,simulationState,modelPar
distributionBett2007=haloSpinDistributionBett2007 ( &
& stateVector(1) , &
& stateVector(2) , &
- & self%darkMatterProfileDMO_ &
+ & self%darkMatterHaloScale_ &
& )
distributionNbody =haloSpinDistributionNbodyErrors( &
& distributionBett2007 , &
@@ -341,7 +334,6 @@ double precision function spinDistributionEvaluate(self,simulationState,modelPar
& self%cosmologyFunctions_ , &
& self%haloMassFunction_ , &
& self%darkMatterHaloScale_ , &
- & self%darkMatterProfileDMO_ , &
& self%darkMatterProfileScaleRadius_ &
& )
end select
@@ -379,7 +371,7 @@ Integrand function used to find cumulative spin distribution over a bin.
implicit none
double precision, intent(in ) :: spinPrime
- call nodeSpin%angularMomentumSet(spinPrime*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_))
+ call nodeSpin%angularMomentumSet(spinPrime*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_))
spinDistributionIntegrate=distributionNbody%distributionAveraged(node,self%massHaloMinimum)
return
end function spinDistributionIntegrate
diff --git a/source/nodes.operators.empirical.UniverseMachine.F90 b/source/nodes.operators.empirical.UniverseMachine.F90
index 695b3d906f..4b5a9a2e3d 100644
--- a/source/nodes.operators.empirical.UniverseMachine.F90
+++ b/source/nodes.operators.empirical.UniverseMachine.F90
@@ -25,10 +25,9 @@
assembly.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass, virialDensityContrastBryanNorman1998
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass, virialDensityContrastBryanNorman1998
!![
@@ -62,7 +61,6 @@
logical :: setFinalStellarMass , hasDisk , hasSpheroid
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
type (virialDensityContrastBryanNorman1998), pointer :: virialDensityContrastDefinition_ => null()
contains
@@ -110,7 +108,6 @@ function empiricalGalaxyUniverseMachineConstructorParameters(parameters) result(
& delta_0
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
!![
@@ -274,18 +271,17 @@ function empiricalGalaxyUniverseMachineConstructorParameters(parameters) result(
-
!!]
- self=nodeOperatorEmpiricalGalaxyUniverseMachine( &
- & massStellarFinal ,fractionMassSpheroid, fractionMassDisk , &
- & epsilon_0 ,epsilon_a ,epsilon_lna ,epsilon_z , &
- & M_0 ,M_a ,M_lna ,M_z , &
- & alpha_0 ,alpha_a ,alpha_lna ,alpha_z , &
- & beta_0 ,beta_a ,beta_z , &
- & gamma_0 ,gamma_a ,gamma_z , &
- & delta_0 , &
- & cosmologyParameters_,cosmologyFunctions_ ,darkMatterProfileDMO_,virialDensityContrast_ &
+ self=nodeOperatorEmpiricalGalaxyUniverseMachine( &
+ & massStellarFinal ,fractionMassSpheroid, fractionMassDisk , &
+ & epsilon_0 ,epsilon_a ,epsilon_lna ,epsilon_z, &
+ & M_0 ,M_a ,M_lna ,M_z , &
+ & alpha_0 ,alpha_a ,alpha_lna ,alpha_z , &
+ & beta_0 ,beta_a ,beta_z , &
+ & gamma_0 ,gamma_a ,gamma_z , &
+ & delta_0 , &
+ & cosmologyParameters_,cosmologyFunctions_ ,virialDensityContrast_ &
& )
!![
@@ -294,15 +290,15 @@ function empiricalGalaxyUniverseMachineConstructorParameters(parameters) result(
return
end function empiricalGalaxyUniverseMachineConstructorParameters
- function empiricalGalaxyUniverseMachineConstructorInternal( &
- & massStellarFinal ,fractionMassSpheroid,fractionMassDisk , &
- & epsilon_0 ,epsilon_a ,epsilon_lna ,epsilon_z , &
- & M_0 ,M_a ,M_lna ,M_z , &
- & alpha_0 ,alpha_a ,alpha_lna ,alpha_z , &
- & beta_0 ,beta_a ,beta_z , &
- & gamma_0 ,gamma_a ,gamma_z , &
- & delta_0 , &
- & cosmologyParameters_,cosmologyFunctions_ ,darkMatterProfileDMO_,virialDensityContrast_ &
+ function empiricalGalaxyUniverseMachineConstructorInternal( &
+ & massStellarFinal ,fractionMassSpheroid,fractionMassDisk , &
+ & epsilon_0 ,epsilon_a ,epsilon_lna ,epsilon_z, &
+ & M_0 ,M_a ,M_lna ,M_z , &
+ & alpha_0 ,alpha_a ,alpha_lna ,alpha_z , &
+ & beta_0 ,beta_a ,beta_z , &
+ & gamma_0 ,gamma_a ,gamma_z , &
+ & delta_0 , &
+ & cosmologyParameters_,cosmologyFunctions_ ,virialDensityContrast_ &
& ) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily empiricalGalaxyUniverseMachine} {\normalfont \ttfamily nodeOperator} class.
@@ -321,10 +317,9 @@ function empiricalGalaxyUniverseMachineConstructorInternal(
& gamma_z
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
!![
-
+
!!]
self%setFinalStellarMass=massStellarFinal >= 0.0d0
@@ -352,7 +347,6 @@ subroutine empiricalGalaxyUniverseMachineDestructor(self)
!![
-
!!]
@@ -458,7 +452,6 @@ subroutine empiricalGalaxyUniverseMachineUpdate(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massStellar = self %stellarMassHaloMassRelation (massHalo,redshift)
@@ -475,7 +468,6 @@ subroutine empiricalGalaxyUniverseMachineUpdate(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
redshiftRoot = self %cosmologyFunctions_%redshiftFromExpansionFactor( &
diff --git a/source/nodes.operators.physics.Bertschinger_mass.interpolate.F90 b/source/nodes.operators.physics.Bertschinger_mass.interpolate.F90
index aa885c81ce..09d4471dd0 100644
--- a/source/nodes.operators.physics.Bertschinger_mass.interpolate.F90
+++ b/source/nodes.operators.physics.Bertschinger_mass.interpolate.F90
@@ -41,7 +41,6 @@
private
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
integer :: massBertschingerID , massBertschingerTargetID, &
& accretionRateBertschingerID
@@ -75,27 +74,24 @@ function bertschingerMassConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(virialDensityContrastClass ), pointer :: virialDensityContrast_
!![
-
!!]
- self=nodeOperatorBertschingerMass(cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=nodeOperatorBertschingerMass(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_)
!![
-
!!]
return
end function bertschingerMassConstructorParameters
- function bertschingerMassConstructorInternal(cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function bertschingerMassConstructorInternal(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily bertschingerMass} node operator class.
!!}
@@ -103,10 +99,9 @@ function bertschingerMassConstructorInternal(cosmologyParameters_,cosmologyFunct
type (nodeOperatorBertschingerMass) :: self
class(cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class(virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
!![
-
+
!!]
!![
@@ -127,7 +122,6 @@ subroutine bertschingerMassDestructor(self)
!![
-
!!]
return
@@ -173,7 +167,6 @@ recursive subroutine bertschingerMassNodeInitialize(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& ) &
& )
@@ -228,7 +221,6 @@ recursive subroutine bertschingerMassNodeInitialize(self,node)
& ) , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& ) &
& -massUnresolved
diff --git a/source/nodes.operators.physics.CGM.chemistry.F90 b/source/nodes.operators.physics.CGM.chemistry.F90
index cf4d3afdfc..83e4e94a47 100644
--- a/source/nodes.operators.physics.CGM.chemistry.F90
+++ b/source/nodes.operators.physics.CGM.chemistry.F90
@@ -27,7 +27,6 @@
use :: Chemical_Reaction_Rates , only : chemicalReactionRateClass
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
use :: Radiation_Fields , only : radiationFieldClass , crossSectionFunctionTemplate
use :: Numerical_Constants_Physical , only : plancksConstant , speedLight
use :: Numerical_Constants_Units , only : angstromsPerMeter , electronVolt
@@ -74,7 +73,6 @@
class (chemicalReactionRateClass ), pointer :: chemicalReactionRate_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
class (radiationFieldClass ), pointer :: radiation_ => null()
logical , allocatable, dimension(:) :: maskAnalytic
integer :: atomicHydrogenIndex , atomicHydrogenCationIndex, &
@@ -138,7 +136,6 @@ function cgmChemistryConstructorParameters(parameters) result(self)
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (radiationFieldClass ), pointer :: radiation_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
double precision :: fractionTimescaleEquilibrium
!![
@@ -154,8 +151,8 @@ function cgmChemistryConstructorParameters(parameters) result(self)
-
!!]
+ radiation_ => null()
if (parameters%isPresent('radiationFieldIntergalacticBackground',searchInParents=.true.)) then
!![
@@ -168,8 +165,8 @@ function cgmChemistryConstructorParameters(parameters) result(self)
!!]
end select
- end if
- self=nodeOperatorCGMChemistry(fractionTimescaleEquilibrium,atomicIonizationRateCollisional_,atomicRecombinationRateRadiative_,atomicCrossSectionIonizationPhoto_,chemicalReactionRate_,darkMatterHaloScale_,cosmologyFunctions_,hotHaloMassDistribution_,radiation_)
+ end if
+ self=nodeOperatorCGMChemistry(fractionTimescaleEquilibrium,atomicIonizationRateCollisional_,atomicRecombinationRateRadiative_,atomicCrossSectionIonizationPhoto_,chemicalReactionRate_,darkMatterHaloScale_,cosmologyFunctions_,radiation_)
!![
@@ -179,30 +176,27 @@ function cgmChemistryConstructorParameters(parameters) result(self)
-
!!]
return
end function cgmChemistryConstructorParameters
- function cgmChemistryConstructorInternal(fractionTimescaleEquilibrium,atomicIonizationRateCollisional_,atomicRecombinationRateRadiative_,atomicCrossSectionIonizationPhoto_,chemicalReactionRate_,darkMatterHaloScale_,cosmologyFunctions_,hotHaloMassDistribution_,radiation_) result(self)
+ function cgmChemistryConstructorInternal(fractionTimescaleEquilibrium,atomicIonizationRateCollisional_,atomicRecombinationRateRadiative_,atomicCrossSectionIonizationPhoto_,chemicalReactionRate_,darkMatterHaloScale_,cosmologyFunctions_,radiation_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily cgmChemistry} node operator class.
!!}
use :: Chemical_Abundances_Structure, only : Chemicals_Index, Chemicals_Property_Count
implicit none
- type (nodeOperatorCGMChemistry ) :: self
- double precision , intent(in ) :: fractionTimescaleEquilibrium
- class (atomicIonizationRateCollisionalClass ), intent(in ), target :: atomicIonizationRateCollisional_
- class (atomicRecombinationRateRadiativeClass ), intent(in ), target :: atomicRecombinationRateRadiative_
- class (atomicCrossSectionIonizationPhotoClass), intent(in ), target :: atomicCrossSectionIonizationPhoto_
- class (chemicalReactionRateClass ), intent(in ), target :: chemicalReactionRate_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (radiationFieldClass ), intent(in ), target :: radiation_
- !$GLC attributes initialized :: radiationIntergalacticBackground
+ type (nodeOperatorCGMChemistry ) :: self
+ double precision , intent(in ) :: fractionTimescaleEquilibrium
+ class (atomicIonizationRateCollisionalClass ), intent(in ), target :: atomicIonizationRateCollisional_
+ class (atomicRecombinationRateRadiativeClass ), intent(in ), target :: atomicRecombinationRateRadiative_
+ class (atomicCrossSectionIonizationPhotoClass), intent(in ), target :: atomicCrossSectionIonizationPhoto_
+ class (chemicalReactionRateClass ), intent(in ), target :: chemicalReactionRate_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
+ class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
+ class (radiationFieldClass ), intent(in ), pointer :: radiation_
!![
-
+
!!]
! Determine if chemicals are being solved for.
@@ -236,7 +230,6 @@ subroutine cgmChemistryDestructor(self)
-
!!]
return
end subroutine cgmChemistryDestructor
@@ -325,9 +318,11 @@ subroutine cgmChemistryDifferentialEvolution(self,node,interrupt,functionInterru
!!}
use :: Chemical_Abundances_Structure , only : chemicalAbundances
use :: Galacticus_Nodes , only : nodeComponentHotHalo
- use :: Numerical_Constants_Astronomical , only : gigaYear , megaParsec
+ use :: Numerical_Constants_Astronomical , only : gigaYear , megaParsec
use :: Numerical_Constants_Prefixes , only : centi
use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class (nodeOperatorCGMChemistry), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
@@ -335,6 +330,7 @@ subroutine cgmChemistryDifferentialEvolution(self,node,interrupt,functionInterru
procedure (interruptTask ), intent(inout), pointer :: functionInterrupt
integer , intent(in ) :: propertyType
class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , parameter :: massHotHaloTiny =1.0d-6
type (chemicalAbundances ), save :: chemicalDensitiesRates , chemicalMassesRates, &
& chemicalDensities
@@ -355,23 +351,27 @@ subroutine cgmChemistryDifferentialEvolution(self,node,interrupt,functionInterru
! Compute the column length through the halo (in cm).
massHotHalo=hotHalo%mass()
if (massHotHalo > massHotHaloTiny) then
- radiusOuter =+hotHalo %outerRadius ( )
- factorBoostColumn=+self %hotHaloMassDistribution_%radialMoment (node,0.0d0,radiusOuter) &
- & *4.0d0 &
- & *Pi &
- & *radiusOuter **2 &
- & /3.0d0 &
- & /massHotHalo
- lengthColumn =+radiusOuter &
- & *megaParsec &
- & /centi
- factorClumping =+self %hotHaloMassDistribution_%densitySquaredIntegral(node ,radiusOuter) &
- & *4.0d0 &
- & /3.0d0 &
- & *Pi &
- & *radiusOuter**3 &
- & /massHotHalo**2
- else
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ radiusOuter = +hotHalo %outerRadius ( )
+ factorBoostColumn = +massDistribution_%densityRadialMoment (0.0d0,radiusOuter ) &
+ & *4.0d0 &
+ & *Pi &
+ & *radiusOuter **2 &
+ & /3.0d0 &
+ & /massHotHalo
+ lengthColumn = +radiusOuter &
+ & *megaParsec &
+ & /centi
+ factorClumping = +massDistribution_%densitySquareIntegral( radiusOuter ) &
+ & *4.0d0 &
+ & /3.0d0 &
+ & *Pi &
+ & *radiusOuter**3 &
+ & /massHotHalo**2
+ !![
+
+ !!]
+ else
lengthColumn =+0.0d0
factorClumping =+1.0d0
end if
diff --git a/source/nodes.operators.physics.cooling_energy_radiated.F90 b/source/nodes.operators.physics.cooling_energy_radiated.F90
index dc3edf2240..8a50b1657c 100644
--- a/source/nodes.operators.physics.cooling_energy_radiated.F90
+++ b/source/nodes.operators.physics.cooling_energy_radiated.F90
@@ -22,13 +22,11 @@
following the model of \cite{benson_galaxy_2010-1}.
!!}
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
- use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Cooling_Functions , only : coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Chemical_States , only : chemicalStateClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Cooling_Functions , only : coolingFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Chemical_States , only : chemicalStateClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -46,13 +44,11 @@
A node operator class that accumulates an estimate of the energy radiated from the hot halo due to cooling following the model of \cite{benson_galaxy_2010-1}.
!!}
private
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (chemicalStateClass ), pointer :: chemicalState_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
+ class (chemicalStateClass ), pointer :: chemicalState_ => null()
+ type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
integer :: energyRadiatedID
contains
final :: coolingEnergyRadiatedDestructor
@@ -81,35 +77,29 @@ function coolingEnergyRadiatedConstructorParameters(parameters) result(self)
implicit none
type (nodeOperatorCoolingEnergyRadiated) :: self
type (inputParameters ), intent(inout) :: parameters
- class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(coolingFunctionClass ), pointer :: coolingFunction_
class(chemicalStateClass ), pointer :: chemicalState_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
-
-
-
-
-
+
+
+
+
!!]
- self=nodeOperatorCoolingEnergyRadiated(cosmologyFunctions_,coolingFunction_,hotHaloTemperatureProfile_,chemicalState_,darkMatterHaloScale_,galacticStructure_)
+ self=nodeOperatorCoolingEnergyRadiated(cosmologyFunctions_,coolingFunction_,chemicalState_,darkMatterHaloScale_)
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end function coolingEnergyRadiatedConstructorParameters
- function coolingEnergyRadiatedConstructorInternal(cosmologyFunctions_,coolingFunction_,hotHaloTemperatureProfile_,chemicalState_,darkMatterHaloScale_,galacticStructure_) result(self)
+ function coolingEnergyRadiatedConstructorInternal(cosmologyFunctions_,coolingFunction_,chemicalState_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily coolingEnergyRadiated} node operator class.
!!}
@@ -117,12 +107,10 @@ function coolingEnergyRadiatedConstructorInternal(cosmologyFunctions_,coolingFun
type (nodeOperatorCoolingEnergyRadiated) :: self
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(coolingFunctionClass ), intent(in ), target :: coolingFunction_
- class(hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class(chemicalStateClass ), intent(in ), target :: chemicalState_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
allocate(self%radiation)
@@ -154,13 +142,11 @@ subroutine coolingEnergyRadiatedDestructor(self)
type(nodeOperatorCoolingEnergyRadiated), intent(inout) :: self
!![
-
-
-
-
-
-
-
+
+
+
+
+
!!]
if (hotHaloMassEjectionEvent%isAttached(self,coolingEnergyRadiatedHotHaloMassEjection)) call hotHaloMassEjectionEvent%detach(self,coolingEnergyRadiatedHotHaloMassEjection)
return
@@ -204,8 +190,10 @@ subroutine coolingEnergyRadiatedDifferentialEvolution(self,node,interrupt,functi
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances , Chemicals_Property_Count
use :: Chemical_Reaction_Rates_Utilities, only : Chemicals_Mass_To_Density_Conversion
- use :: Galactic_Structure_Options , only : radiusLarge , massTypeGalactic
- use :: Numerical_Constants_Astronomical , only : gigaYear , massSolar , megaParsec
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous , radiusLarge, massTypeGalactic
+ use :: Numerical_Constants_Astronomical , only : gigaYear , massSolar , megaParsec
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Physical , only : boltzmannsConstant
use :: Numerical_Constants_Prefixes , only : hecto , centi
@@ -219,6 +207,9 @@ subroutine coolingEnergyRadiatedDifferentialEvolution(self,node,interrupt,functi
integer , intent(in ) :: propertyType
class (nodeComponentBasic ) , pointer :: basic
class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (massDistributionClass ) , pointer :: massDistribution_
+ class (kinematicsDistributionClass ) , pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
double precision :: density , temperature , &
& massToDensityConversion, numberDensityHydrogen, &
& numberDensityAllSpecies, coolingFunction , &
@@ -233,18 +224,29 @@ subroutine coolingEnergyRadiatedDifferentialEvolution(self,node,interrupt,functi
type is (nodeComponentHotHalo)
! Hot halo does not exists - nothing to do here.
class default
- basic => node %basic ( )
- massNotional = +hotHalo %mass ( ) &
- & +hotHalo %outflowedMass( ) &
- & +self %galacticStructure_%massEnclosed (node,radiusLarge,massType=massTypeGalactic)
+ basic => node %basic ( )
+ massDistribution_ => node %massDistribution(massType=massTypeGalactic)
+ massNotional = +hotHalo %mass ( ) &
+ & +hotHalo %outflowedMass ( ) &
+ & +massDistribution_%massTotal ( )
+ !![
+
+ !!]
if (massNotional <= 0.0d0) return
! Compute the mean density and temperature of the hot halo.
+ massDistribution_ => node %massDistribution (componentType=componentTypeHotHalo,massType=massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
density =+massNotional &
& *3.0d0 &
& /4.0d0 &
& /Pi &
& /hotHalo%outerRadius()**3
- temperature=self%hotHaloTemperatureProfile_%temperature(node,hotHalo%outerRadius())
+ coordinates=[hotHalo%outerRadius(),0.0d0,0.0d0]
+ temperature=+kinematicsDistribution_%temperature(coordinates)
+ !![
+
+
+ !!]
! Get the abundances for this node.
abundances_=hotHalo%abundances()
call abundances_%massToMassFraction(hotHalo%mass())
@@ -351,24 +353,30 @@ subroutine coolingEnergyRadiatedHotHaloMassEjection(self,hotHalo,massRate)
Respond to mass ejection from the hot halo component.
!!}
use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeComponentBasic, nodeComponentHotHalo
- use :: Galactic_Structure_Options, only : radiusLarge , massTypeGalactic
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo
+ use :: Galactic_Structure_Options, only : massTypeGalactic
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- class (* ), intent(inout) :: self
- class (nodeComponentHotHalo), intent(inout) :: hotHalo
- double precision , intent(in ) :: massRate
- class (nodeComponentBasic ), pointer :: basic
- type (treeNode ), pointer :: node
- double precision :: massNotional
+ class (* ), intent(inout) :: self
+ class (nodeComponentHotHalo ), intent(inout) :: hotHalo
+ double precision , intent(in ) :: massRate
+ class (nodeComponentBasic ), pointer :: basic
+ type (treeNode ), pointer :: node
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: massNotional
select type (self)
class is (nodeOperatorCoolingEnergyRadiated)
! Compute the mass in the notional hot halo.
- node => hotHalo%hostNode
- basic => node %basic ( )
- massNotional = +hotHalo %mass ( ) &
- & +hotHalo %outflowedMass( ) &
- & +self %galacticStructure_%massEnclosed (node,radiusLarge,massType=massTypeGalactic)
+ node => hotHalo %hostNode
+ basic => node %basic ( )
+ massDistribution_ => node %massDistribution(massType=massTypeGalactic)
+ massNotional = +hotHalo %mass ( ) &
+ & +hotHalo %outflowedMass ( ) &
+ & +massDistribution_%massTotal ( )
+ !![
+
+ !!]
if (massNotional > 0.0d0) &
& call hotHalo%floatRank0MetaPropertyRate(self%energyRadiatedID,-hotHalo%floatRank0MetaPropertyGet(self%energyRadiatedID)*massRate/massNotional)
class default
diff --git a/source/nodes.operators.physics.dark_matter_profile.initialize.F90 b/source/nodes.operators.physics.dark_matter_profile.initialize.F90
new file mode 100644
index 0000000000..186ca6581e
--- /dev/null
+++ b/source/nodes.operators.physics.dark_matter_profile.initialize.F90
@@ -0,0 +1,77 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 .
+
+ !!{
+ Implements a node operator class that simply initializes the dark matter profile.
+ !!}
+
+ !![
+
+
+ A node operator class that simply initializes the dark matter profile.
+
+
+ !!]
+ type, extends(nodeOperatorClass) :: nodeOperatorDarkMatterProfileInitialize
+ !!{
+ A node operator class that simply initializes the dark matter profile in halos.
+ !!}
+ private
+ contains
+ procedure :: nodeTreeInitialize => darkMatterProfileInitializeNodeTreeInitialize
+ end type nodeOperatorDarkMatterProfileInitialize
+
+ interface nodeOperatorDarkMatterProfileInitialize
+ !!{
+ Constructors for the {\normalfont \ttfamily darkMatterProfileInitialize} node operator class.
+ !!}
+ module procedure darkMatterProfileInitializeConstructorParameters
+ end interface nodeOperatorDarkMatterProfileInitialize
+
+contains
+
+ function darkMatterProfileInitializeConstructorParameters(parameters) result(self)
+ !!{
+ Constructor for the {\normalfont \ttfamily darkMatterProfileInitialize} node operator class which takes a parameter set as input.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ implicit none
+ type (nodeOperatorDarkMatterProfileInitialize) :: self
+ type (inputParameters ), intent(inout) :: parameters
+
+ self=nodeOperatorDarkMatterProfileInitialize()
+ !![
+
+ !!]
+ return
+ end function darkMatterProfileInitializeConstructorParameters
+
+ subroutine darkMatterProfileInitializeNodeTreeInitialize(self,node)
+ !!{
+ Initialize dark matter profile scale radii.
+ !!}
+ use :: Galacticus_Nodes, only : nodeComponentDarkMatterProfile
+ implicit none
+ class(nodeOperatorDarkMatterProfileInitialize), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ class(nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile
+
+ darkMatterProfile => node%darkMatterProfile(autoCreate=.true.)
+ return
+ end subroutine darkMatterProfileInitializeNodeTreeInitialize
diff --git a/source/nodes.operators.physics.halo_angular_momentum.Vitvitska2002.F90 b/source/nodes.operators.physics.halo_angular_momentum.Vitvitska2002.F90
index cc7a193500..62ba0620d8 100644
--- a/source/nodes.operators.physics.halo_angular_momentum.Vitvitska2002.F90
+++ b/source/nodes.operators.physics.halo_angular_momentum.Vitvitska2002.F90
@@ -24,7 +24,6 @@
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profile_Scales , only : darkMatterProfileScaleRadiusClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Halo_Spin_Distributions , only : haloSpinDistributionClass
use :: Virial_Orbits , only : virialOrbitClass
use :: Merger_Trees_Build_Mass_Resolution, only : mergerTreeMassResolutionClass
@@ -63,7 +62,6 @@
!!}
private
class (haloSpinDistributionClass ), pointer :: haloSpinDistribution_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialOrbitClass ), pointer :: virialOrbit_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
@@ -93,7 +91,6 @@ function haloAngularMomentumVitvitska2002ConstructorParameters(parameters) resul
type (nodeOperatorHaloAngularMomentumVitvitska2002) :: self
type (inputParameters ), intent(inout) :: parameters
class (haloSpinDistributionClass ), pointer :: haloSpinDistribution_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialOrbitClass ), pointer :: virialOrbit_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_
@@ -115,16 +112,14 @@ function haloAngularMomentumVitvitska2002ConstructorParameters(parameters) resul
-
!!]
- self=nodeOperatorHaloAngularMomentumVitvitska2002(exponentMass,angularMomentumVarianceSpecific,darkMatterProfileScaleRadius_,haloSpinDistribution_,darkMatterProfileDMO_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_)
+ self=nodeOperatorHaloAngularMomentumVitvitska2002(exponentMass,angularMomentumVarianceSpecific,darkMatterProfileScaleRadius_,haloSpinDistribution_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_)
!![
-
@@ -133,7 +128,7 @@ function haloAngularMomentumVitvitska2002ConstructorParameters(parameters) resul
return
end function haloAngularMomentumVitvitska2002ConstructorParameters
- function haloAngularMomentumVitvitska2002ConstructorInternal(exponentMass,angularMomentumVarianceSpecific,darkMatterProfileScaleRadius_,haloSpinDistribution_,darkMatterProfileDMO_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_) result(self)
+ function haloAngularMomentumVitvitska2002ConstructorInternal(exponentMass,angularMomentumVarianceSpecific,darkMatterProfileScaleRadius_,haloSpinDistribution_,darkMatterHaloScale_,virialOrbit_,mergerTreeMassResolution_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily haloAngularMomentumVitvitska2002} node operator class.
!!}
@@ -142,14 +137,13 @@ function haloAngularMomentumVitvitska2002ConstructorInternal(exponentMass,angula
implicit none
type (nodeOperatorHaloAngularMomentumVitvitska2002) :: self
class (haloSpinDistributionClass ), intent(in ), target :: haloSpinDistribution_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialOrbitClass ), intent(in ), target :: virialOrbit_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileScaleRadiusClass ), intent(in ), target :: darkMatterProfileScaleRadius_
class (mergerTreeMassResolutionClass ), intent(in ), target :: mergerTreeMassResolution_
double precision , intent(in ) :: exponentMass , angularMomentumVarianceSpecific
!![
-
+
!!]
! Ensure that the spin component supports vector angular momentum.
@@ -183,7 +177,6 @@ subroutine haloAngularMomentumVitvitska2002Destructor(self)
!![
-
@@ -229,7 +222,7 @@ subroutine haloAngularMomentumVitvitska2002NodeTreeInitialize(self,node)
if (.not.associated(node%firstChild)) then
theta =acos(2.0d0 *node%hostTree%randomNumberGenerator_%uniformSample()-1.0d0)
phi = 2.0d0*Pi*node%hostTree%randomNumberGenerator_%uniformSample()
- angularMomentumValue=self%haloSpinDistribution_%sample(node)*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ angularMomentumValue=self%haloSpinDistribution_%sample(node)*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_)
angularMomentumTotal=angularMomentumValue*[sin(theta)*cos(phi),sin(theta)*sin(phi),cos(theta)]
else
nodeChild => node %firstChild
diff --git a/source/nodes.operators.physics.halo_angular_momentum_random.F90 b/source/nodes.operators.physics.halo_angular_momentum_random.F90
index a65f5695fe..885151a760 100644
--- a/source/nodes.operators.physics.halo_angular_momentum_random.F90
+++ b/source/nodes.operators.physics.halo_angular_momentum_random.F90
@@ -21,8 +21,8 @@
Implements a node operator class that initializes halo angular momenta using spins drawn at random from a distribution.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Halo_Spin_Distributions , only : haloSpinDistributionClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Halo_Spin_Distributions, only : haloSpinDistributionClass
!![
@@ -34,7 +34,7 @@
A node operator class that initializes halo spins to random values drawn from a distribution.
!!}
private
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (haloSpinDistributionClass), pointer :: haloSpinDistribution_ => null()
double precision :: factorReset
contains
@@ -60,7 +60,7 @@ function haloAngularMomentumRandomConstructorParameters(parameters) result(self)
implicit none
type (nodeOperatorHaloAngularMomentumRandom) :: self
type (inputParameters ), intent(inout) :: parameters
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (haloSpinDistributionClass ), pointer :: haloSpinDistribution_
double precision :: factorReset
@@ -72,28 +72,28 @@ function haloAngularMomentumRandomConstructorParameters(parameters) result(self)
-
+
!!]
- self=nodeOperatorHaloAngularMomentumRandom(factorReset,haloSpinDistribution_,darkMatterProfileDMO_)
+ self=nodeOperatorHaloAngularMomentumRandom(factorReset,haloSpinDistribution_,darkMatterHaloScale_)
!![
-
+
!!]
return
end function haloAngularMomentumRandomConstructorParameters
- function haloAngularMomentumRandomConstructorInternal(factorReset,haloSpinDistribution_,darkMatterProfileDMO_) result(self)
+ function haloAngularMomentumRandomConstructorInternal(factorReset,haloSpinDistribution_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily haloAngularMomentumRandom} node operator class.
!!}
implicit none
type (nodeOperatorHaloAngularMomentumRandom) :: self
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (haloSpinDistributionClass ), intent(in ), target :: haloSpinDistribution_
double precision , intent(in ) :: factorReset
!![
-
+
!!]
return
@@ -107,7 +107,7 @@ subroutine haloAngularMomentumRandomDestructor(self)
type(nodeOperatorHaloAngularMomentumRandom), intent(inout) :: self
!![
-
+
!!]
return
@@ -145,7 +145,8 @@ subroutine haloAngularMomentumRandomNodeInitialize(self,node)
basicProgenitor => nodeProgenitor %basic ( )
spinPrevious = self %haloSpinDistribution_%sample( nodeProgenitor)
massPrevious = basicProgenitor %mass ( )
- angularMomentum = spinPrevious*Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterProfileDMO_)
+ angularMomentum = spinPrevious*Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterHaloScale_)
+ call spinProgenitor%angularMomentumSet(spinPrevious*Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterHaloScale_))
call spinProgenitor%angularMomentumSet(angularMomentum)
if (spinProgenitor%angularMomentumVectorIsSettable()) &
& call spinProgenitor%angularMomentumVectorSet([angularMomentum,0.0d0,0.0d0])
@@ -156,8 +157,8 @@ subroutine haloAngularMomentumRandomNodeInitialize(self,node)
spinPrevious=self %haloSpinDistribution_%sample(nodeProgenitor)
massPrevious=basicProgenitor %mass ( )
end if
- spinProgenitor => nodeProgenitor%spin(autoCreate=.true.)
- angularMomentum = spinPrevious*Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterProfileDMO_)
+ spinProgenitor => nodeProgenitor%spin(autoCreate=.true.)
+ angularMomentum = spinPrevious*Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterHaloScale_)
call spinProgenitor%angularMomentumSet ( angularMomentum )
if (spinProgenitor%angularMomentumVectorIsSettable()) &
& call spinProgenitor%angularMomentumVectorSet([angularMomentum,0.0d0,0.0d0])
diff --git a/source/nodes.operators.physics.halo_angular_momentum_random_walk.F90 b/source/nodes.operators.physics.halo_angular_momentum_random_walk.F90
index 8bca79ee6f..c349450795 100644
--- a/source/nodes.operators.physics.halo_angular_momentum_random_walk.F90
+++ b/source/nodes.operators.physics.halo_angular_momentum_random_walk.F90
@@ -21,9 +21,8 @@
Implements a node operator class that initializes halo angular momenta using a random walk in angular momentum.
!!}
- use :: Halo_Spin_Distributions , only : haloSpinDistributionClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Halo_Spin_Distributions, only : haloSpinDistributionClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -47,7 +46,6 @@
!!}
private
class (haloSpinDistributionClass), pointer :: haloSpinDistribution_ => null()
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
double precision :: angularMomentumVarianceSpecific
contains
@@ -74,7 +72,6 @@ function haloAngularMomentumRandomWalkConstructorParameters(parameters) result(s
type (nodeOperatorHaloAngularMomentumRandomWalk) :: self
type (inputParameters ), intent(inout) :: parameters
class (haloSpinDistributionClass ), pointer :: haloSpinDistribution_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: angularMomentumVarianceSpecific
@@ -86,31 +83,28 @@ function haloAngularMomentumRandomWalkConstructorParameters(parameters) result(s
0.0029d0
-
!!]
- self=nodeOperatorHaloAngularMomentumRandomWalk(angularMomentumVarianceSpecific,haloSpinDistribution_,darkMatterProfileDMO_,darkMatterHaloScale_)
+ self=nodeOperatorHaloAngularMomentumRandomWalk(angularMomentumVarianceSpecific,haloSpinDistribution_,darkMatterHaloScale_)
!![
-
!!]
return
end function haloAngularMomentumRandomWalkConstructorParameters
- function haloAngularMomentumRandomWalkConstructorInternal(angularMomentumVarianceSpecific,haloSpinDistribution_,darkMatterProfileDMO_,darkMatterHaloScale_) result(self)
+ function haloAngularMomentumRandomWalkConstructorInternal(angularMomentumVarianceSpecific,haloSpinDistribution_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily haloAngularMomentumRandomWalk} node operator class.
!!}
implicit none
type (nodeOperatorHaloAngularMomentumRandomWalk) :: self
class (haloSpinDistributionClass ), intent(in ), target :: haloSpinDistribution_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
double precision , intent(in ) :: angularMomentumVarianceSpecific
!![
-
+
!!]
return
@@ -125,7 +119,6 @@ subroutine haloAngularMomentumRandomWalkDestructor(self)
!![
-
!!]
return
@@ -161,8 +154,8 @@ subroutine haloAngularMomentumRandomWalkNodeInitialize(self,node)
! Select a angular momentum for the initial halo using the spin distribution function.
basicProgenitor => nodeProgenitor %basic ( )
spinProgenitor => nodeProgenitor %spin (autoCreate=.true. )
- angularMomentumScalar = +self %haloSpinDistribution_%sample( nodeProgenitor) &
- & *Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterProfileDMO_)
+ angularMomentumScalar = +self %haloSpinDistribution_%sample( nodeProgenitor) &
+ & *Dark_Matter_Halo_Angular_Momentum_Scale(nodeProgenitor,self%darkMatterHaloScale_)
call spinProgenitor%angularMomentumSet(angularMomentumScalar)
! Compute the initial angular momentum vector. We choose this to be aligned along the x-axis. As we only care about the
! magnitude of the angular momentum any choice of initial vector direction is equivalent.
diff --git a/source/nodes.operators.physics.position.trace_dark_matter.F90 b/source/nodes.operators.physics.position.trace_dark_matter.F90
index 80639d9863..c44f7dd117 100644
--- a/source/nodes.operators.physics.position.trace_dark_matter.F90
+++ b/source/nodes.operators.physics.position.trace_dark_matter.F90
@@ -21,7 +21,6 @@
Implements a node operator class that sets the positions of subhalos to trace the dark matter component of their host halo.
!!}
- use :: Galactic_Structure , only : galacticStructureClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Satellite_Oprhan_Distributions, only : satelliteOrphanDistributionTraceDarkMatter
!![
@@ -41,7 +40,6 @@
!!}
private
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
type (satelliteOrphanDistributionTraceDarkMatter), pointer :: satelliteOrphanDistribution_ => null()
contains
!![
@@ -74,36 +72,32 @@ function positionTraceDarkMatterConstructorParameters(parameters) result(self)
type (nodeOperatorPositionTraceDarkMatter) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=nodeOperatorPositionTraceDarkMatter(darkMatterHaloScale_,galacticStructure_)
+ self=nodeOperatorPositionTraceDarkMatter(darkMatterHaloScale_)
!![
-
!!]
return
end function positionTraceDarkMatterConstructorParameters
- function positionTraceDarkMatterConstructorInternal(darkMatterHaloScale_,galacticStructure_) result(self)
+ function positionTraceDarkMatterConstructorInternal(darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily positionTraceDarkMatter} node operator class.
!!}
implicit none
type (nodeOperatorPositionTraceDarkMatter) :: self
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
allocate(self%satelliteOrphanDistribution_)
!![
-
+
!!]
return
end function positionTraceDarkMatterConstructorInternal
@@ -131,7 +125,6 @@ subroutine positionTraceDarkMatterDestructor(self)
if (satelliteHostChangeEvent%isAttached(self,positionTraceDarkMatterSatelliteHostChange)) call satelliteHostChangeEvent%detach(self,positionTraceDarkMatterSatelliteHostChange)
!![
-
!!]
return
diff --git a/source/nodes.operators.physics.satellite.heating.tidal.F90 b/source/nodes.operators.physics.satellite.heating.tidal.F90
index 6e6f5314ad..7a8591aa4d 100644
--- a/source/nodes.operators.physics.satellite.heating.tidal.F90
+++ b/source/nodes.operators.physics.satellite.heating.tidal.F90
@@ -24,7 +24,6 @@
!!}
use :: Satellite_Tidal_Heating, only : satelliteTidalHeatingRateClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -37,7 +36,6 @@
!!}
private
class (satelliteTidalHeatingRateClass), pointer :: satelliteTidalHeatingRate_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: efficiencyDecay
logical :: applyPreInfall
contains
@@ -65,7 +63,6 @@ function satelliteTidalHeatingRateConstructorParameters(parameters) result(self)
type (nodeOperatorSatelliteTidalHeating) :: self
type (inputParameters ), intent(inout) :: parameters
class (satelliteTidalHeatingRateClass ), pointer :: satelliteTidalHeatingRate_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: efficiencyDecay
logical :: applyPreInfall
@@ -83,29 +80,26 @@ function satelliteTidalHeatingRateConstructorParameters(parameters) result(self)
-
!!]
- self=nodeOperatorSatelliteTidalHeating(efficiencyDecay,applyPreInfall,satelliteTidalHeatingRate_,galacticStructure_)
+ self=nodeOperatorSatelliteTidalHeating(efficiencyDecay,applyPreInfall,satelliteTidalHeatingRate_)
!![
-
!!]
return
end function satelliteTidalHeatingRateConstructorParameters
- function satelliteTidalHeatingRateConstructorInternal(efficiencyDecay,applyPreInfall,satelliteTidalHeatingRate_,galacticStructure_) result(self)
+ function satelliteTidalHeatingRateConstructorInternal(efficiencyDecay,applyPreInfall,satelliteTidalHeatingRate_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily satelliteTidalHeatingRate} node operator class.
!!}
implicit none
type (nodeOperatorSatelliteTidalHeating) :: self
class (satelliteTidalHeatingRateClass ), intent(in ), target :: satelliteTidalHeatingRate_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: efficiencyDecay
logical , intent(in ) :: applyPreInfall
!![
-
+
!!]
return
@@ -120,7 +114,6 @@ subroutine satelliteTidalHeatingRateDestructor(self)
!![
-
!!]
return
end subroutine satelliteTidalHeatingRateDestructor
@@ -129,7 +122,9 @@ subroutine satelliteTidalHeatingRateDifferentialEvolution(self,node,interrupt,fu
!!{
Perform mass loss from a satellite due to tidal stripping.
!!}
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
use :: Galacticus_Nodes , only : nodeComponentSatellite, nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : kilo
@@ -144,10 +139,12 @@ subroutine satelliteTidalHeatingRateDifferentialEvolution(self,node,interrupt,fu
class (nodeComponentBasic ) , pointer :: basic , basicHost
class (nodeComponentSatellite ) , pointer :: satellite
type (treeNode ) , pointer :: nodeHost
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , dimension(3) :: position , velocity
double precision :: radius , orbitalPeriod , &
& radialFrequency , angularFrequency
type (tensorRank2Dimension3Symmetric) :: tidalTensor , tidalTensorPathIntegrated
+ type (coordinateCartesian ) :: coordinates
!$GLC attributes unused :: interrupt, functionInterrupt, propertyType
if (.not.self%applyPreInfall.and..not.node%isSatellite()) return
@@ -178,7 +175,12 @@ subroutine satelliteTidalHeatingRateDifferentialEvolution(self,node,interrupt,fu
radius = Vector_Magnitude (position)
if (radius <= 0.0d0) return ! Do not compute rates at zero radius.
! Calculate tidal tensor and rate of change of integrated tidal tensor.
- tidalTensor = self%galacticStructure_%tidalTensor(nodeHost,position)
+ coordinates = position
+ massDistribution_ => nodeHost %massDistribution( )
+ tidalTensor = massDistribution_%tidalTensor (coordinates)
+ !![
+
+ !!]
! Compute the orbital period.
angularFrequency=+Vector_Magnitude(Vector_Product(position,velocity)) &
& /radius**2 &
diff --git a/source/nodes.operators.physics.satellite_merging.radius_trigger.F90 b/source/nodes.operators.physics.satellite_merging.radius_trigger.F90
index 809ff8d032..f9ccee935e 100644
--- a/source/nodes.operators.physics.satellite_merging.radius_trigger.F90
+++ b/source/nodes.operators.physics.satellite_merging.radius_trigger.F90
@@ -22,7 +22,6 @@
!!}
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Kepler_Orbits , only : keplerOrbitCount
!![
@@ -36,7 +35,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: radiusVirialFraction
logical :: recordMergedSubhaloProperties , recordFirstLevelOnly
integer :: mergedSubhaloIDs (keplerOrbitCount) , nodeHierarchyLevelMaximumID
@@ -74,7 +72,6 @@ function satelliteMergingRadiusTriggerConstructorParameters(parameters) result(s
type (nodeOperatorSatelliteMergingRadiusTrigger) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: radiusVirialFraction
logical :: recordMergedSubhaloProperties, recordFirstLevelOnly
@@ -98,18 +95,16 @@ function satelliteMergingRadiusTriggerConstructorParameters(parameters) result(s
-
!!]
- self=nodeOperatorSatelliteMergingRadiusTrigger(radiusVirialFraction,recordMergedSubhaloProperties,recordFirstLevelOnly,darkMatterHaloScale_,galacticStructure_)
+ self=nodeOperatorSatelliteMergingRadiusTrigger(radiusVirialFraction,recordMergedSubhaloProperties,recordFirstLevelOnly,darkMatterHaloScale_)
!![
-
!!]
return
end function satelliteMergingRadiusTriggerConstructorParameters
- function satelliteMergingRadiusTriggerConstructorInternal(radiusVirialFraction,recordMergedSubhaloProperties,recordFirstLevelOnly,darkMatterHaloScale_,galacticStructure_) result(self)
+ function satelliteMergingRadiusTriggerConstructorInternal(radiusVirialFraction,recordMergedSubhaloProperties,recordFirstLevelOnly,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily satelliteMergingRadiusTrigger} node operator class.
!!}
@@ -120,20 +115,19 @@ function satelliteMergingRadiusTriggerConstructorInternal(radiusVirialFraction,r
double precision , intent(in ) :: radiusVirialFraction
logical , intent(in ) :: recordMergedSubhaloProperties, recordFirstLevelOnly
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
if (recordMergedSubhaloProperties) then
!![
-
-
-
-
-
-
-
+
+
+
+
+
+
+
!!]
end if
return
@@ -148,7 +142,6 @@ subroutine satelliteMergingRadiusTriggerDestructor(self)
!![
-
!!]
return
end subroutine satelliteMergingRadiusTriggerDestructor
@@ -266,45 +259,53 @@ double precision function satelliteMergingRadiusTriggerRadiusMerge(self,node)
Compute the merging radius for a node.
!!}
use :: Galacticus_Nodes , only : treeNode
- use :: Galactic_Structure_Options, only : massTypeGalactic, radiusLarge
+ use :: Galactic_Structure_Options, only : massTypeGalactic
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (nodeOperatorSatelliteMergingRadiusTrigger), intent(inout) :: self
type (treeNode ), intent(inout) :: node
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionHost_
double precision :: radiusHalfMassCentral, radiusHalfMassSatellite
! Find the host node.
nodeHost => node%mergesWith()
- ! Get half-mass radii of central and satellite galaxies. We first check that the total mass in the galactic component
- ! (found by setting the radius to "radiusLarge") is non-zero as we do not want to attempt to find the half-mass radius
- ! of the galactic component, if no galactic component exists. To correctly handle the case that numerical errors lead
- ! to a zero-size galactic component (the enclosed mass within zero radius is non-zero and equals to the total mass of
- ! this component), we do a further check that the enclosed mass within zero radius is smaller than half of the total
- ! mass in the galactic component.
- if ( &
- & self%galacticStructure_%massEnclosed(nodeHost,massType=massTypeGalactic,radius=radiusLarge) &
- & > &
- & max( &
- & 0.0d0, &
- & 2.0d0*self%galacticStructure_%massEnclosed(nodeHost,massType=massTypeGalactic,radius=0.0d0 ) &
- & ) &
+ ! Get mass distributions.
+ massDistribution_ => node %massDistribution(massType=massTypeGalactic)
+ massDistributionHost_ => nodeHost%massDistribution(massType=massTypeGalactic)
+ ! Get half-mass radii of central and satellite galaxies. We first check that the total mass in the galactic component is
+ ! non-zero as we do not want to attempt to find the half-mass radius of the galactic component, if no galactic component
+ ! exists. To correctly handle the case that numerical errors lead to a zero-size galactic component (the enclosed mass
+ ! within zero radius is non-zero and equals to the total mass of this component), we do a further check that the enclosed
+ ! mass within zero radius is smaller than half of the total mass in the galactic component.
+ if ( &
+ & massDistributionHost_%massTotal() &
+ & > &
+ & max( &
+ & 0.0d0, &
+ & 2.0d0*massDistributionHost_%massEnclosedBySphere(radius=0.0d0) &
+ & ) &
& ) then
- radiusHalfMassCentral =self%galacticStructure_%radiusEnclosingMass(nodeHost,massFractional=0.5d0,massType=massTypeGalactic)
+ radiusHalfMassCentral =massDistributionHost_%radiusEnclosingMass(massFractional=0.5d0)
else
radiusHalfMassCentral =0.0d0
end if
- if ( &
- & self%galacticStructure_%massEnclosed(node ,massType=massTypeGalactic,radius=radiusLarge) &
- & > &
- & max( &
- & 0.0d0, &
- & 2.0d0*self%galacticStructure_%massEnclosed(node ,massType=massTypeGalactic,radius=0.0d0 ) &
- & ) &
+ if ( &
+ & massDistribution_ %massTotal() &
+ & > &
+ & max( &
+ & 0.0d0, &
+ & 2.0d0*massDistribution_ %massEnclosedBySphere(radius=0.0d0) &
+ & ) &
& ) then
- radiusHalfMassSatellite=self%galacticStructure_%radiusEnclosingMass(node ,massFractional=0.5d0,massType=massTypeGalactic)
+ radiusHalfMassSatellite=massDistribution_ %radiusEnclosingMass(massFractional=0.5d0)
else
radiusHalfMassSatellite=0.0d0
- end if
+ end if
+ !![
+
+
+ !!]
satelliteMergingRadiusTriggerRadiusMerge=max( &
& + radiusHalfMassSatellite &
& + radiusHalfMassCentral , &
diff --git a/source/nodes.operators.physics.satellite_orbits.F90 b/source/nodes.operators.physics.satellite_orbits.F90
index e6c729d353..cbb8888f45 100644
--- a/source/nodes.operators.physics.satellite_orbits.F90
+++ b/source/nodes.operators.physics.satellite_orbits.F90
@@ -23,9 +23,6 @@
Implements a node operator class that propagates satellite halos along their orbits.
!!}
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
-
!![
A node operator class that propagates satellite halos along their orbits.
@@ -36,12 +33,9 @@
A node operator class that propagates satellite halos along their orbits.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- logical :: trackPreInfallOrbit
- integer :: rateGrowthMassBoundID
+ logical :: trackPreInfallOrbit
+ integer :: rateGrowthMassBoundID
contains
- final :: satelliteOrbitDestructor
procedure :: nodeInitialize => satelliteOrbitNodeInitialize
procedure :: nodePromote => satelliteOrbitNodePromote
procedure :: nodesMerge => satelliteOrbitNodeMerge
@@ -74,8 +68,6 @@ function satelliteOrbitConstructorParameters(parameters) result(self)
implicit none
type (nodeOperatorSatelliteOrbit) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
logical :: trackPreInfallOrbit
!![
@@ -85,30 +77,24 @@ function satelliteOrbitConstructorParameters(parameters) result(self)
If true, (approximately) track the orbits of halos prior to infall.
-
-
!!]
- self=nodeOperatorSatelliteOrbit(trackPreInfallOrbit,galacticStructure_,darkMatterProfileDMO_)
+ self=nodeOperatorSatelliteOrbit(trackPreInfallOrbit)
!![
-
-
!!]
return
end function satelliteOrbitConstructorParameters
- function satelliteOrbitConstructorInternal(trackPreInfallOrbit,galacticStructure_,darkMatterProfileDMO_) result(self)
+ function satelliteOrbitConstructorInternal(trackPreInfallOrbit) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily satelliteOrbit} node operator class.
!!}
use :: Input_Parameters, only : inputParameters
implicit none
- type (nodeOperatorSatelliteOrbit) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- logical , intent(in ), :: trackPreInfallOrbit
+ type (nodeOperatorSatelliteOrbit) :: self
+ logical , intent(in ) :: trackPreInfallOrbit
!![
-
+
!!]
if (self%trackPreInfallOrbit) then
@@ -119,20 +105,6 @@ function satelliteOrbitConstructorInternal(trackPreInfallOrbit,galacticStructure
return
end function satelliteOrbitConstructorInternal
- subroutine satelliteOrbitDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily satelliteOrbit} node operator class.
- !!}
- implicit none
- type(nodeOperatorSatelliteOrbit), intent(inout) :: self
-
- !![
-
-
- !!]
- return
- end subroutine satelliteOrbitDestructor
-
subroutine satelliteOrbitNodeInitialize(self,node)
!!{
Estimate the position of nodes relative to their hosts prior to infall.
@@ -219,21 +191,24 @@ integer function orbitalODEs(time,phaseSpaceCoordinates,phaseSpaceCoordinatesRat
ODEs describing a halo orbit.
!!}
use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly, massTypeDark
use :: Interface_GSL , only : GSL_Success
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec, gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
+ use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec, gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions , only : massDistributionClass
use :: Vectors , only : Vector_Magnitude
implicit none
- double precision , intent(in ) :: time
- double precision , dimension(:), intent(in ) :: phaseSpaceCoordinates
- double precision , dimension(:), intent( out) :: phaseSpaceCoordinatesRateOfChange
- double precision , dimension(3) :: position , velocity , &
- & acceleration
- type (treeNode ), pointer :: nodeHost , nodeDescendent
- class (nodeComponentBasic), pointer :: basicProgenitor , basicDescendent
- double precision :: massSatellite , massHost , &
- & factorInterpolate , radius , &
- & massRatio
+ double precision , intent(in ) :: time
+ double precision , dimension(:), intent(in ) :: phaseSpaceCoordinates
+ double precision , dimension(:), intent( out) :: phaseSpaceCoordinatesRateOfChange
+ double precision , dimension(3) :: position , velocity , &
+ & acceleration
+ type (treeNode ), pointer :: nodeHost , nodeDescendent
+ class (nodeComponentBasic ), pointer :: basicProgenitor , basicDescendent
+ class (massDistributionClass), pointer :: massDistributionDescendent , massDistributionHost
+ double precision :: massSatellite , massHost , &
+ & factorInterpolate , radius , &
+ & massRatio
! Extract orbital position and velocity.
orbitalODEs =GSL_Success
@@ -264,26 +239,32 @@ integer function orbitalODEs(time,phaseSpaceCoordinates,phaseSpaceCoordinatesRat
if (associated(nodeHost)) basicProgenitor => nodeHost%basic()
end do
if (associated(nodeHost)) then
- nodeDescendent => nodeHost %parent
- basicDescendent => nodeDescendent%basic ()
- radius = Vector_Magnitude(position)
- factorInterpolate = +(+ time -basicProgenitor%time()) &
- & /(+basicDescendent%time()-basicProgenitor%time())
- massHost = +self_%darkMatterProfileDMO_%enclosedMass(nodeDescendent,radius)* factorInterpolate &
- & +self_%darkMatterProfileDMO_%enclosedMass(nodeHost ,radius)*(1.0d0-factorInterpolate)
- massRatio =min( &
- & +massRatioMaximum, &
- & max( &
- & +massRatioMinimum, &
- & +massSatellite &
- & /massHost &
- & ) &
- & )
- acceleration =-gravitationalConstantGalacticus &
- & *massHost &
- & *position &
- & /radius **3 &
- & /Mpc_per_km_per_s_To_Gyr
+ nodeDescendent => nodeHost %parent
+ basicDescendent => nodeDescendent%basic ( )
+ massDistributionHost => nodeHost %massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ massDistributionDescendent => nodeDescendent%massDistribution(componentTypeDarkMatterOnly,massTypeDark)
+ radius = Vector_Magnitude(position)
+ factorInterpolate = +(+ time -basicProgenitor%time()) &
+ & /(+basicDescendent%time()-basicProgenitor%time())
+ massHost = +massDistributionDescendent%massEnclosedBySphere(radius)* factorInterpolate &
+ & +massDistributionHost %massEnclosedBySphere(radius)*(1.0d0-factorInterpolate)
+ massRatio = min( &
+ & +massRatioMaximum, &
+ & max( &
+ & +massRatioMinimum, &
+ & +massSatellite &
+ & /massHost &
+ & ) &
+ & )
+ acceleration = -gravitationalConstantGalacticus &
+ & *massHost &
+ & *position &
+ & /radius **3 &
+ & /Mpc_per_km_per_s_To_Gyr
+ !![
+
+
+ !!]
else
! No host exists at this time, assume zero acceleration.
acceleration=0.0d0
@@ -353,22 +334,26 @@ subroutine satelliteOrbitDifferentialEvolution(self,node,interrupt,functionInter
!!{
Perform evolution of a satellite orbit due to its velocity and the acceleration of its host's potential.
!!}
- use :: Galacticus_Nodes , only : nodeComponentSatellite , nodecomponentbasic
+ use :: Galacticus_Nodes , only : nodeComponentSatellite
use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
use :: Vectors , only : Vector_Magnitude
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
implicit none
class (nodeOperatorSatelliteOrbit), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
logical , intent(inout) :: interrupt
procedure (interruptTask ), intent(inout), pointer :: functionInterrupt
integer , intent(in ) :: propertyType
- type (treeNode ), pointer :: nodeHost
+ type (treeNode ) , pointer :: nodeHost
class (nodeComponentSatellite ) , pointer :: satellite
+ class (massDistributionClass ) , pointer :: massDistribution_, massDistributionHost_
double precision , dimension(3) :: position , velocity , &
& acceleration
double precision :: massEnclosedHost , massEnclosedSatellite, &
& radius , massRatio
+ type (coordinateCartesian ) :: coordinates
!$GLC attributes unused :: interrupt, functionInterrupt, propertyType
! Ignore the main branch, and non-satellites unless we are tracking pre-infall orbits.
@@ -399,15 +384,22 @@ subroutine satelliteOrbitDifferentialEvolution(self,node,interrupt,functionInter
! so the velocity remains constant between kicks).
if (.not.node%isSatellite()) return
if (radius <= 0.0d0) return ! If radius is non-positive, assume no acceleration.
- massEnclosedSatellite=max( &
- & 0.0d0 , &
- & min( &
- & self %galacticStructure_%massEnclosed(node ,radius ), &
- & satellite %boundMass ( ) &
- & ) &
- & )
- massEnclosedHost = self %galacticStructure_%massEnclosed(nodeHost,radius )
- acceleration = self %galacticStructure_%acceleration(nodeHost,position)
+ coordinates = position
+ massDistribution_ => node %massDistribution()
+ massDistributionHost_ => nodeHost%massDistribution()
+ massEnclosedSatellite = max( &
+ & 0.0d0 , &
+ & min( &
+ & massDistribution_ %massEnclosedBySphere(radius ), &
+ & satellite %boundMass ( ) &
+ & ) &
+ & )
+ massEnclosedHost = massDistributionHost_%massEnclosedBySphere(radius )
+ acceleration = massDistributionHost_%acceleration (coordinates)
+ !![
+
+
+ !!]
! Include a factor (1+m_{sat}/m_{host})=m_{sat}/µ (where µ is the reduced mass) to convert from the two-body problem of
! satellite and host orbiting their common center of mass to the equivalent one-body problem (since we're solving for the
! motion of the satellite relative to the center of the host which is held fixed).
diff --git a/source/nodes.operators.physics.subsubhalo_promotion.F90 b/source/nodes.operators.physics.subsubhalo_promotion.F90
index 1214eeafd0..53604c6c3f 100644
--- a/source/nodes.operators.physics.subsubhalo_promotion.F90
+++ b/source/nodes.operators.physics.subsubhalo_promotion.F90
@@ -21,8 +21,6 @@
Implements a node operator class that promotes sub-sub-halos.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
-
!![
A node operator class that promotes sub-sub-halos.
@@ -33,9 +31,7 @@
A node operator class that shifts node indices at node promotion.
!!}
private
- class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
contains
- final :: subsubhaloPromotionDestructor
procedure :: differentialEvolution => subsubhaloPromotionDifferentialEvolution
end type nodeOperatorSubsubhaloPromotion
@@ -57,31 +53,22 @@ function subsubhaloPromotionConstructorParameters(parameters) result(self)
implicit none
type (nodeOperatorSubsubhaloPromotion) :: self
type (inputParameters ), intent(inout) :: parameters
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- !![
-
- !!]
- self=nodeOperatorSubsubhaloPromotion(darkMatterProfileDMO_)
+ self=nodeOperatorSubsubhaloPromotion()
!![
-
!!]
return
end function subsubhaloPromotionConstructorParameters
- function subsubhaloPromotionConstructorInternal(darkMatterProfileDMO_) result(self)
+ function subsubhaloPromotionConstructorInternal() result(self)
!!{
Internal constructor for the {\normalfont \ttfamily subsubhaloPromotion} node operator class.
!!}
- use :: Error , only : Component_List , Error_Report
- use :: Galacticus_Nodes , only : defaultSatelliteComponent
+ use:: Error , only : Component_List , Error_Report
+ use:: Galacticus_Nodes, only : defaultSatelliteComponent
implicit none
- type (nodeOperatorSubsubhaloPromotion) :: self
- class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- !![
-
- !!]
+ type(nodeOperatorSubsubhaloPromotion) :: self
if (.not.defaultSatelliteComponent%positionIsGettable()) &
& call Error_Report &
@@ -96,24 +83,13 @@ function subsubhaloPromotionConstructorInternal(darkMatterProfileDMO_) result(se
return
end function subsubhaloPromotionConstructorInternal
- subroutine subsubhaloPromotionDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily subsubhaloPromotion} node operator class.
- !!}
- implicit none
- type(nodeOperatorSubsubhaloPromotion), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine subsubhaloPromotionDestructor
-
subroutine subsubhaloPromotionDifferentialEvolution(self,node,interrupt,functionInterrupt,propertyType)
!!{
Determine if sub-sub-halos should be promoted.
!!}
- use :: Galacticus_Nodes, only : propertyInactive, nodeComponentSatellite
+ use :: Galacticus_Nodes , only : propertyInactive , nodeComponentSatellite
+ use :: Galactic_Structure_Options, only : componentTypeDarkMatterOnly, massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (nodeOperatorSubsubhaloPromotion), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
@@ -121,6 +97,7 @@ subroutine subsubhaloPromotionDifferentialEvolution(self,node,interrupt,function
procedure (interruptTask ), intent(inout), pointer :: functionInterrupt
integer , intent(in ) :: propertyType
class (nodeComponentSatellite ) , pointer :: satellite , satelliteHost
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , dimension(3) :: positionSatellite
double precision :: radiusSatellite , massBoundHost, &
& massEnclosedHost
@@ -136,9 +113,13 @@ subroutine subsubhaloPromotionDifferentialEvolution(self,node,interrupt,function
positionSatellite = satellite%position ()
radiusSatellite = sqrt(sum(positionSatellite**2))
! Determine the mass enclosed by this orbit and the bound mass of the host.
- satelliteHost => node %parent %satellite ( )
- massBoundHost = satelliteHost %boundMass ( )
- massEnclosedHost = self %darkMatterProfileDMO_%enclosedMass(node%parent,radiusSatellite)
+ massDistribution_ => node %parent%massDistribution (componentTypeDarkMatterOnly,massTypeDark)
+ satelliteHost => node %parent%satellite ( )
+ massBoundHost = satelliteHost %boundMass ( )
+ massEnclosedHost = massDistribution_ %massEnclosedBySphere(radiusSatellite )
+ !![
+
+ !!]
! If the satellite is within the radius enclosing the total bound mass of the host it will not be promoted.
if (massEnclosedHost <= massBoundHost) return
! The satellite is outside the current bound radius of the host, so should be promoted. Trigger an interrupt.
diff --git a/source/nodes.property_extractor.CGM_cooling_function.F90 b/source/nodes.property_extractor.CGM_cooling_function.F90
index 4948d4b044..94cee95141 100644
--- a/source/nodes.property_extractor.CGM_cooling_function.F90
+++ b/source/nodes.property_extractor.CGM_cooling_function.F90
@@ -24,11 +24,7 @@ Implements a property extractor class for the CGM cooling function at a set of r
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
use :: Cooling_Functions , only : coolingFunctionClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles , only : hotHaloTemperatureProfileClass
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
- use :: Galactic_Structure , only : galacticStructureClass
-
!![
A property extractor class for the CGM cooling function at a set of radii.
@@ -47,10 +43,7 @@ A property extractor class for the CGM cooling function at a set of radii.
private
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation => null()
integer :: radiiCount , elementCount_ , &
& abundancesCount , chemicalsCount , &
@@ -94,11 +87,8 @@ function cgmCoolingFunctionConstructorParameters(parameters) result(self)
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (coolingFunctionClass ), pointer :: coolingFunction_
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- logical :: includeRadii , includeDensity
+ logical :: includeRadii , includeDensity
type (varying_string ) :: label
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
@@ -126,27 +116,21 @@ function cgmCoolingFunctionConstructorParameters(parameters) result(self)
A label to distinguish this cooling function from others.
-
-
-
-
-
-
+
+
+
!!]
- self=nodePropertyExtractorCGMCoolingFunction(radiusSpecifiers,includeRadii,includeDensity,label,cosmologyFunctions_,darkMatterHaloScale_,galacticStructure_,coolingFunction_,hotHaloTemperatureProfile_,hotHaloMassDistribution_)
+ self=nodePropertyExtractorCGMCoolingFunction(radiusSpecifiers,includeRadii,includeDensity,label,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_)
!![
-
-
-
-
-
-
+
+
+
!!]
return
end function cgmCoolingFunctionConstructorParameters
- function cgmCoolingFunctionConstructorInternal(radiusSpecifiers,includeRadii,includeDensity,label,cosmologyFunctions_,darkMatterHaloScale_,galacticStructure_,coolingFunction_,hotHaloTemperatureProfile_,hotHaloMassDistribution_) result(self)
+ function cgmCoolingFunctionConstructorInternal(radiusSpecifiers,includeRadii,includeDensity,label,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily cgmCoolingFunction} property extractor class.
!!}
@@ -159,14 +143,11 @@ function cgmCoolingFunctionConstructorInternal(radiusSpecifiers,includeRadii,inc
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (coolingFunctionClass ), intent(in ), target :: coolingFunction_
- class (hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- logical , intent(in ) :: includeRadii , includeDensity
+ logical , intent(in ) :: includeRadii , includeDensity
type (varying_string ), intent(in ) :: label
!![
-
+
!!]
! Decode radii specifiers.
@@ -210,13 +191,10 @@ subroutine cgmCoolingFunctionDestructor(self)
type(nodePropertyExtractorCGMCoolingFunction), intent(inout) :: self
!![
-
-
-
-
-
-
-
+
+
+
+
!!]
return
end subroutine cgmCoolingFunctionDestructor
@@ -255,12 +233,15 @@ function cgmCoolingFunctionExtract(self,node,time,instance)
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances
use :: Chemical_Reaction_Rates_Utilities , only : Chemicals_Mass_To_Fraction_Conversion
- use :: Galactic_Structure_Options , only : componentTypeAll , massTypeGalactic , massTypeStellar
+ use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeHotHalo , massTypeGaseous , massTypeGalactic , &
+ & massTypeStellar
use :: Galactic_Structure_Radii_Definitions, only : radiusTypeDarkMatterScaleRadius , radiusTypeDiskHalfMassRadius, radiusTypeDiskRadius , radiusTypeGalacticLightFraction, &
& radiusTypeGalacticMassFraction , radiusTypeRadius , radiusTypeSpheroidHalfMassRadius, radiusTypeSpheroidRadius , &
& radiusTypeStellarMassFraction , radiusTypeVirialRadius
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode , &
& nodeComponentBasic , nodeComponentHotHalo
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Numerical_Constants_Astronomical , only : massSolar , megaParsec
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Prefixes , only : hecto
@@ -276,6 +257,9 @@ function cgmCoolingFunctionExtract(self,node,time,instance)
class (nodeComponentDisk ) , pointer :: disk
class (nodeComponentSpheroid ) , pointer :: spheroid
class (nodeComponentDarkMatterProfile ) , pointer :: darkMatterProfile
+ class (massDistributionClass ) , pointer :: massDistribution_
+ class (kinematicsDistributionClass ) , pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
integer :: i
double precision :: radius , radiusVirial , &
& density , temperature , &
@@ -309,27 +293,33 @@ function cgmCoolingFunctionExtract(self,node,time,instance)
radius=+radius*spheroid %halfMassRadius()
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case (radiusTypeStellarMassFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
@@ -356,9 +346,16 @@ function cgmCoolingFunctionExtract(self,node,time,instance)
! Convert to number density per unit total mass density.
call fractionsChemical%scale(massToDensityConversion)
end if
- ! Get density and temperature at the required radius.
- density =self%hotHaloMassDistribution_ %density (node,radius)
- temperature =self%hotHaloTemperatureProfile_%temperature(node,radius)
+ ! Get density and temperature.
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ density = massDistribution_ %density (coordinates )
+ temperature = kinematicsDistribution_%temperature (coordinates )
+ !![
+
+
+ !!]
! Compute number density of hydrogen (in cm⁻³).
numberDensityHydrogen=+density &
& *abundancesGas %hydrogenMassFraction() &
diff --git a/source/nodes.property_extractor.ICM_SZ.F90 b/source/nodes.property_extractor.ICM_SZ.F90
index abe37e884c..b974084dbe 100644
--- a/source/nodes.property_extractor.ICM_SZ.F90
+++ b/source/nodes.property_extractor.ICM_SZ.F90
@@ -20,13 +20,10 @@
!!{
Contains a module which implements an intracluster medium Sunyaev-Zeldovich Compton-y parameter property extractor class.
!!}
- use :: Chemical_States , only : chemicalState , chemicalStateClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass , enumerationDensityCosmologicalType
- use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistribution , hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfile, hotHaloTemperatureProfileClass
+ use :: Chemical_States , only : chemicalState , chemicalStateClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass , enumerationDensityCosmologicalType
+ use :: Cosmology_Parameters , only : cosmologyParameters, cosmologyParametersClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScale, darkMatterHaloScaleClass
!![
@@ -60,10 +57,7 @@
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_ => null()
class (chemicalStateClass ), pointer :: chemicalState_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (nodePropertyExtractorDensityContrasts), pointer :: densityContrastExtractor_ => null()
double precision :: densityContrast , distanceAngular
logical :: useDensityContrast , useFixedDistance
@@ -94,26 +88,20 @@ function icmSZConstructorParameters(parameters) result(self)
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Cosmology_Functions, only : enumerationDensityCosmologicalEncode
implicit none
- type (nodePropertyExtractorICMSZ ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_
- class (chemicalStateClass ), pointer :: chemicalState_
- class (galacticStructureClass ), pointer :: galacticStructure_
- double precision :: densityContrast , distanceAngular
- type (varying_string ) :: densityContrastRelativeTo
+ type (nodePropertyExtractorICMSZ) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ class (cosmologyParametersClass ), pointer :: cosmologyParameters_
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class (chemicalStateClass ), pointer :: chemicalState_
+ double precision :: densityContrast , distanceAngular
+ type (varying_string ) :: densityContrastRelativeTo
!![
-
-
-
-
-
-
-
+
+
+
+
!!]
if (parameters%isPresent('densityContrast')) then
!![
@@ -141,24 +129,21 @@ function icmSZConstructorParameters(parameters) result(self)
end if
!![
- self=nodePropertyExtractorICMSZ(cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,chemicalState_,galacticStructure_{conditions})
+ self=nodePropertyExtractorICMSZ(cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,chemicalState_{conditions})
-
-
-
-
-
-
-
+
+
+
+
!!]
return
end function icmSZConstructorParameters
- function icmSZConstructorInternal(cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,chemicalState_,galacticStructure_,densityContrast,densityContrastRelativeTo,distanceAngular) result(self)
+ function icmSZConstructorInternal(cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,chemicalState_,densityContrast,densityContrastRelativeTo,distanceAngular) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily icmSZ} property extractor class.
!!}
@@ -170,15 +155,12 @@ function icmSZConstructorInternal(cosmologyParameters_,cosmologyFunctions_,darkM
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class (chemicalStateClass ), intent(in ), target :: chemicalState_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ), optional :: densityContrast , distanceAngular
type (enumerationDensityCosmologicalType), intent(in ), optional :: densityContrastRelativeTo
character (len=8 ) :: label
!![
-
+
!!]
self%useDensityContrast=present(densityContrast)
@@ -195,8 +177,7 @@ function icmSZConstructorInternal(cosmologyParameters_,cosmologyFunctions_,darkM
& densityContrastRelativeTo=densityContrastRelativeTo, &
& cosmologyparameters_ =cosmologyParameters_ , &
& cosmologyFunctions_ =cosmologyFunctions_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & galacticStructure_ =galacticStructure_ &
+ & darkMatterHaloScale_ =darkMatterHaloScale_ &
& )
@@ -217,13 +198,10 @@ subroutine icmSZDestructor(self)
type(nodePropertyExtractorICMSZ), intent(inout) :: self
!![
-
-
-
-
-
-
-
+
+
+
+
!!]
if (self%useDensityContrast) then
!![
@@ -295,21 +273,35 @@ double precision function integrandComptonY(radius)
Integrand function used for computing ICM SZ properties.
!!}
use :: Abundances_Structure , only : abundances
- use :: Numerical_Constants_Astronomical, only : massSolar , megaParsec
+ use :: Numerical_Constants_Astronomical, only : massSolar , megaParsec
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
- use :: Numerical_Constants_Physical , only : boltzmannsConstant, electronMass, speedLight, thomsonCrossSection
- use :: Numerical_Constants_Prefixes , only : centi , hecto
+ use :: Numerical_Constants_Physical , only : boltzmannsConstant , electronMass , speedLight, thomsonCrossSection
+ use :: Numerical_Constants_Prefixes , only : centi , hecto
+ use :: Mass_Distributions , only : massDistributionClass, kinematicsDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo), pointer :: hotHalo
- double precision :: density , temperature, &
- & numberDensityHydrogen, massICM
- type (abundances ) :: abundancesICM
+ double precision , intent(in ) :: radius
+ class (nodeComponentHotHalo ), pointer :: hotHalo
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass), pointer :: kinematicsDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: density , temperature, &
+ & numberDensityHydrogen , massICM
+ type (abundances ) :: abundancesICM
+ ! Get the mass distribution.
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
! Get the density of the ICM.
- density = self %hotHaloMassDistribution_ %density (node,radius)
+ density = massDistribution_ %density (coordinates )
! Get the temperature of the ICM.
- temperature = self %hotHaloTemperatureProfile_%temperature(node,radius)
+ temperature = kinematicsDistribution_%temperature (coordinates )
+ !![
+
+
+ !!]
! Get abundances and chemistry of the ICM.
hotHalo => node %hotHalo ()
massICM = hotHalo%mass ()
diff --git a/source/nodes.property_extractor.ICM_Xray_luminosity.F90 b/source/nodes.property_extractor.ICM_Xray_luminosity.F90
index 35a2695ea4..034aeda67a 100644
--- a/source/nodes.property_extractor.ICM_Xray_luminosity.F90
+++ b/source/nodes.property_extractor.ICM_Xray_luminosity.F90
@@ -21,11 +21,9 @@
Contains a module which implements an intracluster medium X-ray luminosity property extractor class.
!!}
- use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistribution , hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfile, hotHaloTemperatureProfileClass
+ use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScale, darkMatterHaloScaleClass
!![
@@ -37,11 +35,9 @@
A icmXRayLuminosity property extractor class.
!!}
private
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class(coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class(coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
contains
final :: icmXRayLuminosityDestructor
procedure :: extract => icmXRayLuminosityExtract
@@ -69,31 +65,25 @@ function icmXRayLuminosityConstructorParameters(parameters) result(self)
type (nodePropertyExtractorICMXRayLuminosity) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(coolingFunctionClass ), pointer :: coolingFunction_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
!![
-
-
-
-
-
+
+
+
!!]
- self=nodePropertyExtractorICMXRayLuminosity(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_)
+ self=nodePropertyExtractorICMXRayLuminosity(cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_)
!![
-
-
-
-
-
+
+
+
!!]
return
end function icmXRayLuminosityConstructorParameters
- function icmXRayLuminosityConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_) result(self)
+ function icmXRayLuminosityConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily icmXRayLuminosity} property extractor class.
!!}
@@ -101,11 +91,9 @@ function icmXRayLuminosityConstructorInternal(cosmologyFunctions_,darkMatterHalo
type (nodePropertyExtractorICMXRayLuminosity) :: self
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class(coolingFunctionClass ), intent(in ), target :: coolingFunction_
!![
-
+
!!]
return
@@ -119,11 +107,9 @@ subroutine icmXRayLuminosityDestructor(self)
type(nodePropertyExtractorICMXRayLuminosity), intent(inout) :: self
!![
-
-
-
-
-
+
+
+
!!]
return
end subroutine icmXRayLuminosityDestructor
@@ -138,11 +124,15 @@ double precision function icmXRayLuminosityExtract(self,node,instance)
use :: Numerical_Constants_Units , only : electronVolt
use :: Numerical_Integration , only : integrator
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class(nodePropertyExtractorICMXRayLuminosity ), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation_
+ class(massDistributionClass ), pointer :: massDistribution_
+ class(kinematicsDistributionClass ), pointer :: kinematicsDistribution_
type (integrator ) :: integratorLuminosity
!$GLC attributes unused :: self, instance
@@ -151,11 +141,16 @@ double precision function icmXRayLuminosityExtract(self,node,instance)
!![
!!]
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute luminosity and temperature.
integratorLuminosity =integrator (integrandLuminosityXray ,toleranceRelative =1.0d-3)
icmXRayLuminosityExtract=integratorLuminosity %integrate(0.0d0 ,self%darkMatterHaloScale_%radiusVirial(node) )
!![
-
+
+
+
!!]
return
@@ -172,6 +167,7 @@ Integrand function used for computing ICM X-ray luminosities.
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : centi , hecto
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
double precision , intent(in ) :: radius
class (nodeComponentHotHalo), pointer :: hotHalo
@@ -180,11 +176,14 @@ Integrand function used for computing ICM X-ray luminosities.
& massToDensityConversion
type (abundances ) :: abundancesICM
type (chemicalAbundances ) :: massChemicalICM , fractionChemicalICM
+ type (coordinateSpherical ) :: coordinates
- ! Get the density of the ICM.
- density =self%hotHaloMassDistribution_ %density (node,radius)
+ ! Set the coordinates.
+ coordinates = [radius,0.0d0,0.0d0]
+ ! Get the density of the ICM.
+ density = massDistribution_ %density (coordinates)
! Get the temperature of the ICM.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,radius)
+ temperature = kinematicsDistribution_%temperature(coordinates)
! Get abundances and chemistry of the ICM.
hotHalo => node %hotHalo ()
massICM = hotHalo%mass ()
diff --git a/source/nodes.property_extractor.ICM_Xray_temperature.F90 b/source/nodes.property_extractor.ICM_Xray_temperature.F90
index a60bb4faba..86f6048db7 100644
--- a/source/nodes.property_extractor.ICM_Xray_temperature.F90
+++ b/source/nodes.property_extractor.ICM_Xray_temperature.F90
@@ -21,11 +21,9 @@
Contains a module which implements an intracluster medium X-ray luminosity-weighted temperature property extractor class.
!!}
- use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistribution , hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfile, hotHaloTemperatureProfileClass
+ use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScale, darkMatterHaloScaleClass
!![
@@ -37,11 +35,9 @@
An ICM luminosity-weighted temperature property extractor class.
!!}
private
- class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class(hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class(coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class(coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
contains
final :: icmXRayTemperatureDestructor
procedure :: extract => icmXRayTemperatureExtract
@@ -69,31 +65,25 @@ function icmXRayTemperatureConstructorParameters(parameters) result(self)
type (nodePropertyExtractorICMXRayTemperature) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(coolingFunctionClass ), pointer :: coolingFunction_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
!![
-
-
-
-
-
+
+
+
!!]
- self=nodePropertyExtractorICMXRayTemperature(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_)
+ self=nodePropertyExtractorICMXRayTemperature(cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_)
!![
-
-
-
-
-
+
+
+
!!]
return
end function icmXRayTemperatureConstructorParameters
- function icmXRayTemperatureConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_) result(self)
+ function icmXRayTemperatureConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily icmXRayTemperature} property extractor class.
!!}
@@ -101,11 +91,9 @@ function icmXRayTemperatureConstructorInternal(cosmologyFunctions_,darkMatterHal
type (nodePropertyExtractorICMXRayTemperature) :: self
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class(coolingFunctionClass ), intent(in ), target :: coolingFunction_
!![
-
+
!!]
return
@@ -119,11 +107,9 @@ subroutine icmXRayTemperatureDestructor(self)
type(nodePropertyExtractorICMXRayTemperature), intent(inout) :: self
!![
-
-
-
-
-
+
+
+
!!]
return
end subroutine icmXRayTemperatureDestructor
@@ -138,13 +124,17 @@ double precision function icmXRayTemperatureExtract(self,node,instance)
use :: Numerical_Constants_Units , only : electronVolt
use :: Numerical_Integration , only : integrator
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class (nodePropertyExtractorICMXRayTemperature), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
type (radiationFieldCosmicMicrowaveBackground), pointer :: radiation_
- type (integrator ) :: integratorLuminosity, integratorTemperature
- double precision :: luminosity , temperature
+ type (integrator ) :: integratorLuminosity , integratorTemperature
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ double precision :: luminosity , temperature
!$GLC attributes unused :: self, instance
! Initialize radiation field.
@@ -152,6 +142,9 @@ double precision function icmXRayTemperatureExtract(self,node,instance)
!![
!!]
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute luminosity and temperature.
integratorLuminosity =integrator (integrandLuminosityXray ,toleranceRelative =1.0d-3)
integratorTemperature=integrator (integrandTemperatureXray,toleranceRelative =1.0d-3)
@@ -168,7 +161,9 @@ double precision function icmXRayTemperatureExtract(self,node,instance)
temperature=+0.0d0
end if
!![
-
+
+
+
!!]
icmXRayTemperatureExtract=temperature
return
@@ -186,19 +181,23 @@ Integrand function used for computing ICM X-ray luminosities.
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : centi , hecto
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
double precision , intent(in ) :: radius
class (nodeComponentHotHalo), pointer :: hotHalo
+ type (coordinateSpherical ) :: coordinates
double precision :: density , temperature , &
& numberDensityHydrogen , massICM , &
& massToDensityConversion
type (abundances ) :: abundancesICM
type (chemicalAbundances ) :: massChemicalICM , fractionChemicalICM
+ ! Set the coordinates.
+ coordinates = [radius,0.0d0,0.0d0]
! Get the density of the ICM.
- density =self%hotHaloMassDistribution_ %density (node,radius)
+ density = massDistribution_ %density (coordinates)
! Get the temperature of the ICM.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,radius)
+ temperature = kinematicsDistribution_%temperature(coordinates)
! Get abundances and chemistry of the ICM.
hotHalo => node %hotHalo ()
massICM = hotHalo%mass ()
@@ -237,11 +236,14 @@ double precision function integrandTemperatureXray(radius)
!!{
Integrand function used for computing ICM X-ray luminosity-weighted temperatures.
!!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
implicit none
- double precision, intent(in ) :: radius
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
- integrandTemperatureXray=+integrandLuminosityXray ( radius) &
- & *self%hotHaloTemperatureProfile_%temperature(node,radius)
+ coordinates =[radius,0.0d0,0.0d0]
+ integrandTemperatureXray=+integrandLuminosityXray ( radius) &
+ & *kinematicsDistribution_%temperature(coordinates)
return
end function integrandTemperatureXray
diff --git a/source/nodes.property_extractor.ICM_cooling_power_in_band.F90 b/source/nodes.property_extractor.ICM_cooling_power_in_band.F90
index d72a86823b..08b66aa020 100644
--- a/source/nodes.property_extractor.ICM_cooling_power_in_band.F90
+++ b/source/nodes.property_extractor.ICM_cooling_power_in_band.F90
@@ -21,11 +21,9 @@
Contains a module which implements an intracluster medium cooling power in band property extractor class.
!!}
- use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistribution , hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfile, hotHaloTemperatureProfileClass
+ use :: Cooling_Functions , only : coolingFunction , coolingFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScale, darkMatterHaloScaleClass
!![
@@ -37,13 +35,11 @@
A property extractor class which extracts the fraction of the ICM cooling power due to emission in a given energy band.
!!}
private
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- double precision :: energyLow , energyHigh
- type (varying_string ) :: label
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ double precision :: energyLow , energyHigh
+ type (varying_string ) :: label
contains
final :: icmCoolingPowerInBandDestructor
procedure :: extract => icmCoolingPowerInBandExtract
@@ -71,11 +67,9 @@ function icmCoolingPowerInBandConstructorParameters(parameters) result(self)
type (nodePropertyExtractorICMCoolingPowerInBand) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class (coolingFunctionClass ), pointer :: coolingFunction_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- double precision :: energyLow , energyHigh
+ double precision :: energyLow , energyHigh
type (varying_string ) :: label
@@ -95,39 +89,33 @@ function icmCoolingPowerInBandConstructorParameters(parameters) result(self)
A label to use as a suffix for this property.
-
-
-
-
-
+
+
+
!!]
- self=nodePropertyExtractorICMCoolingPowerInBand(energyLow,energyHigh,label,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_)
+ self=nodePropertyExtractorICMCoolingPowerInBand(energyLow,energyHigh,label,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_)
!![
-
-
-
-
-
+
+
+
!!]
return
end function icmCoolingPowerInBandConstructorParameters
- function icmCoolingPowerInBandConstructorInternal(energyLow,energyHigh,label,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_) result(self)
+ function icmCoolingPowerInBandConstructorInternal(energyLow,energyHigh,label,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily icmCoolingPowerInBand} property extractor class.
!!}
implicit none
type (nodePropertyExtractorICMCoolingPowerInBand) :: self
- double precision , intent(in ) :: energyLow , energyHigh
+ double precision , intent(in ) :: energyLow , energyHigh
type (varying_string ), intent(in ) :: label
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class (coolingFunctionClass ), intent(in ), target :: coolingFunction_
!![
-
+
!!]
return
@@ -141,11 +129,9 @@ subroutine icmCoolingPowerInBandDestructor(self)
type(nodePropertyExtractorICMCoolingPowerInBand), intent(inout) :: self
!![
-
-
-
-
-
+
+
+
!!]
return
end subroutine icmCoolingPowerInBandDestructor
@@ -159,13 +145,17 @@ double precision function icmCoolingPowerInBandExtract(self,node,instance)
use :: Numerical_Constants_Units , only : electronVolt
use :: Numerical_Integration , only : integrator
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class (nodePropertyExtractorICMCoolingPowerInBand), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
type (radiationFieldCosmicMicrowaveBackground ), pointer :: radiation_
- type (integrator ) :: integratorTotal, integratorInBand
- double precision :: luminosityTotal, luminosityInBand
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ type (integrator ) :: integratorTotal , integratorInBand
+ double precision :: luminosityTotal , luminosityInBand
!$GLC attributes unused :: instance
! Initialize radiation field.
@@ -173,6 +163,9 @@ double precision function icmCoolingPowerInBandExtract(self,node,instance)
!![
!!]
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute luminosity and temperature.
integratorTotal =integrator (integrandLuminosityTotal ,toleranceRelative =1.0d-3)
integratorInBand=integrator (integrandLuminosityInBand,toleranceRelative =1.0d-3)
@@ -185,7 +178,9 @@ double precision function icmCoolingPowerInBandExtract(self,node,instance)
icmCoolingPowerInBandExtract=+0.0d0
end if
!![
-
+
+
+
!!]
return
@@ -238,6 +233,7 @@ subroutine icmProperties(radius,numberDensityHydrogen,temperature,abundancesICM,
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Prefixes , only : hecto
use :: Numerical_Constants_Astronomical , only : massSolar , megaParsec
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
double precision , intent(in ) :: radius
double precision , intent( out) :: numberDensityHydrogen , temperature
@@ -245,13 +241,16 @@ subroutine icmProperties(radius,numberDensityHydrogen,temperature,abundancesICM,
type (chemicalAbundances ), intent( out) :: densityChemicalICM
class (nodeComponentHotHalo), pointer :: hotHalo
type (chemicalAbundances ) :: massChemicalICM
+ type (coordinateSpherical ) :: coordinates
double precision :: density , massICM , &
& massToDensityConversion
+ ! Set the coordinates.
+ coordinates = [radius,0.0d0,0.0d0]
! Get the density of the ICM.
- density =self%hotHaloMassDistribution_ %density (node,radius)
+ density = massDistribution_ %density (coordinates)
! Get the temperature of the ICM.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,radius)
+ temperature = kinematicsDistribution_%temperature(coordinates)
! Get abundances and chemistry of the ICM.
hotHalo => node %hotHalo ()
massICM = hotHalo%mass ()
diff --git a/source/nodes.property_extractor.ICM_optical_depth_LymanAlpha.F90 b/source/nodes.property_extractor.ICM_optical_depth_LymanAlpha.F90
index 577c8d729f..d280adcd18 100644
--- a/source/nodes.property_extractor.ICM_optical_depth_LymanAlpha.F90
+++ b/source/nodes.property_extractor.ICM_optical_depth_LymanAlpha.F90
@@ -19,22 +19,20 @@
!% Contains a module which implements an intracluster medium cooling power in band property extractor class.
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistribution , hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfile, hotHaloTemperatureProfileClass
-
- !#
- !# An intracluster medium cooling power in band property extractor class.
- !#
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScale, darkMatterHaloScaleClass
+
+ !![
+
+ An intracluster medium cooling power in band property extractor class.
+
+ !!]
type, extends(nodePropertyExtractorScalar) :: nodePropertyExtractorICMOpticalDepthLymanAlpha
!% A property extractor class which extracts the fraction of the ICM cooling power due to emission in a given energy band.
private
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- integer :: speciesHydrogenNeutral , speciesHydrogenIonized
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ integer :: speciesHydrogenNeutral , speciesHydrogenIonized
contains
final :: icmOpticalDepthLymanAlphaDestructor
procedure :: extract => icmOpticalDepthLymanAlphaExtract
@@ -58,34 +56,31 @@ function icmOpticalDepthLymanAlphaConstructorParameters(parameters) result(self)
type (nodePropertyExtractorICMOpticalDepthLymanAlpha) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- !#
- !#
- !#
- !#
- self=nodePropertyExtractorICMOpticalDepthLymanAlpha(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_)
- !#
- !#
- !#
- !#
- !#
+ !![
+
+
+ !!]
+ self=nodePropertyExtractorICMOpticalDepthLymanAlpha(cosmologyFunctions_,darkMatterHaloScale_)
+ !![
+
+
+
+ !!]
return
end function icmOpticalDepthLymanAlphaConstructorParameters
- function icmOpticalDepthLymanAlphaConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_) result(self)
+ function icmOpticalDepthLymanAlphaConstructorInternal(cosmologyFunctions_,darkMatterHaloScale_) result(self)
!% Internal constructor for the {\normalfont \ttfamily icmOpticalDepthLymanAlpha} property extractor class.
use :: Chemical_Abundances_Structure, only : Chemicals_Index
implicit none
type (nodePropertyExtractorICMOpticalDepthLymanAlpha) :: self
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class(hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
- !#
-
+ !![
+
+ !!]
self%speciesHydrogenNeutral=Chemicals_Index("AtomicHydrogen" )
self%speciesHydrogenIonized=Chemicals_Index("AtomicHydrogenCation")
@@ -97,10 +92,10 @@ subroutine icmOpticalDepthLymanAlphaDestructor(self)
implicit none
type(nodePropertyExtractorICMOpticalDepthLymanAlpha), intent(inout) :: self
- !#
- !#
- !#
- !#
+ !![
+
+
+ !!]
return
end subroutine icmOpticalDepthLymanAlphaDestructor
@@ -111,21 +106,34 @@ double precision function icmOpticalDepthLymanAlphaExtract(self,node,instance)
use :: Numerical_Constants_Units , only : electronVolt
use :: Numerical_Integration , only : integrator
use :: Radiation_Fields , only : radiationFieldCosmicMicrowaveBackground
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
class(nodePropertyExtractorICMOpticalDepthLymanAlpha), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
type (radiationFieldCosmicMicrowaveBackground ), pointer :: radiation_
+ class(massDistributionClass ), pointer :: massDistribution_
+ class(kinematicsDistributionClass ), pointer :: kinematicsDistribution_
type (integrator ) :: integrator_
!$GLC attributes unused :: instance
! Initialize radiation field.
allocate(radiation_)
- !#
+ !![
+
+ !!]
+ ! Get the mass distribution.
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
! Compute luminosity and temperature.
integrator_ =integrator (integrandOpticalDepth,toleranceRelative =1.0d-3)
icmOpticalDepthLymanAlphaExtract=integrator_%integrate(0.0d0 ,self%darkMatterHaloScale_%radiusVirial(node) )
- !#
+ !![
+
+
+
+ !!]
return
contains
@@ -182,6 +190,7 @@ subroutine icmProperties(radius,numberDensityHydrogen,temperature,abundancesICM,
use :: Numerical_Constants_Atomic , only : massHydrogenAtom
use :: Numerical_Constants_Prefixes , only : hecto
use :: Numerical_Constants_Astronomical , only : massSolar , megaParsec
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
double precision , intent(in ) :: radius
double precision , intent( out) :: numberDensityHydrogen , temperature
@@ -189,13 +198,16 @@ subroutine icmProperties(radius,numberDensityHydrogen,temperature,abundancesICM,
type (chemicalAbundances ), intent( out) :: densityChemicalICM
class (nodeComponentHotHalo), pointer :: hotHalo
type (chemicalAbundances ) :: massChemicalICM
+ type (coordinateSpherical ) :: coordinates
double precision :: density , massICM , &
& massToDensityConversion
+ ! Set the coordinates.
+ coordinates = [radius,0.0d0,0.0d0]
! Get the density of the ICM.
- density =self%hotHaloMassDistribution_ %density (node,radius)
+ density = massDistribution_ %density (coordinates)
! Get the temperature of the ICM.
- temperature=self%hotHaloTemperatureProfile_%temperature(node,radius)
+ temperature = kinematicsDistribution_%temperature(coordinates)
! Get abundances and chemistry of the ICM.
hotHalo => node %hotHalo ()
massICM = hotHalo%mass ()
diff --git a/source/nodes.property_extractor.bound_mass_radius.F90 b/source/nodes.property_extractor.bound_mass_radius.F90
index 6ee17bcf66..cd05e19193 100644
--- a/source/nodes.property_extractor.bound_mass_radius.F90
+++ b/source/nodes.property_extractor.bound_mass_radius.F90
@@ -21,8 +21,6 @@
Contains a module which implements a property extractor class that extracts the radius enclosing the current bound mass.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
@@ -35,9 +33,7 @@
A property extractor class that extracts the radius enclosing the current bound mass.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: radiusBoundMassDestructor
procedure :: extract => radiusBoundMassExtract
procedure :: name => radiusBoundMassName
procedure :: description => radiusBoundMassDescription
@@ -49,7 +45,6 @@
Constructors for the ``radiusBoundMass'' output analysis class.
!!}
module procedure radiusBoundMassConstructorParameters
- module procedure radiusBoundMassConstructorInternal
end interface nodePropertyExtractorRadiusBoundMass
contains
@@ -62,63 +57,41 @@ function radiusBoundMassConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorRadiusBoundMass) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorRadiusBoundMass(galacticStructure_)
+ self=nodePropertyExtractorRadiusBoundMass()
!![
-
!!]
return
end function radiusBoundMassConstructorParameters
- function radiusBoundMassConstructorInternal(galacticStructure_) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily radiusBoundMass} property extractor class which takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type (nodePropertyExtractorRadiusBoundMass) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function radiusBoundMassConstructorInternal
-
- subroutine radiusBoundMassDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily radiusBoundMass} property extractor class which takes a parameter set as input.
- !!}
- implicit none
- type(nodePropertyExtractorRadiusBoundMass), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine radiusBoundMassDestructor
-
double precision function radiusBoundMassExtract(self,node,instance)
!!{
Implement a bound mass radius property extractor.
!!}
- use :: Galacticus_Nodes, only : nodeComponentSatellite
+ use :: Galacticus_Nodes , only : nodeComponentSatellite
+ use :: Galactic_Structure_Options, only : radiusLarge
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (nodePropertyExtractorRadiusBoundMass), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
class (nodeComponentSatellite ) , pointer :: satellite
+ class (massDistributionClass ) , pointer :: massDistribution_
+ double precision , parameter :: toleranceRelative=1.0d-6
double precision :: massTotal
!$GLC attributes unused :: instance
- satellite => node%satellite()
- massTotal = self%galacticStructure_%massEnclosed (node )
- radiusBoundMassExtract = self%galacticStructure_%radiusEnclosingMass(node,mass=min(satellite%boundMass(),massTotal))
+ ! Find the radius enclosing the bound mass. Limit the bound mass to that enclosed within some very large radius. Additionally,
+ ! seek the radius enclosing a fraction 1-10⁻⁶ of the bound mass to avoid problems with numerical precision when solving
+ ! numerically for this radius.
+ satellite => node %satellite ( )
+ massDistribution_ => node %massDistribution ( )
+ massTotal = massDistribution_%massEnclosedBySphere(radius= radiusLarge)
+ radiusBoundMassExtract = massDistribution_%radiusEnclosingMass (mass =(1.0d0-toleranceRelative)*min(satellite%boundMass(),massTotal ))
+ !![
+
+ !!]
return
end function radiusBoundMassExtract
diff --git a/source/nodes.property_extractor.concentration.F90 b/source/nodes.property_extractor.concentration.F90
index d7e08665ba..52bef1b3dd 100644
--- a/source/nodes.property_extractor.concentration.F90
+++ b/source/nodes.property_extractor.concentration.F90
@@ -21,10 +21,9 @@
Contains a module which implements a concentration output analysis property extractor class.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -42,7 +41,6 @@
private
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
logical :: useLastIsolatedTime
contains
@@ -73,7 +71,6 @@ function concentrationConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
logical :: useLastIsolatedTime
@@ -86,23 +83,21 @@ function concentrationConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorConcentration(useLastIsolatedTime,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=nodePropertyExtractorConcentration(useLastIsolatedTime,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,virialDensityContrastDefinition_)
!![
-
!!]
return
end function concentrationConstructorParameters
- function concentrationConstructorInternal(useLastIsolatedTime,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
+ function concentrationConstructorInternal(useLastIsolatedTime,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
!!{
Internal constructor for the ``concentration'' output analysis property extractor class.
!!}
@@ -110,11 +105,10 @@ function concentrationConstructorInternal(useLastIsolatedTime,cosmologyParameter
type (nodePropertyExtractorConcentration) :: self
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_, virialDensityContrastDefinition_
logical , intent(in ) :: useLastIsolatedTime
!![
-
+
!!]
return
@@ -130,7 +124,6 @@ subroutine concentrationDestructor(self)
!![
-
!!]
@@ -166,7 +159,6 @@ double precision function concentrationExtract(self,node,instance)
& radius = radiusHalo , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ , &
& useLastIsolatedTime =self%useLastIsolatedTime &
& )
diff --git a/source/nodes.property_extractor.dark_matter_profile.SIDM_interaction_radius.F90 b/source/nodes.property_extractor.dark_matter_profile.SIDM_interaction_radius.F90
index 55713355de..a7dc989a59 100644
--- a/source/nodes.property_extractor.dark_matter_profile.SIDM_interaction_radius.F90
+++ b/source/nodes.property_extractor.dark_matter_profile.SIDM_interaction_radius.F90
@@ -103,23 +103,25 @@ double precision function darkMatterProfileRadiusInteractionSIDMExtract(self,nod
!!{
Implement a {\normalfont \ttfamily darkMatterProfileRadiusInteractionSIDM} output analysis.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOSIDM
- use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions, only : massDistributionSphericalSIDM, massDistributionClass
implicit none
class(nodePropertyExtractorDarkMatterProfileRadiusInteractionSIDM), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
- class(nodeComponentBasic ), pointer :: basic
+ class(massDistributionClass ), pointer :: massDistribution_
!$GLC attributes unused :: instance
- select type (darkMatterProfileDMO_ => self%darkMatterProfileDMO_)
- class is (darkMatterProfileDMOSIDM)
- basic => node %basic ( )
- darkMatterProfileRadiusInteractionSIDMExtract = darkMatterProfileDMO_%radiusInteraction(node,basic%time())
+ massDistribution_ => self%darkMatterProfileDMO_%get(node)
+ select type (massDistribution_)
+ class is (massDistributionSphericalSIDM)
+ darkMatterProfileRadiusInteractionSIDMExtract=massDistribution_%radiusInteraction()
class default
- darkMatterProfileRadiusInteractionSIDMExtract = 0.0d0
+ darkMatterProfileRadiusInteractionSIDMExtract=0.0d0
end select
return
+ !![
+
+ !!]
end function darkMatterProfileRadiusInteractionSIDMExtract
function darkMatterProfileRadiusInteractionSIDMName(self)
diff --git a/source/nodes.property_extractor.density.F90 b/source/nodes.property_extractor.density.F90
index 2feae326a2..d275f04621 100644
--- a/source/nodes.property_extractor.density.F90
+++ b/source/nodes.property_extractor.density.F90
@@ -22,7 +22,6 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -35,7 +34,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii
type (varying_string ), allocatable, dimension(:) :: radiusSpecifiers
@@ -74,7 +72,6 @@ function densityProfileConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: includeRadii
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
@@ -91,18 +88,16 @@ function densityProfileConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorDensityProfile(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorDensityProfile(radiusSpecifiers,includeRadii,darkMatterHaloScale_)
!![
-
!!]
return
end function densityProfileConstructorParameters
- function densityProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_) result(self)
+ function densityProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily densityProfile} property extractor class.
!!}
@@ -111,10 +106,9 @@ function densityProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMat
type (nodePropertyExtractorDensityProfile) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: includeRadii
!![
-
+
!!]
if (includeRadii) then
@@ -144,7 +138,6 @@ subroutine densityProfileDestructor(self)
!![
-
!!]
return
end subroutine densityProfileDestructor
@@ -185,6 +178,8 @@ function densityProfileExtract(self,node,time,instance)
& radiusTypeGalacticMassFraction , radiusTypeRadius , radiusTypeSpheroidHalfMassRadius, radiusTypeSpheroidRadius , &
& radiusTypeStellarMassFraction , radiusTypeVirialRadius
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Numerical_Constants_Math , only : Pi
use :: Error , only : Error_Report
implicit none
@@ -196,6 +191,8 @@ function densityProfileExtract(self,node,time,instance)
class (nodeComponentDisk ), pointer :: disk
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
integer :: i
double precision :: radius , radiusVirial
!$GLC attributes unused :: time, instance
@@ -225,42 +222,49 @@ function densityProfileExtract(self,node,time,instance)
radius=+radius*spheroid %halfMassRadius()
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
- case (radiusTypeStellarMassFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
+ case (radiusTypeStellarMassFraction %ID)
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
- densityProfileExtract (i,1)=self%galacticStructure_%density( &
- & node , &
- & [ &
- & radius , &
- & Pi/2.0d0 , &
- & 0.0d0 &
- & ] , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
- if (self%includeRadii) &
- & densityProfileExtract(i,2)= radius
+ coordinates=[radius,Pi/2.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(&
+ & componentType=self%radii(i)%component , &
+ & massType =self%radii(i)%mass &
+ & )
+ densityProfileExtract (i,1) = massDistribution_%density ( &
+ & coordinates= coordinates &
+ & )
+ if (self%includeRadii) &
+ & densityProfileExtract(i,2) = radius
+ !![
+
+ !!]
end do
return
end function densityProfileExtract
diff --git a/source/nodes.property_extractor.density_contrasts.F90 b/source/nodes.property_extractor.density_contrasts.F90
index 5c4795b27f..635ddade4e 100644
--- a/source/nodes.property_extractor.density_contrasts.F90
+++ b/source/nodes.property_extractor.density_contrasts.F90
@@ -24,9 +24,8 @@
use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass , enumerationDensityCosmologicalType
use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
- use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
- use :: Galactic_Structure , only : galacticStructureClass
use :: Galactic_Structure_Options, only : enumerationMassTypeType
+ use :: Mass_Distributions , only : massDistributionClass
use :: Root_Finder , only : rootFinder
!![
@@ -51,7 +50,6 @@
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (rootFinder ) :: finder
integer :: elementCount_ , countDensityContrasts
type (enumerationMassTypeType ) :: massTypeSelected
@@ -78,11 +76,9 @@
end interface nodePropertyExtractorDensityContrasts
! Module-scope variables used in root finding.
- type (treeNode ), pointer :: node_
- class (nodeComponentBasic ), pointer :: basic_
- class (nodePropertyExtractorDensityContrasts), pointer :: self_
- double precision :: densityTarget
- !$omp threadprivate(node_,basic_,self_,densityTarget)
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: densityTarget
+ !$omp threadprivate(massDistribution_,densityTarget)
contains
@@ -98,7 +94,6 @@ function densityContrastsConstructorParameters(parameters) result(self)
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: densityContrasts
logical :: darkMatterOnly
type (varying_string ) :: densityContrastRelativeTo
@@ -125,20 +120,18 @@ function densityContrastsConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorDensityContrasts(densityContrasts,darkMatterOnly,enumerationDensityCosmologicalEncode(char(densityContrastRelativeTo),includesPrefix=.false.),cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorDensityContrasts(densityContrasts,darkMatterOnly,enumerationDensityCosmologicalEncode(char(densityContrastRelativeTo),includesPrefix=.false.),cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_)
!![
-
!!]
return
end function densityContrastsConstructorParameters
- function densityContrastsConstructorInternal(densityContrasts,darkMatterOnly,densityContrastRelativeTo,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,galacticStructure_) result(self)
+ function densityContrastsConstructorInternal(densityContrasts,darkMatterOnly,densityContrastRelativeTo,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily densityContrasts} property extractor class.
!!}
@@ -149,13 +142,12 @@ function densityContrastsConstructorInternal(densityContrasts,darkMatterOnly,den
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ), dimension(:) :: densityContrasts
logical , intent(in ) :: darkMatterOnly
type (enumerationDensityCosmologicalType ), intent(in ) :: densityContrastRelativeTo
double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-3
!![
-
+
!!]
self%countDensityContrasts=size(densityContrasts)
@@ -190,7 +182,6 @@ subroutine densityContrastsDestructor(self)
-
!!]
return
end subroutine densityContrastsDestructor
@@ -229,12 +220,14 @@ function densityContrastsExtract(self,node,time,instance)
use :: Cosmology_Functions , only : densityCosmologicalMean, densityCosmologicalCritical
use :: Error , only : Error_Report
use :: Galactic_Structure_Options, only : componentTypeAll
+ use :: Galacticus_Nodes , only : nodeComponentBasic
implicit none
double precision , dimension(:,:), allocatable :: densityContrastsExtract
class (nodePropertyExtractorDensityContrasts ), intent(inout) , target :: self
type (treeNode ), intent(inout) , target :: node
double precision , intent(in ) :: time
type (multiCounter ), intent(inout) , optional :: instance
+ class (nodeComponentBasic ) , pointer :: basic
double precision , parameter :: radiusTiny =1.0d-12
integer :: i
double precision :: enclosedMass , radius, &
@@ -242,19 +235,16 @@ function densityContrastsExtract(self,node,time,instance)
!$GLC attributes unused :: time, instance
allocate(densityContrastsExtract(self%countDensityContrasts,self%elementCount_))
- ! Make the self, node, and basic component available to the root finding routine.
- self_ => self
- node_ => node
- basic_ => node%basic()
! Find the reference density at this epoch.
- densityReference=self%cosmologyFunctions_%matterDensityEpochal(basic_%time())
+ basic => node %basic ( )
+ densityReference = self%cosmologyFunctions_%matterDensityEpochal(basic%time())
select case (self%densityContrastRelativeTo%ID)
case (densityCosmologicalMean %ID)
! No modification required.
case (densityCosmologicalCritical%ID)
! Modify reference density to be the critical density.
densityReference=+densityReference &
- & /self%cosmologyFunctions_%OmegaMatterEpochal(basic_%time())
+ & /self%cosmologyFunctions_%OmegaMatterEpochal(basic%time())
case default
call Error_Report('unknown cosmological density'//{introspection:location})
end select
@@ -262,7 +252,8 @@ function densityContrastsExtract(self,node,time,instance)
if (self%darkMatterOnly) densityReference=+ densityReference &
& *(self%cosmologyParameters_%OmegaMatter()-self%cosmologyParameters_%OmegaBaryon()) &
& / self%cosmologyParameters_%OmegaMatter()
- ! Iterate over density contrasts.
+ ! Iterate over density contrasts.
+ massDistribution_ => node%massDistribution(massType=self%massTypeSelected)
do i=1,self%countDensityContrasts
densityTarget=self%densityContrasts(i)*densityReference
if (densityContrastsRoot(radiusTiny*self%darkMatterHaloScale_%radiusVirial(node)) < 0.0d0) then
@@ -272,16 +263,14 @@ function densityContrastsExtract(self,node,time,instance)
enclosedMass=0.0d0
else
! The target density is reached, so find the exact radius at which it occurs.
- radius =self%finder%find (rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
- enclosedMass=self%galacticStructure_%massEnclosed( &
- & node , &
- & radius , &
- & componentType= componentTypeAll, &
- & massType =self%massTypeSelected &
- & )
+ radius =self %finder%find (rootGuess=self%darkMatterHaloScale_%radiusVirial(node))
+ enclosedMass=massDistribution_ %massEnclosedBySphere( radius )
end if
densityContrastsExtract(i,:)=[radius,enclosedMass]
end do
+ !![
+
+ !!]
return
end function densityContrastsExtract
@@ -368,19 +357,12 @@ Root function used in finding the radius that encloses a given density contrast.
use :: Numerical_Constants_Math , only : Pi
implicit none
double precision, intent(in ) :: radius
- double precision :: enclosedMass
- enclosedMass =self_%galacticStructure_%massEnclosed( &
- & node_ , &
- & radius , &
- & componentType= componentTypeAll, &
- & massType =self_%massTypeSelected &
- & )
- densityContrastsRoot=+3.0d0 &
- & *enclosedMass &
- & /4.0d0 &
- & /Pi &
- & /radius**3 &
+ densityContrastsRoot=+3.0d0 &
+ & *massDistribution_%massEnclosedBySphere(radius) &
+ & /4.0d0 &
+ & /Pi &
+ & /radius**3 &
& -densityTarget
return
end function densityContrastsRoot
diff --git a/source/nodes.property_extractor.half_light_properties.F90 b/source/nodes.property_extractor.half_light_properties.F90
index da9e7f7066..3ff29069ac 100644
--- a/source/nodes.property_extractor.half_light_properties.F90
+++ b/source/nodes.property_extractor.half_light_properties.F90
@@ -21,8 +21,6 @@
Contains a module which implements a half-light radii property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
@@ -38,9 +36,7 @@
A half-light radii property extractor class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: radiiHalfLightPropertiesDestructor
procedure :: elementCount => radiiHalfLightPropertiesElementCount
procedure :: extract => radiiHalfLightPropertiesExtract
procedure :: names => radiiHalfLightPropertiesNames
@@ -53,7 +49,6 @@
Constructors for the ``radiiHalfLightProperties'' output analysis class.
!!}
module procedure radiiHalfLightPropertiesConstructorParameters
- module procedure radiiHalfLightPropertiesConstructorInternal
end interface nodePropertyExtractorRadiiHalfLightProperties
contains
@@ -66,47 +61,14 @@ function radiiHalfLightPropertiesConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorRadiiHalfLightProperties) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorRadiiHalfLightProperties(galacticStructure_)
+ self=nodePropertyExtractorRadiiHalfLightProperties()
!![
-
!!]
return
end function radiiHalfLightPropertiesConstructorParameters
- function radiiHalfLightPropertiesConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily radiiHalfLightProperties} property extractor class.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type (nodePropertyExtractorRadiiHalfLightProperties) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function radiiHalfLightPropertiesConstructorInternal
-
- subroutine radiiHalfLightPropertiesDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily radiiHalfLightProperties} property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorRadiiHalfLightProperties), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine radiiHalfLightPropertiesDestructor
-
integer function radiiHalfLightPropertiesElementCount(self,time)
!!{
Return the number of elements in the {\normalfont \ttfamily radiiHalfLightProperties} property extractor class.
@@ -126,6 +88,7 @@ function radiiHalfLightPropertiesExtract(self,node,time,instance)
Implement a {\normalfont \ttfamily radiiHalfLightProperties} property extractor.
!!}
use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll, massTypeStellar, weightByLuminosity
+ use :: Mass_Distributions , only : massDistributionClass
use :: Stellar_Luminosities_Structure, only : unitStellarLuminosities
implicit none
double precision , dimension(:) , allocatable :: radiiHalfLightPropertiesExtract
@@ -133,20 +96,29 @@ function radiiHalfLightPropertiesExtract(self,node,time,instance)
type (treeNode ), intent(inout), target :: node
double precision , intent(in ) :: time
type (multiCounter ), intent(inout), optional :: instance
+ class (massDistributionClass ) , pointer :: massDistribution_ , lightDistribution_
integer :: i , j
double precision :: halfLightRadius , massEnclosed
!$GLC attributes unused :: self, instance
allocate(radiiHalfLightPropertiesExtract(2*unitStellarLuminosities%luminosityOutputCount(time)))
j=-1
+ massDistribution_ => node%massDistribution(componentType=componentTypeAll,massType=massTypeAll)
do i=1,unitStellarLuminosities%luminosityCount()
if (unitStellarLuminosities%isOutput(i,time)) then
- halfLightRadius=self%galacticStructure_%radiusEnclosingMass(node,massFractional=0.5d0 ,massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=i)
- massEnclosed =self%galacticStructure_%massEnclosed (node, halfLightRadius,componentType=componentTypeAll,massType=massTypeAll )
- j=j+1
- radiiHalfLightPropertiesExtract(2*j+1:2*j+2)=[halfLightRadius,massEnclosed]
- end if
- end do
+ lightDistribution_ => node %massDistribution (massType =massTypeStellar,weightBy=weightByLuminosity,weightIndex=i)
+ halfLightRadius = lightDistribution_%radiusEnclosingMass (massFractional=0.5d0 )
+ massEnclosed = massDistribution_ %massEnclosedBySphere(radius =halfLightRadius )
+ j = j+1
+ radiiHalfLightPropertiesExtract(2*j+1:2*j+2) = [halfLightRadius,massEnclosed]
+ !![
+
+ !!]
+ end if
+ end do
+ !![
+
+ !!]
return
end function radiiHalfLightPropertiesExtract
diff --git a/source/nodes.property_extractor.luminosity_stellar.F90 b/source/nodes.property_extractor.luminosity_stellar.F90
index d6d40a2e64..a08b4faec5 100644
--- a/source/nodes.property_extractor.luminosity_stellar.F90
+++ b/source/nodes.property_extractor.luminosity_stellar.F90
@@ -23,7 +23,6 @@
use :: ISO_Varying_String, only : varying_string
use :: Output_Times , only : outputTimesClass
- use :: Galactic_Structure, only : galacticStructureClass
!![
@@ -35,13 +34,12 @@
A stellar luminosity output analysis property extractor class.
!!}
private
- type (varying_string ) :: filterName , filterType, &
- & postprocessChain , name_ , &
- & description_
- double precision :: redshiftBand
- integer , allocatable, dimension(:) :: luminosityIndex
- class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
+ type (varying_string ) :: filterName , filterType, &
+ & postprocessChain , name_ , &
+ & description_
+ double precision :: redshiftBand
+ integer , allocatable, dimension(:) :: luminosityIndex
+ class (outputTimesClass), pointer :: outputTimes_ => null()
contains
final :: luminosityStellarDestructor
procedure :: extract => luminosityStellarExtract
@@ -70,7 +68,6 @@ function luminosityStellarConstructorParameters(parameters) result(self)
type (nodePropertyExtractorLuminosityStellar) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
type (varying_string ) :: filterName , filterType , &
& postprocessChain
double precision :: redshiftBand
@@ -109,31 +106,29 @@ function luminosityStellarConstructorParameters(parameters) result(self)
!!]
end if
!![
-
-
+
!!]
if (redshiftBandIsPresent) then
if (postprocessChainIsPresent) then
- self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,galacticStructure_,redshiftBand=redshiftBand,postprocessChain=char(postprocessChain))
+ self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,redshiftBand=redshiftBand,postprocessChain=char(postprocessChain))
else
- self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,galacticStructure_,redshiftBand=redshiftBand )
+ self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,redshiftBand=redshiftBand )
end if
else
if (postprocessChainIsPresent) then
- self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,galacticStructure_, postprocessChain=char(postprocessChain))
+ self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_, postprocessChain=char(postprocessChain))
else
- self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_,galacticStructure_ )
+ self=nodePropertyExtractorLuminosityStellar(char(filterName),char(filterType),outputTimes_ )
end if
end if
!![
-
-
+
!!]
return
end function luminosityStellarConstructorParameters
- function luminosityStellarConstructorInternal(filterName,filterType,outputTimes_,galacticStructure_,redshiftBand,postprocessChain,outputMask) result(self)
+ function luminosityStellarConstructorInternal(filterName,filterType,outputTimes_,redshiftBand,postprocessChain,outputMask) result(self)
!!{
Internal constructor for the ``luminosityStellar'' output analysis property extractor class.
!!}
@@ -143,14 +138,13 @@ function luminosityStellarConstructorInternal(filterName,filterType,outputTimes_
type (nodePropertyExtractorLuminosityStellar) :: self
character (len=* ), intent(in ) :: filterName , filterType
class (outputTimesClass ), intent(in ), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
character (len=* ), intent(in ), optional :: postprocessChain
double precision , intent(in ), optional :: redshiftBand
logical , intent(in ), dimension(:), optional :: outputMask
integer (c_size_t ) :: i
character (len=7 ) :: label
!![
-
+
!!]
allocate(self%luminosityIndex(self%outputTimes_%count()))
@@ -183,8 +177,7 @@ subroutine luminosityStellarDestructor(self)
type(nodePropertyExtractorLuminosityStellar), intent(inout) :: self
!![
-
-
+
!!]
return
end subroutine luminosityStellarDestructor
@@ -193,20 +186,26 @@ double precision function luminosityStellarExtract(self,node,instance)
!!{
Implement a stellar luminosity output analysis property extractor.
!!}
- use :: Galactic_Structure_Options, only : massTypeStellar , radiusLarge, weightByLuminosity
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
+ use :: Galactic_Structure_Options, only : massTypeStellar , weightByLuminosity
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use, intrinsic :: ISO_C_Binding , only : c_size_t
implicit none
class (nodePropertyExtractorLuminosityStellar), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class (massDistributionClass ) , pointer :: massDistribution_
class (nodeComponentBasic ), pointer :: basic
integer(c_size_t ) :: i
!$GLC attributes unused :: instance
- basic => node %basic ( )
- i = self%outputTimes_ %index (basic%time(),findClosest=.true. )
- luminosityStellarExtract = self%galacticStructure_%massEnclosed(node , radiusLarge,massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=self%luminosityIndex(i))
+ basic => node %basic ( )
+ i = self%outputTimes_%index (basic%time(),findClosest=.true. )
+ massDistribution_ => node %massDistribution(massType=massTypeStellar,weightBy=weightByLuminosity,weightIndex=self%luminosityIndex(i))
+ luminosityStellarExtract = massDistribution_%massTotal ( )
+ !![
+
+ !!]
return
end function luminosityStellarExtract
diff --git a/source/nodes.property_extractor.mass_ISM.F90 b/source/nodes.property_extractor.mass_ISM.F90
index f33bec5274..d7ebd7347b 100644
--- a/source/nodes.property_extractor.mass_ISM.F90
+++ b/source/nodes.property_extractor.mass_ISM.F90
@@ -21,8 +21,6 @@
Contains a module which implements an ISM mass output analysis property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
An ISM mass output analysis property extractor class.
@@ -33,9 +31,7 @@
A stellar mass output analysis class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: massISMDestructor
procedure :: extract => massISMExtract
procedure :: quantity => massISMQuantity
procedure :: name => massISMName
@@ -48,7 +44,6 @@
Constructors for the ``massISM'' output analysis class.
!!}
module procedure massISMConstructorParameters
- module procedure massISMConstructorInternal
end interface nodePropertyExtractorMassISM
contains
@@ -61,60 +56,36 @@ function massISMConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorMassISM) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorMassISM(galacticStructure_)
+
+ self=nodePropertyExtractorMassISM()
!![
-
!!]
return
end function massISMConstructorParameters
- function massISMConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``massISM'' output analysis property extractor class.
- !!}
- use :: Input_Parameters, only : inputParameters
- implicit none
- type (nodePropertyExtractorMassISM) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function massISMConstructorInternal
-
- subroutine massISMDestructor(self)
- !!{
- Destructor for the ``massISM'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorMassISM), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine massISMDestructor
-
double precision function massISMExtract(self,node,instance)
!!{
Implement a massISM output analysis.
!!}
- use :: Galactic_Structure_Options, only : componentTypeDisk, componentTypeSpheroid, massTypeGaseous, radiusLarge
+ use :: Galactic_Structure_Options, only : componentTypeDisk , componentTypeSpheroid, massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class(nodePropertyExtractorMassISM), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistributionDisk, massDistributionSpheroid
!$GLC attributes unused :: self, instance
- massISMExtract=+self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeGaseous,componentType=componentTypeDisk ) &
- & +self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeGaseous,componentType=componentTypeSpheroid)
+ massDistributionDisk => node %massDistribution(massType=massTypeGaseous,componentType=componentTypeDisk )
+ massDistributionSpheroid => node %massDistribution(massType=massTypeGaseous,componentType=componentTypeSpheroid)
+ massISMExtract = +massDistributionDisk %massTotal ( ) &
+ & +massDistributionSpheroid%massTotal ( )
+ !![
+
+
+ !!]
return
end function massISMExtract
diff --git a/source/nodes.property_extractor.mass_halo.F90 b/source/nodes.property_extractor.mass_halo.F90
index d6a9427ec1..9a1d79194a 100644
--- a/source/nodes.property_extractor.mass_halo.F90
+++ b/source/nodes.property_extractor.mass_halo.F90
@@ -21,10 +21,9 @@
Contains a module which implements a halo mass output analysis property extractor class.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -39,7 +38,6 @@
time at which is was last isolated (as is used for standard definition of halo mass).
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -72,7 +70,6 @@ function massHaloConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
logical :: useLastIsolatedTime
@@ -85,23 +82,21 @@ function massHaloConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorMassHalo(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=nodePropertyExtractorMassHalo(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_)
!![
-
!!]
return
end function massHaloConstructorParameters
- function massHaloConstructorInternal(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
+ function massHaloConstructorInternal(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
!!{
Internal constructor for the ``massHalo'' output analysis property extractor class.
!!}
@@ -110,10 +105,9 @@ function massHaloConstructorInternal(useLastIsolatedTime,cosmologyFunctions_,cos
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_, virialDensityContrastDefinition_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
logical , intent(in ) :: useLastIsolatedTime
!![
-
+
!!]
return
@@ -130,7 +124,6 @@ subroutine massHaloDestructor(self)
-
!!]
return
@@ -161,7 +154,6 @@ double precision function massHaloExtract(self,node,instance)
& self%virialDensityContrastDefinition_%densityContrast(basic%mass(),time), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ , &
& useLastIsolatedTime =self%useLastIsolatedTime &
& )
diff --git a/source/nodes.property_extractor.mass_profile.F90 b/source/nodes.property_extractor.mass_profile.F90
index 696e936095..b5e52c0cf1 100644
--- a/source/nodes.property_extractor.mass_profile.F90
+++ b/source/nodes.property_extractor.mass_profile.F90
@@ -22,7 +22,6 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
!![
@@ -36,7 +35,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (cosmologyParametersClass), pointer :: cosmologyParameters_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii
@@ -77,7 +75,6 @@ function massProfileConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
logical :: includeRadii
@@ -95,19 +92,17 @@ function massProfileConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorMassProfile(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_,cosmologyParameters_)
+ self=nodePropertyExtractorMassProfile(radiusSpecifiers,includeRadii,darkMatterHaloScale_,cosmologyParameters_)
!![
-
!!]
return
end function massProfileConstructorParameters
- function massProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_,cosmologyParameters_) result(self)
+ function massProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,cosmologyParameters_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily massProfile} property extractor class.
!!}
@@ -116,11 +111,10 @@ function massProfileConstructorInternal(radiusSpecifiers,includeRadii,darkMatter
type (nodePropertyExtractorMassProfile) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
logical , intent(in ) :: includeRadii
!![
-
+
!!]
if (includeRadii) then
@@ -155,7 +149,6 @@ subroutine massProfileDestructor(self)
!![
-
!!]
return
@@ -198,6 +191,7 @@ function massProfileExtract(self,node,time,instance)
& radiusTypeStellarMassFraction , radiusTypeVirialRadius , radiusTypeSatelliteBoundMassFraction
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , nodeComponentSatellite , &
& treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Error , only : Error_Report
implicit none
double precision , dimension(:,:), allocatable :: massProfileExtract
@@ -209,6 +203,7 @@ function massProfileExtract(self,node,time,instance)
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_
integer :: i
double precision :: radius , radiusVirial, &
& mass
@@ -239,52 +234,66 @@ function massProfileExtract(self,node,time,instance)
case (radiusTypeSpheroidHalfMassRadius%ID)
radius=+radius*spheroid %halfMassRadius()
case (radiusTypeSatelliteBoundMassFraction%ID)
- mass =+satellite%boundMass () &
- & *self %fractionDarkMatter
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & mass = mass , &
- & massType = massTypeDark , &
- & componentType= componentTypeAll , &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ mass = +satellite %boundMass ( &
+ & ) &
+ & *self %fractionDarkMatter
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeDark , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & mass = mass &
+ & )
+ !![
+
+ !!]
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
- case (radiusTypeStellarMassFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
+ case (radiusTypeStellarMassFraction %ID)
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
- massProfileExtract (i,1)=self%galacticStructure_%massEnclosed( &
- & node , &
- & radius , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
- if (self%includeRadii) &
- & massProfileExtract(i,2)= radius
+ massDistribution_ => node %massDistribution (&
+ & componentType=self%radii(i)%component, &
+ & massType =self%radii(i)%mass &
+ & )
+ massProfileExtract (i,1) = massDistribution_%massEnclosedBySphere( &
+ & radius = radius &
+ & )
+ if (self%includeRadii) &
+ & massProfileExtract(i,2) = radius
+ !![
+
+ !!]
end do
return
end function massProfileExtract
diff --git a/source/nodes.property_extractor.mass_stellar.F90 b/source/nodes.property_extractor.mass_stellar.F90
index 0adf11c463..d62d83b721 100644
--- a/source/nodes.property_extractor.mass_stellar.F90
+++ b/source/nodes.property_extractor.mass_stellar.F90
@@ -21,8 +21,6 @@
Contains a module which implements a stellar mass property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A stellar mass output analysis property extractor class.
@@ -33,9 +31,7 @@
A stellar mass property extractor class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: massStellarDestructor
procedure :: extract => massStellarExtract
procedure :: name => massStellarName
procedure :: description => massStellarDescription
@@ -48,7 +44,6 @@
Constructors for the ``massStellar'' property extractor class.
!!}
module procedure massStellarConstructorParameters
- module procedure massStellarConstructorInternal
end interface nodePropertyExtractorMassStellar
contains
@@ -61,58 +56,32 @@ function massStellarConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorMassStellar) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorMassStellar(galacticStructure_)
+ self=nodePropertyExtractorMassStellar()
!![
-
!!]
return
end function massStellarConstructorParameters
- function massStellarConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``massStellar'' output analysis property extractor class.
- !!}
- implicit none
- type (nodePropertyExtractorMassStellar) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function massStellarConstructorInternal
-
- subroutine massStellarDestructor(self)
- !!{
- Destructor for the ``massStellar'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorMassStellar), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine massStellarDestructor
-
double precision function massStellarExtract(self,node,instance)
!!{
Implement a massStellar output analysis.
!!}
- use :: Galactic_Structure_Options, only : massTypeStellar, radiusLarge
+ use :: Galactic_Structure_Options, only : massTypeStellar
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class(nodePropertyExtractorMassStellar), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistribution_
!$GLC attributes unused :: self, instance
- massStellarExtract=self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeStellar)
+ massDistribution_ => node %massDistribution(massType=massTypeStellar)
+ massStellarExtract = massDistribution_%massTotal ( )
+ !![
+
+ !!]
return
end function massStellarExtract
diff --git a/source/nodes.property_extractor.mass_stellar_morphology.F90 b/source/nodes.property_extractor.mass_stellar_morphology.F90
index 32ac7d3cc2..27fc116115 100644
--- a/source/nodes.property_extractor.mass_stellar_morphology.F90
+++ b/source/nodes.property_extractor.mass_stellar_morphology.F90
@@ -21,8 +21,6 @@
Contains a module which implements a stellar mass-weighted morphology output analysis property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A stellar mass-weighted morphology output analysis property extractor class.
@@ -33,9 +31,7 @@
A stellar mass output analysis class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: massStellarMorphologyDestructor
procedure :: extract => massStellarMorphologyExtract
procedure :: name => massStellarMorphologyName
procedure :: description => massStellarMorphologyDescription
@@ -47,7 +43,6 @@
Constructors for the ``massStellarMorphology'' output analysis class.
!!}
module procedure massStellarMorphologyConstructorParameters
- module procedure massStellarMorphologyConstructorInternal
end interface nodePropertyExtractorMassStellarMorphology
contains
@@ -60,60 +55,33 @@ function massStellarMorphologyConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorMassStellarMorphology) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorMassStellarMorphology(galacticStructure_)
+
+ self=nodePropertyExtractorMassStellarMorphology()
!![
-
!!]
return
end function massStellarMorphologyConstructorParameters
- function massStellarMorphologyConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``massStellarMorphology'' output analysis property extractor class.
- !!}
- implicit none
- type (nodePropertyExtractorMassStellarMorphology) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function massStellarMorphologyConstructorInternal
-
- subroutine massStellarMorphologyDestructor(self)
- !!{
- Destructor for the ``massStellarMorphology'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorMassStellarMorphology), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine massStellarMorphologyDestructor
-
double precision function massStellarMorphologyExtract(self,node,instance)
!!{
Implement a stellar mass-weighted morphology output analysis.
!!}
- use :: Galactic_Structure_Options, only : componentTypeDisk, componentTypeSpheroid, massTypeStellar, radiusLarge
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeDisk , componentTypeSpheroid, massTypeStellar
implicit none
class (nodePropertyExtractorMassStellarMorphology), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
- double precision :: massStellarDisk, massStellarSpheroid
+ class (massDistributionClass ) , pointer :: massDistributionDisk, massDistributionSpheroid
+ double precision :: massStellarDisk , massStellarSpheroid
!$GLC attributes unused :: self, instance
- massStellarDisk =self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeStellar,componentType=componentTypeDisk )
- massStellarSpheroid=self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeStellar,componentType=componentTypeSpheroid)
+ massDistributionDisk => node %massDistribution(massType=massTypeStellar,componentType=componentTypeDisk )
+ massDistributionSpheroid => node %massDistribution(massType=massTypeStellar,componentType=componentTypeSpheroid)
+ massStellarDisk = massDistributionDisk%massTotal ( )
+ massStellarSpheroid = massDistributionDisk%massTotal ( )
if (massStellarDisk+massStellarSpheroid > 0.0d0) then
massStellarMorphologyExtract=+ massStellarSpheroid &
& /( &
@@ -123,6 +91,10 @@ double precision function massStellarMorphologyExtract(self,node,instance)
else
massStellarMorphologyExtract=+0.0d0
end if
+ !![
+
+
+ !!]
return
end function massStellarMorphologyExtract
diff --git a/source/nodes.property_extractor.mass_stellar_spheroid.F90 b/source/nodes.property_extractor.mass_stellar_spheroid.F90
index 4a24bf544d..7758f33625 100644
--- a/source/nodes.property_extractor.mass_stellar_spheroid.F90
+++ b/source/nodes.property_extractor.mass_stellar_spheroid.F90
@@ -21,8 +21,6 @@
Contains a module which implements a spheroid stellar mass output analysis property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A spheroid stellar mass output analysis property extractor class.
@@ -33,9 +31,7 @@
A stellar mass output analysis class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: massStellarSpheroidDestructor
procedure :: extract => massStellarSpheroidExtract
procedure :: quantity => massStellarSpheroidQuantity
procedure :: name => massStellarSpheroidName
@@ -48,7 +44,6 @@
Constructors for the ``massStellarSpheroid'' output analysis class.
!!}
module procedure massStellarSpheroidConstructorParameters
- module procedure massStellarSpheroidConstructorInternal
end interface nodePropertyExtractorMassStellarSpheroid
contains
@@ -59,60 +54,34 @@ function massStellarSpheroidConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameters
implicit none
- type (nodePropertyExtractorMassStellarSpheroid) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
+ type(nodePropertyExtractorMassStellarSpheroid) :: self
+ type(inputParameters ), intent(inout) :: parameters
- !![
-
- !!]
- self=nodePropertyExtractorMassStellarSpheroid(galacticStructure_)
+ self=nodePropertyExtractorMassStellarSpheroid()
!![
-
!!]
return
end function massStellarSpheroidConstructorParameters
- function massStellarSpheroidConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``massStellarSpheroid'' output analysis property extractor class.
- !!}
- implicit none
- type (nodePropertyExtractorMassStellarSpheroid) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function massStellarSpheroidConstructorInternal
-
- subroutine massStellarSpheroidDestructor(self)
- !!{
- Destructor for the ``massStellarSpheroid'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorMassStellarSpheroid), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine massStellarSpheroidDestructor
-
double precision function massStellarSpheroidExtract(self,node,instance)
!!{
Implement a stellar mass-weighted morphology output analysis.
!!}
- use :: Galactic_Structure_Options, only : componentTypeSpheroid, massTypeStellar, radiusLarge
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeSpheroid, massTypeStellar
implicit none
- class (nodePropertyExtractorMassStellarSpheroid), intent(inout), target :: self
- type (treeNode ), intent(inout), target :: node
- type (multiCounter ), intent(inout), optional :: instance
+ class(nodePropertyExtractorMassStellarSpheroid), intent(inout), target :: self
+ type (treeNode ), intent(inout), target :: node
+ type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistribution_
!$GLC attributes unused :: self, instance
- massStellarSpheroidExtract=self%galacticStructure_%massEnclosed(node,radiusLarge,massType=massTypeStellar,componentType=componentTypeSpheroid)
+ massDistribution_ => node %massDistribution(massType=massTypeStellar,componentType=componentTypeSpheroid)
+ massStellarSpheroidExtract = massDistribution_%massTotal ( )
+ !![
+
+ !!]
return
end function massStellarSpheroidExtract
diff --git a/source/nodes.property_extractor.orbital_adiabatic_ratio.disk.F90 b/source/nodes.property_extractor.orbital_adiabatic_ratio.disk.F90
index b022c18fa1..87b63dd042 100644
--- a/source/nodes.property_extractor.orbital_adiabatic_ratio.disk.F90
+++ b/source/nodes.property_extractor.orbital_adiabatic_ratio.disk.F90
@@ -21,7 +21,7 @@
Contains a module which implements a property extractor class for the orbital adiabatic ratio of disks.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -41,7 +41,7 @@
A property extractor class for the orbital adiabatic ratio of disks.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
final :: adiabaticRatioOrbitalDiskDestructor
procedure :: extract => adiabaticRatioOrbitalDiskExtract
@@ -68,28 +68,28 @@ function adiabaticRatioOrbitalDiskConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorAdiabaticRatioOrbitalDisk) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
!![
-
+
!!]
- self=nodePropertyExtractorAdiabaticRatioOrbitalDisk(galacticStructure_)
+ self=nodePropertyExtractorAdiabaticRatioOrbitalDisk(darkMatterHaloScale_)
!![
-
+
!!]
return
end function adiabaticRatioOrbitalDiskConstructorParameters
- function adiabaticRatioOrbitalDiskConstructorInternal(galacticStructure_) result(self)
+ function adiabaticRatioOrbitalDiskConstructorInternal(darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily adiabaticRatioOrbital} node operator class.
!!}
implicit none
type (nodePropertyExtractorAdiabaticRatioOrbitalDisk) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
return
@@ -103,7 +103,7 @@ subroutine adiabaticRatioOrbitalDiskDestructor(self)
type(nodePropertyExtractorAdiabaticRatioOrbitalDisk), intent(inout) :: self
!![
-
+
!!]
return
end subroutine adiabaticRatioOrbitalDiskDestructor
@@ -133,7 +133,7 @@ double precision function adiabaticRatioOrbitalDiskExtract(self,node,instance)
disk => node %disk ()
satellite => node %satellite ()
orbit = satellite%virialOrbit()
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusPericenter,velocityPericenter,self%galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusPericenter,velocityPericenter,self%darkMatterHaloScale_)
if (disk%radius() > 0.0d0) &
& adiabaticRatioOrbitalDiskExtract=+( &
& + radiusPericenter &
diff --git a/source/nodes.property_extractor.projected_density.F90 b/source/nodes.property_extractor.projected_density.F90
index 206c34bc76..30b32b0898 100644
--- a/source/nodes.property_extractor.projected_density.F90
+++ b/source/nodes.property_extractor.projected_density.F90
@@ -22,7 +22,6 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -41,7 +40,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii
type (varying_string ), allocatable, dimension(:) :: radiusSpecifiers
@@ -84,7 +82,6 @@ function projectedDensityConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: includeRadii
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
@@ -101,18 +98,16 @@ function projectedDensityConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorProjectedDensity(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorProjectedDensity(radiusSpecifiers,includeRadii,darkMatterHaloScale_)
!![
-
!!]
return
end function projectedDensityConstructorParameters
- function projectedDensityConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_) result(self)
+ function projectedDensityConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily projectedDensity} property extractor class.
!!}
@@ -121,10 +116,9 @@ function projectedDensityConstructorInternal(radiusSpecifiers,includeRadii,darkM
type (nodePropertyExtractorProjectedDensity) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: includeRadii
!![
-
+
!!]
if (includeRadii) then
@@ -154,7 +148,6 @@ subroutine projectedDensityDestructor(self)
!![
-
!!]
return
end subroutine projectedDensityDestructor
@@ -197,6 +190,8 @@ function projectedDensityExtract(self,node,time,instance) result(densityProjecte
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Numerical_Integration , only : integrator, GSL_Integ_Gauss15
use :: Numerical_Comparison , only : Values_Agree
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Error , only : Error_Report
implicit none
double precision , dimension(:,:), allocatable :: densityProjected
@@ -207,6 +202,7 @@ function projectedDensityExtract(self,node,time,instance) result(densityProjecte
class (nodeComponentDisk ), pointer :: disk
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: toleranceRelative =1.0d-2, epsilonSingularity =1.0d-3
type (integrator ) :: integrator_
integer :: i
@@ -214,6 +210,7 @@ function projectedDensityExtract(self,node,time,instance) result(densityProjecte
& radiusSingularity , densityProjectedPrevious , &
& densityProjectedCurrent , toleranceAbsolute
logical :: converged
+ type (coordinateSpherical ) :: coordinates
!$GLC attributes unused :: time, instance
allocate(densityProjected(self%radiiCount,self%elementCount_))
@@ -241,51 +238,50 @@ function projectedDensityExtract(self,node,time,instance) result(densityProjecte
radius_=+radius_*spheroid %halfMassRadius()
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius_=+radius_ &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius_ = +radius_ &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case (radiusTypeStellarMassFraction %ID)
- radius_=+radius_ &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius_ = +radius_ &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
- densityProjectedPrevious=0.0d0
- radiusOuter =max(radius_* 2.0d0 ,radiusVirial)
+ massDistribution_ => node%massDistribution(self%radii(i)%component,self%radii(i)%mass)
+ densityProjectedPrevious = 0.0d0
+ radiusOuter = max(radius_* 2.0d0 ,radiusVirial)
! Cut out a small region round the coordinate singularity at the inner radius. This region will be integrated analytically
! assuming a constant density over this region. The region outside of this cut-out will be integrated numerically.
radiusSingularity =min(radius_*(1.0d0+epsilonSingularity),radiusOuter )
!! Analytic integral within the cut-out.
- densityProjected(i,1)=+2.0d0 &
- & *sqrt( &
- & +radiusSingularity**2 &
- & -radius_ **2 &
- & ) &
- & *self%galacticStructure_%density( &
- & node , &
- & [ &
- & radius_ , &
- & 0.0d0 , &
- & 0.0d0 &
- & ] , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
+ coordinates=[radius_,0.0d0,0.0d0]
+ densityProjected(i,1)=+2.0d0 &
+ & *sqrt( &
+ & +radiusSingularity**2 &
+ & -radius_ **2 &
+ & ) &
+ & *massDistribution_%density(coordinates)
!! Numerical integral outside of the cut-out.
if (radiusSingularity < radiusOuter) then
! Set an absolute tolerance scale for projected density convergence that is a small fraction of the mean halo density,
@@ -305,6 +301,9 @@ function projectedDensityExtract(self,node,time,instance) result(densityProjecte
densityProjected(i,1)=+densityProjected (i,1) &
& +densityProjectedCurrent
if (self%includeRadii) densityProjected(i,2)=radius_
+ !![
+
+ !!]
end do
return
@@ -322,22 +321,14 @@ Integrand function used for computing projected densities.
if (radius <= radius_) then
projectedDensityIntegrand=+0.0d0
else
- projectedDensityIntegrand=+2.0d0 &
- & *radius **2 &
- & /sqrt( &
- & +radius **2 &
- & -radius_**2 &
- & ) &
- & *self%galacticStructure_%density( &
- & node , &
- & [ &
- & radius , &
- & 0.0d0 , &
- & 0.0d0 &
- & ] , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
+ coordinates=[radius,0.0d0,0.0d0]
+ projectedDensityIntegrand=+2.0d0 &
+ & *radius **2 &
+ & /sqrt( &
+ & +radius **2 &
+ & -radius_**2 &
+ & ) &
+ & *massDistribution_%density(coordinates)
end if
return
end function projectedDensityIntegrand
diff --git a/source/nodes.property_extractor.projected_mass.F90 b/source/nodes.property_extractor.projected_mass.F90
index a49280bddb..a5f1f19aea 100644
--- a/source/nodes.property_extractor.projected_mass.F90
+++ b/source/nodes.property_extractor.projected_mass.F90
@@ -20,9 +20,8 @@
!!{
Contains a module which implements a property extractor class for the projected density at a set of radii.
!!}
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale, darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -41,7 +40,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii
type (varying_string ), allocatable, dimension(:) :: radiusSpecifiers
@@ -84,7 +82,6 @@ function projectedMassConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: includeRadii
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
@@ -101,18 +98,16 @@ function projectedMassConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorProjectedMass(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorProjectedMass(radiusSpecifiers,includeRadii,darkMatterHaloScale_)
!![
-
!!]
return
end function projectedMassConstructorParameters
- function projectedMassConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_) result(self)
+ function projectedMassConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily projectedMass} property extractor class.
!!}
@@ -121,10 +116,9 @@ function projectedMassConstructorInternal(radiusSpecifiers,includeRadii,darkMatt
type (nodePropertyExtractorProjectedMass) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: includeRadii
!![
-
+
!!]
if (includeRadii) then
@@ -154,7 +148,6 @@ subroutine projectedMassDestructor(self)
!![
-
!!]
return
end subroutine projectedMassDestructor
@@ -197,6 +190,7 @@ function projectedMassExtract(self,node,time,instance) result(massProjected)
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Numerical_Integration , only : integrator, GSL_Integ_Gauss15
use :: Numerical_Comparison , only : Values_Agree
+ use :: Mass_Distributions , only : massDistributionClass
use :: Error , only : Error_Report
implicit none
double precision , dimension(:,:), allocatable :: massProjected
@@ -207,6 +201,7 @@ function projectedMassExtract(self,node,time,instance) result(massProjected)
class (nodeComponentDisk ), pointer :: disk
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: toleranceRelative =1.0d-2
type (integrator ) :: integrator_
integer :: i
@@ -240,50 +235,55 @@ function projectedMassExtract(self,node,time,instance) result(massProjected)
radius_=+radius_*spheroid %halfMassRadius()
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius_=+radius_ &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius_ = +radius_ &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case (radiusTypeStellarMassFraction %ID)
- radius_=+radius_ &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius_ = +radius_ &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
massProjectedPrevious=0.0d0
radiusOuter =max(radius_*2.0d0,radiusVirial)
! Evaluate the integral, then add on the mass of the sphere entirely enclosed inside the cylinder.
- converged=.false.
+ massDistribution_ => node%massDistribution(componentType=self%radii(i)%component,massType=self%radii(i)%mass)
+ converged = .false.
do while (.not.converged)
massProjectedCurrent=integrator_%integrate(log(radius_),log(radiusOuter))
- converged =Values_Agree(massProjectedCurrent,massProjectedPrevious,relTol=toleranceRelative)
+ converged =Values_Agree(massProjectedCurrent,massProjectedPrevious,relTol=toleranceRelative)
if (.not.converged) then
radiusOuter =2.0d0*radiusOuter
massProjectedPrevious= massProjectedCurrent
end if
end do
- massProjected(i,1)=+ massProjectedCurrent &
- & +self%galacticStructure_%massEnclosed ( &
- & node , &
- & radius_ , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
+ massProjected(i,1)=+ massProjectedCurrent &
+ & +massDistribution_%massEnclosedBySphere(radius_)
if (self%includeRadii) massProjected(i,2)=radius_
+ !![
+
+ !!]
end do
return
@@ -293,39 +293,33 @@ double precision function projectedMassIntegrand(radiusLogarithmic)
!!{
Integrand function used for computing projected masses.
!!}
+ use :: Coordinates , only : coordinateSpherical, assignment(=)
use :: Numerical_Constants_Math, only : Pi
implicit none
- double precision, intent(in ) :: radiusLogarithmic
- double precision :: radius
+ double precision , intent(in ) :: radiusLogarithmic
+ double precision :: radius
+ type (coordinateSpherical) :: coordinates
radius=exp(radiusLogarithmic)
if (radius <= radius_) then
projectedMassIntegrand=+0.0d0
else
- projectedMassIntegrand=+4.0d0 & ! ⎫
- & *Pi & ! ⎬ Surface area of the spherical shell.
- & *radius**2 & ! ⎭
- & *( & ! ⎫
- & +1.0d0 & ! ⎪
- & -sqrt( & ! ⎪
- & +1.0d0 & ! ⎪
- & -( & ! ⎪
- & +radius_ & ! ⎬ Fraction of shell solid angle lying
- & /radius & ! ⎪ inside the cylinder
- & )**2 & ! ⎪
- & ) & ! ⎪
- & ) & ! ⎭
- & *self%galacticStructure_%density( & ! ⎫
- & node , & ! ⎪
- & [ & ! ⎪
- & radius , & ! ⎪
- & 0.0d0 , & ! ⎬ Density of the spherical shell.
- & 0.0d0 & ! ⎪
- & ] , & ! ⎪
- & componentType=self%radii(i)%component, & ! ⎪
- & massType =self%radii(i)%mass & ! ⎪
- & ) & ! ⎭
- & *radius ! } Account for logarithmic integration variable.
+ coordinates =[radius,0.0d0,0.0d0]
+ projectedMassIntegrand=+4.0d0 & ! ⎫
+ & *Pi & ! ⎬ Surface area of the spherical shell.
+ & *radius**2 & ! ⎭
+ & *( & ! ⎫
+ & +1.0d0 & ! ⎪
+ & -sqrt( & ! ⎪
+ & +1.0d0 & ! ⎪
+ & -( & ! ⎪
+ & +radius_ & ! ⎬ Fraction of shell solid angle lying
+ & /radius & ! ⎪ inside the cylinder
+ & )**2 & ! ⎪
+ & ) & ! ⎪
+ & ) & ! ⎭
+ & *massDistribution_%density(coordinates) & ! } Density of the spherical shell.
+ & *radius ! } Account for logarithmic integration variable.
end if
return
end function projectedMassIntegrand
diff --git a/source/nodes.property_extractor.radius.half_mass.galactic.F90 b/source/nodes.property_extractor.radius.half_mass.galactic.F90
index 053428847b..f5c956b067 100644
--- a/source/nodes.property_extractor.radius.half_mass.galactic.F90
+++ b/source/nodes.property_extractor.radius.half_mass.galactic.F90
@@ -21,8 +21,6 @@
Contains a module which implements a half-galactic mass radius output analysis property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A half-galactic mass output analysis property extractor class.
@@ -33,9 +31,7 @@
A half-galactic mass property extractor output analysis class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: radiusHalfMassGalacticDestructor
procedure :: extract => radiusHalfMassGalacticExtract
procedure :: name => radiusHalfMassGalacticName
procedure :: description => radiusHalfMassGalacticDescription
@@ -47,7 +43,6 @@
Constructors for the ``radiusHalfMassGalactic'' output analysis class.
!!}
module procedure radiusHalfMassGalacticConstructorParameters
- module procedure radiusHalfMassGalacticConstructorInternal
end interface nodePropertyExtractorRadiusHalfMassGalactic
contains
@@ -60,62 +55,35 @@ function radiusHalfMassGalacticConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorRadiusHalfMassGalactic) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorRadiusHalfMassGalactic(galacticStructure_)
+ self=nodePropertyExtractorRadiusHalfMassGalactic()
!![
-
!!]
return
end function radiusHalfMassGalacticConstructorParameters
- function radiusHalfMassGalacticConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``radiusHalfMassGalactic'' output analysis property extractor class.
- !!}
- implicit none
- type (nodePropertyExtractorRadiusHalfMassGalactic) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function radiusHalfMassGalacticConstructorInternal
-
- subroutine radiusHalfMassGalacticDestructor(self)
- !!{
- Destructor for the ``radiusHalfMassGalactic'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorRadiusHalfMassGalactic), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine radiusHalfMassGalacticDestructor
-
double precision function radiusHalfMassGalacticExtract(self,node,instance)
!!{
Implement a half-mass output analysis.
!!}
use :: Galactic_Structure_Options, only : massTypeGalactic
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class(nodePropertyExtractorRadiusHalfMassGalactic), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistribution_
!$GLC attributes unused :: self, instance
- radiusHalfMassGalacticExtract=self%galacticStructure_%radiusEnclosingMass(node,massFractional=0.5d0,massType=massTypeGalactic)
+ massDistribution_ => node %massDistribution (massType =massTypeGalactic)
+ radiusHalfMassGalacticExtract = massDistribution_%radiusEnclosingMass(massFractional=0.5d0 )
+ !![
+
+ !!]
return
end function radiusHalfMassGalacticExtract
-
function radiusHalfMassGalacticName(self)
!!{
Return the name of the radiusHalfMassGalactic property.
diff --git a/source/nodes.property_extractor.radius.half_mass.stellar.F90 b/source/nodes.property_extractor.radius.half_mass.stellar.F90
index 35720f9ae6..dfcbf164af 100644
--- a/source/nodes.property_extractor.radius.half_mass.stellar.F90
+++ b/source/nodes.property_extractor.radius.half_mass.stellar.F90
@@ -21,8 +21,6 @@
Contains a module which implements a half-stellar mass radius output analysis property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A half-(stellar) mass output analysis property extractor class.
@@ -33,9 +31,7 @@
A half-(stellar) mass property extractor output analysis class.
!!}
private
- class(galacticStructureClass), pointer :: galacticStructure_ => null()
contains
- final :: radiusHalfMassStellarDestructor
procedure :: extract => radiusHalfMassStellarExtract
procedure :: name => radiusHalfMassStellarName
procedure :: description => radiusHalfMassStellarDescription
@@ -47,7 +43,6 @@
Constructors for the ``radiusHalfMassStellar'' output analysis class.
!!}
module procedure radiusHalfMassStellarConstructorParameters
- module procedure radiusHalfMassStellarConstructorInternal
end interface nodePropertyExtractorRadiusHalfMassStellar
contains
@@ -60,58 +55,32 @@ function radiusHalfMassStellarConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorRadiusHalfMassStellar) :: self
type (inputParameters ), intent(inout) :: parameters
- class(galacticStructureClass ), pointer :: galacticStructure_
- !![
-
- !!]
- self=nodePropertyExtractorRadiusHalfMassStellar(galacticStructure_)
+ self=nodePropertyExtractorRadiusHalfMassStellar()
!![
-
!!]
return
end function radiusHalfMassStellarConstructorParameters
- function radiusHalfMassStellarConstructorInternal(galacticStructure_) result(self)
- !!{
- Internal constructor for the ``radiusHalfMassStellar'' output analysis property extractor class.
- !!}
- implicit none
- type (nodePropertyExtractorRadiusHalfMassStellar) :: self
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
- !![
-
- !!]
-
- return
- end function radiusHalfMassStellarConstructorInternal
-
- subroutine radiusHalfMassStellarDestructor(self)
- !!{
- Destructor for the ``radiusHalfMassStellar'' output analysis property extractor class.
- !!}
- implicit none
- type(nodePropertyExtractorRadiusHalfMassStellar), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine radiusHalfMassStellarDestructor
-
double precision function radiusHalfMassStellarExtract(self,node,instance)
!!{
Implement a half-mass output analysis.
!!}
use :: Galactic_Structure_Options, only : massTypeStellar
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class(nodePropertyExtractorRadiusHalfMassStellar), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistribution_
!$GLC attributes unused :: self, instance
- radiusHalfMassStellarExtract=self%galacticStructure_%radiusEnclosingMass(node,massFractional=0.5d0,massType=massTypeStellar)
+ massDistribution_ => node %massDistribution (massType =massTypeStellar)
+ radiusHalfMassStellarExtract = massDistribution_%radiusEnclosingMass(massFractional=0.5d0 )
+ !![
+
+ !!]
return
end function radiusHalfMassStellarExtract
diff --git a/source/nodes.property_extractor.radius_Einstein.F90 b/source/nodes.property_extractor.radius_Einstein.F90
index 88150f03bf..b735549263 100644
--- a/source/nodes.property_extractor.radius_Einstein.F90
+++ b/source/nodes.property_extractor.radius_Einstein.F90
@@ -23,8 +23,8 @@
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Root_Finder , only : rootFinder
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Integration , only : integrator
!![
@@ -39,7 +39,6 @@
private
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: redshiftSource , timeSource
type (rootFinder ) :: finder
type (integrator ) :: integratorImpactParameter , integratorLineOfSight
@@ -61,10 +60,10 @@
! Submodule-scope variables used in root-finding.
class (nodePropertyExtractorRadiusEinstein), pointer :: self_
- type (treeNode ), pointer :: node_
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: densitySurfaceCritical , radiusImpact_, &
& distanceLineOfSightMaximum_
- !$omp threadprivate(self_,node_,densitySurfaceCritical,radiusImpact_,distanceLineOfSightMaximum_)
+ !$omp threadprivate(self_,massDistribution_,densitySurfaceCritical,radiusImpact_,distanceLineOfSightMaximum_)
contains
@@ -78,7 +77,6 @@ function radiusEinsteinConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: redshiftSource
!![
@@ -89,24 +87,21 @@ function radiusEinsteinConstructorParameters(parameters) result(self)
-
!!]
self=nodePropertyExtractorRadiusEinstein( &
& cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshiftSource)), &
& cosmologyFunctions_ , &
- & darkMatterHaloScale_ , &
- & galacticStructure_ &
+ & darkMatterHaloScale_ &
& )
!![
-
!!]
return
end function radiusEinsteinConstructorParameters
- function radiusEinsteinConstructorInternal(timeSource,cosmologyFunctions_,darkMatterHaloScale_,galacticStructure_) result(self)
+ function radiusEinsteinConstructorInternal(timeSource,cosmologyFunctions_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the ``radiusEinstein'' node property extractor.
!!}
@@ -115,10 +110,9 @@ function radiusEinsteinConstructorInternal(timeSource,cosmologyFunctions_,darkMa
type (nodePropertyExtractorRadiusEinstein) :: self
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: timeSource
!![
-
+
!!]
! Compute corresponding redshift - these are needed for the descriptor.
@@ -154,7 +148,6 @@ subroutine radiusEinsteinDestructor(self)
!![
-
!!]
return
end subroutine radiusEinsteinDestructor
@@ -178,7 +171,7 @@ double precision function radiusEinsteinExtract(self,node,instance)
& *arcsecondstoDegrees &
& *degreesToRadians
double precision :: distanceAngularLensSource , distanceAngularLens, &
- & distanceAngularSource , massTotal
+ & distanceAngularSource
!$GLC attributes unused :: instance
radiusEinsteinExtract=-1.0d0
@@ -201,12 +194,11 @@ double precision function radiusEinsteinExtract(self,node,instance)
& /distanceAngularLens &
& /distanceAngularLensSource
! Find the outer radius of the halo.
- satellite => node %satellite ( )
- massTotal = self%galacticStructure_%massEnclosed (node )
- distanceLineOfSightMaximum_ = self%galacticStructure_%radiusEnclosingMass(node,mass=min(satellite%boundMass(),massTotal))
+ massDistribution_ => node%massDistribution()
+ satellite => node %satellite ( )
+ distanceLineOfSightMaximum_ = massDistribution_%radiusEnclosingMass(min(satellite%boundMass(),basic%mass()))
! Find the radius within which the mean projected surface density equals the critical density.
self_ => self
- node_ => node
if (radiusEinsteinProjectedDensityRoot(radiusEinsteinTiny*distanceAngularLens) < 0.0d0) then
! For extremely tiny Einstein radii we simply return zero, to avoid unnecessary computation.
radiusEinsteinExtract=+0.0d0
@@ -263,25 +255,19 @@ double precision function radiusEinsteinProjectedDensityIntegrandLineOfSight(dis
!!{
Integrand function used in finding the mean enclosed projected density.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll, massTypeAll
+ use :: Galactic_Structure_Options, only : componentTypeAll , massTypeAll
+ use :: Coordinates , only : coordinateSpherical, assignment(=)
implicit none
- double precision, intent(in ) :: distance
- double precision :: radius
+ double precision , intent(in ) :: distance
+ double precision :: radius
+ type (coordinateSpherical) :: coordinates
radius =+sqrt( &
& +distance **2 &
& +radiusImpact_**2 &
& )
- radiusEinsteinProjectedDensityIntegrandLineOfSight=+self_%galacticStructure_%density( &
- & node_ , &
- & [ &
- & radius , &
- & 0.0d0 , &
- & 0.0d0 &
- & ] , &
- & componentType=componentTypeAll, &
- & massType =massTypeAll &
- & )
+ coordinates = [radius,0.0d0,0.0d0]
+ radiusEinsteinProjectedDensityIntegrandLineOfSight=+massDistribution_%density(coordinates)
return
end function radiusEinsteinProjectedDensityIntegrandLineOfSight
diff --git a/source/nodes.property_extractor.radius_velocity_maximum.F90 b/source/nodes.property_extractor.radius_velocity_maximum.F90
index 0279ef816e..620c75f180 100644
--- a/source/nodes.property_extractor.radius_velocity_maximum.F90
+++ b/source/nodes.property_extractor.radius_velocity_maximum.F90
@@ -116,14 +116,20 @@ double precision function radiusVelocityMaximumExtract(self,node,instance)
!!{
Implement a radius of maximum velocity output analysis.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class(nodePropertyExtractorRadiusVelocityMaximum), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
+ class(massDistributionClass ) , pointer :: massDistribution_
!$GLC attributes unused :: instance
- radiusVelocityMaximumExtract=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum(node)
+ massDistribution_ => self %darkMatterProfileDMO_%get (node)
+ radiusVelocityMaximumExtract = massDistribution_ %radiusRotationCurveMaximum( )
+ !![
+
+ !!]
return
end function radiusVelocityMaximumExtract
diff --git a/source/nodes.property_extractor.radius_virial.F90 b/source/nodes.property_extractor.radius_virial.F90
index eafd1fd2cc..31c5324f62 100644
--- a/source/nodes.property_extractor.radius_virial.F90
+++ b/source/nodes.property_extractor.radius_virial.F90
@@ -21,10 +21,9 @@
Contains a module which implements a virial radius output analysis property extractor class.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -40,7 +39,6 @@
radius).
!!}
private
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -73,7 +71,6 @@ function radiusVirialConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
logical :: useLastIsolatedTime
@@ -86,23 +83,21 @@ function radiusVirialConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorRadiusVirial(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=nodePropertyExtractorRadiusVirial(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_)
!![
-
!!]
return
end function radiusVirialConstructorParameters
- function radiusVirialConstructorInternal(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
+ function radiusVirialConstructorInternal(useLastIsolatedTime,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_) result(self)
!!{
Internal constructor for the ``radiusVirial'' output analysis property extractor class.
!!}
@@ -111,10 +106,9 @@ function radiusVirialConstructorInternal(useLastIsolatedTime,cosmologyFunctions_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_, virialDensityContrastDefinition_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
logical , intent(in ) :: useLastIsolatedTime
!![
-
+
!!]
return
@@ -131,7 +125,6 @@ subroutine radiusVirialDestructor(self)
-
!!]
return
@@ -163,7 +156,6 @@ double precision function radiusVirialExtract(self,node,instance)
& radius = radiusVirialExtract , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ , &
& useLastIsolatedTime =self%useLastIsolatedTime &
& )
diff --git a/source/nodes.property_extractor.rotation_curve.F90 b/source/nodes.property_extractor.rotation_curve.F90
index 35a7303f90..c45c0325a3 100644
--- a/source/nodes.property_extractor.rotation_curve.F90
+++ b/source/nodes.property_extractor.rotation_curve.F90
@@ -22,7 +22,6 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -41,7 +40,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii
type (varying_string ), allocatable, dimension(:) :: radiusSpecifiers
@@ -80,7 +78,6 @@ function rotationCurveConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: includeRadii
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
@@ -97,18 +94,16 @@ function rotationCurveConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorRotationCurve(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorRotationCurve(radiusSpecifiers,includeRadii,darkMatterHaloScale_)
!![
-
!!]
return
end function rotationCurveConstructorParameters
- function rotationCurveConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_,galacticStructure_) result(self)
+ function rotationCurveConstructorInternal(radiusSpecifiers,includeRadii,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily rotationCurve} property extractor class.
!!}
@@ -117,10 +112,9 @@ function rotationCurveConstructorInternal(radiusSpecifiers,includeRadii,darkMatt
type (nodePropertyExtractorRotationCurve) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: includeRadii
!![
-
+
!!]
if (includeRadii) then
@@ -150,7 +144,6 @@ subroutine rotationCurveDestructor(self)
!![
-
!!]
return
end subroutine rotationCurveDestructor
@@ -192,6 +185,7 @@ function rotationCurveExtract(self,node,time,instance)
& radiusTypeStellarMassFraction , radiusTypeVirialRadius
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Error , only : Error_Report
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
double precision , dimension(:,:), allocatable :: rotationCurveExtract
class (nodePropertyExtractorRotationCurve), intent(inout) , target :: self
@@ -201,6 +195,7 @@ function rotationCurveExtract(self,node,time,instance)
class (nodeComponentDisk ), pointer :: disk
class (nodeComponentSpheroid ), pointer :: spheroid
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ class (massDistributionClass ), pointer :: massDistribution_
integer :: i
double precision :: radius , radiusVirial
!$GLC attributes unused :: time, instance
@@ -230,38 +225,48 @@ function rotationCurveExtract(self,node,time,instance)
radius=+radius*spheroid %halfMassRadius()
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case (radiusTypeStellarMassFraction %ID)
- radius=+radius &
- & *self%galacticStructure_%radiusEnclosingMass &
- & ( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radius = +radius &
+ & *massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
- rotationCurveExtract (i,1)=self%galacticStructure_%velocityRotation( &
- & node , &
- & radius , &
- & componentType=self%radii(i)%component, &
- & massType =self%radii(i)%mass &
- & )
- if (self%includeRadii) &
- & rotationCurveExtract(i,2)= radius
+ massDistribution_ => node %massDistribution( &
+ & componentType=self%radii(i)%component, &
+ & massType =self%radii(i)%mass &
+ & )
+ rotationCurveExtract (i,1) = massDistribution_%rotationCurve ( &
+ & radius &
+ & )
+ if (self%includeRadii) &
+ & rotationCurveExtract(i,2) = radius
+ !![
+
+ !!]
end do
return
end function rotationCurveExtract
diff --git a/source/nodes.property_extractor.satellite.dynamical_time.F90 b/source/nodes.property_extractor.satellite.dynamical_time.F90
index fc144bf583..8a1571bc85 100644
--- a/source/nodes.property_extractor.satellite.dynamical_time.F90
+++ b/source/nodes.property_extractor.satellite.dynamical_time.F90
@@ -25,7 +25,6 @@
!!}
use :: Satellite_Tidal_Stripping_Radii, only : satelliteTidalStrippingRadiusClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -46,7 +45,6 @@
!!}
private
class(satelliteTidalStrippingRadiusClass), pointer :: satelliteTidalStrippingRadius_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: dynamicalTimeDestructor
procedure :: extract => dynamicalTimeExtract
@@ -74,31 +72,27 @@ function dynamicalTimeConstructorParameters(parameters) result(self)
type (nodePropertyExtractorSatelliteDynamicalTime) :: self
type (inputParameters ), intent(inout) :: parameters
class(satelliteTidalStrippingRadiusClass ), pointer :: satelliteTidalStrippingRadius_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=nodePropertyExtractorSatelliteDynamicalTime(satelliteTidalStrippingRadius_, galacticStructure_)
+ self=nodePropertyExtractorSatelliteDynamicalTime(satelliteTidalStrippingRadius_)
!![
-
!!]
return
end function dynamicalTimeConstructorParameters
- function dynamicalTimeConstructorInternal(satelliteTidalStrippingRadius_, galacticStructure_) result(self)
+ function dynamicalTimeConstructorInternal(satelliteTidalStrippingRadius_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily satelliteDynamicalTime} property extractor class.
!!}
implicit none
type (nodePropertyExtractorSatelliteDynamicalTime) :: self
class(satelliteTidalStrippingRadiusClass ), intent(in ), target :: satelliteTidalStrippingRadius_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -113,7 +107,6 @@ subroutine dynamicalTimeDestructor(self)
!![
-
!!]
return
end subroutine dynamicalTimeDestructor
@@ -122,18 +115,25 @@ double precision function dynamicalTimeExtract(self,node,instance)
!!{
Implement a dynamical time property extractor.
!!}
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
implicit none
class (nodePropertyExtractorSatelliteDynamicalTime), intent(inout), target :: self
type (treeNode ), intent(inout), target :: node
type (multiCounter ), intent(inout), optional :: instance
- double precision :: radiusTidal, massTidal
+ class (massDistributionClass ) , pointer :: massDistribution_
+ double precision :: radiusTidal , massTidal
!$GLC attributes unused :: instance
- dynamicalTimeExtract =-1.0d0
- radiusTidal =self%satelliteTidalStrippingRadius_%radius (node )
+
+ dynamicalTimeExtract=-1.0d0
+ radiusTidal =self%satelliteTidalStrippingRadius_%radius(node)
if (radiusTidal <= 0.0d0) return
- massTidal =self%galacticStructure_ %massEnclosed(node,radiusTidal)
+ massDistribution_ => node %massDistribution ( )
+ massTidal = massDistribution_%massEnclosedBySphere(radiusTidal)
+ !![
+
+ !!]
if (massTidal <= 0.0d0) return
dynamicalTimeExtract =+sqrt( &
& +Pi **2 &
diff --git a/source/nodes.property_extractor.satellite_orbital_extrema.F90 b/source/nodes.property_extractor.satellite_orbital_extrema.F90
index 56abe97a35..9ad2d283ca 100644
--- a/source/nodes.property_extractor.satellite_orbital_extrema.F90
+++ b/source/nodes.property_extractor.satellite_orbital_extrema.F90
@@ -21,7 +21,7 @@
Contains a module which implements satellite orbital extrema property extractor class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -36,10 +36,10 @@
A satellite orbital extrema property extractor class.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
- integer :: offsetPericenter , offsetApocenter , &
- & elementCount_
- logical :: extractPericenter , extractApocenter
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ integer :: offsetPericenter , offsetApocenter , &
+ & elementCount_
+ logical :: extractPericenter , extractApocenter
contains
final :: satelliteOrbitalExtremaDestructor
procedure :: elementCount => satelliteOrbitalExtremaElementCount
@@ -67,8 +67,8 @@ function satelliteOrbitalExtremaConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorSatelliteOrbitalExtrema) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
- logical :: extractPericenter , extractApocenter
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ logical :: extractPericenter , extractApocenter
!![
@@ -83,17 +83,17 @@ function satelliteOrbitalExtremaConstructorParameters(parameters) result(self)
Specifies whether or not satellite orbital apocenter data (radius, velocity) should be extracted.
-
+
!!]
- self=nodePropertyExtractorSatelliteOrbitalExtrema(extractPericenter,extractApocenter,galacticStructure_)
+ self=nodePropertyExtractorSatelliteOrbitalExtrema(extractPericenter,extractApocenter,darkMatterHaloScale_)
!![
-
+
!!]
return
end function satelliteOrbitalExtremaConstructorParameters
- function satelliteOrbitalExtremaConstructorInternal(extractPericenter,extractApocenter,galacticStructure_) result(self)
+ function satelliteOrbitalExtremaConstructorInternal(extractPericenter,extractApocenter,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily satelliteOrbitalExtrema} property extractor class.
!!}
@@ -101,9 +101,9 @@ function satelliteOrbitalExtremaConstructorInternal(extractPericenter,extractApo
implicit none
type (nodePropertyExtractorSatelliteOrbitalExtrema) :: self
logical , intent(in ) :: extractPericenter , extractApocenter
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
self%elementCount_=0
@@ -127,7 +127,7 @@ subroutine satelliteOrbitalExtremaDestructor(self)
type(nodePropertyExtractorSatelliteOrbitalExtrema), intent(inout) :: self
!![
-
+
!!]
return
end subroutine satelliteOrbitalExtremaDestructor
@@ -170,7 +170,7 @@ function satelliteOrbitalExtremaExtract(self,node,time,instance)
nodeHost => node %parent
satellite => node %satellite ()
orbit = satellite%virialOrbit()
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%darkMatterHaloScale_)
else
radiusOrbital =0.0d0
velocityOrbital=0.0d0
@@ -182,7 +182,7 @@ function satelliteOrbitalExtremaExtract(self,node,time,instance)
nodeHost => node %parent
satellite => node %satellite ()
orbit = satellite%virialOrbit()
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumApocenter ,radiusOrbital,velocityOrbital,self%galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumApocenter ,radiusOrbital,velocityOrbital,self%darkMatterHaloScale_)
else
radiusOrbital =0.0d0
velocityOrbital=0.0d0
diff --git a/source/nodes.property_extractor.spin_parameter.F90 b/source/nodes.property_extractor.spin_parameter.F90
index 6cfd882de5..8e6fe81e0b 100644
--- a/source/nodes.property_extractor.spin_parameter.F90
+++ b/source/nodes.property_extractor.spin_parameter.F90
@@ -21,8 +21,8 @@
Contains a module which implements a spin parameter output analysis property extractor class.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMO, darkMatterProfileDMOClass
-
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+
!![
A spin parameter output analysis property extractor class.
@@ -33,7 +33,7 @@
A spin parameter property extractor output analysis class.
!!}
private
- class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
final :: spinDestructor
procedure :: extract => spinExtract
@@ -60,29 +60,29 @@ function spinConstructorParameters(parameters) result(self)
implicit none
type (nodePropertyExtractorSpin) :: self
type (inputParameters ), intent(inout) :: parameters
- class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
!$GLC attributes unused :: parameters
!![
-
+
!!]
- self=nodePropertyExtractorSpin(darkMatterProfileDMO_)
+ self=nodePropertyExtractorSpin(darkMatterHaloScale_)
!![
-
+
!!]
return
end function spinConstructorParameters
- function spinConstructorInternal(darkMatterProfileDMO_) result(self)
+ function spinConstructorInternal(darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily spin} output analysis property extractor class.
!!}
implicit none
type (nodePropertyExtractorSpin) :: self
- class(darkMatterProfileDMOClass), intent(in ), target :: darkMatterProfileDMO_
+ class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
return
@@ -96,7 +96,7 @@ subroutine spinDestructor(self)
type(nodePropertyExtractorSpin), intent(inout) :: self
!![
-
+
!!]
return
end subroutine spinDestructor
@@ -115,8 +115,8 @@ double precision function spinExtract(self,node,instance)
!$GLC attributes unused :: self, instance
spin => node %spin()
- spinExtract = +spin%angularMomentum () &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_)
+ spinExtract = +spin%angularMomentum () &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkmatterHaloScale_)
return
end function spinExtract
diff --git a/source/nodes.property_extractor.tidally_truncated_NFW_fit.F90 b/source/nodes.property_extractor.tidally_truncated_NFW_fit.F90
index 2721456fa9..8baed838f1 100644
--- a/source/nodes.property_extractor.tidally_truncated_NFW_fit.F90
+++ b/source/nodes.property_extractor.tidally_truncated_NFW_fit.F90
@@ -25,7 +25,6 @@
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass, darkMatterProfileDMONFW
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -46,7 +45,6 @@
type (darkMatterProfileDMONFW ), pointer :: darkMatterProfileDMONFW_ => null()
class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: tidallyTruncatedNFWFitDestructor
procedure :: elementCount => tidallyTruncatedNFWFitElementCount
@@ -76,24 +74,21 @@ function tidallyTruncatedNFWFitConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=nodePropertyExtractorTidallyTruncatedNFWFit(darkMatterHaloScale_,galacticStructure_,darkMatterProfileDMO_)
+ self=nodePropertyExtractorTidallyTruncatedNFWFit(darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
!!]
return
end function tidallyTruncatedNFWFitConstructorParameters
- function tidallyTruncatedNFWFitConstructorInternal(darkMatterHaloScale_,galacticStructure_,darkMatterProfileDMO_) result(self)
+ function tidallyTruncatedNFWFitConstructorInternal(darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the ``tidallyTruncatedNFWFit'' output analysis property extractor class.
!!}
@@ -101,9 +96,8 @@ function tidallyTruncatedNFWFitConstructorInternal(darkMatterHaloScale_,galactic
type (nodePropertyExtractorTidallyTruncatedNFWFit) :: self
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
allocate(self%darkMatterProfileDMONFW_)
@@ -131,7 +125,6 @@ subroutine tidallyTruncatedNFWFitDestructor(self)
-
!!]
return
end subroutine tidallyTruncatedNFWFitDestructor
@@ -154,9 +147,11 @@ function tidallyTruncatedNFWFitExtract(self,node,time,instance)
Implement a tidallyTruncatedNFWFit output analysis.
!!}
use, intrinsic :: ISO_C_Binding , only : c_size_t
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile, nodeComponentSatellite
use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
use :: Multidimensional_Minimizer, only : multiDMinimizer
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
double precision , allocatable , dimension(:) :: tidallyTruncatedNFWFitExtract
class (nodePropertyExtractorTidallyTruncatedNFWFit), intent(inout), target :: self
@@ -165,41 +160,49 @@ function tidallyTruncatedNFWFitExtract(self,node,time,instance)
type (multiCounter ), intent(inout), optional :: instance
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionNFW
double precision , allocatable , dimension(:) :: radii , fractionDensity
type (multiDMinimizer ), allocatable :: minimizer_
double precision , dimension(1) :: locationMinimum
- double precision , parameter :: fractionRadiusScale =0.1d0, fractionMaximum =0.1d0, &
- & radiusMaximumFractionDensityVirialMinimum=0.1d0, fractionStep =0.1d0, &
+ double precision , parameter :: fractionRadiusScale =0.1d0, fractionMaximum =0.1d0, &
+ & radiusMaximumFractionDensityVirialMinimum=0.1d0, fractionStep =0.1d0, &
& radiusMaximumScaleVirialMaximum =1.0d1
- integer , parameter :: radiusMaximumCountRadiiPerDecade =10 , countRadiiPerDecade=10
- integer :: countRadii , i , &
+ integer , parameter :: radiusMaximumCountRadiiPerDecade =10 , countRadiiPerDecade =10
+ integer :: countRadii , i , &
& iteration , radiusMaximumCountRadii
logical :: converged
- double precision :: radiusOuter , massTotal , &
- & radiusMinimum , radiusMaximum , &
- & radiusScale , radiusVirial , &
+ double precision :: radiusOuter , massTotal , &
+ & radiusMinimum , radiusMaximum , &
+ & radiusScale , radiusVirial , &
& radiusMaximumFractionDensityVirial , factorStepRadius
+ type (coordinateSpherical ) :: coordinates , coordinatesMaximum , &
+ & coordinatesVirial
!$GLC attributes unused :: instance
allocate(tidallyTruncatedNFWFitExtract(3))
- darkMatterProfile => node %darkMatterProfile( )
- tidallyTruncatedNFWFitExtract(3) = self%darkMatterProfileDMONFW_%density (node,darkMatterProfile%scale())
+ darkMatterProfile => node %darkMatterProfile( )
+ massDistribution_ => self %darkMatterProfileDMO_ %get (node )
+ massDistributionNFW => self %darkMatterProfileDMONFW_%get (node )
+ coordinates = [darkMatterProfile%scale(),0.0d0,0.0d0]
+ tidallyTruncatedNFWFitExtract(3) = massDistributionNFW %density (coordinates)
if (node%isSatellite()) then
! Extract required properties.
- satellite => node %satellite ( )
- massTotal = self %galacticStructure_ %massEnclosed (node )
- radiusOuter = self %galacticStructure_ %radiusEnclosingMass(node,mass=min(satellite%boundMass(),massTotal))
- radiusScale = darkMatterProfile %scale ( )
- radiusVirial = self %darkMatterHaloScale_%radiusVirial (node )
+ radiusVirial = self %darkMatterHaloScale_%radiusVirial ( node )
+ satellite => node %satellite ( )
+ massTotal = massDistribution_ %massEnclosedBySphere(radius=radiusVirial )
+ radiusOuter = massDistribution_ %radiusEnclosingMass (mass =min(satellite%boundMass(),massTotal))
+ radiusScale = darkMatterProfile %scale ( )
! Choose radii for fitting.
radiusMaximum=radiusVirial
if (radiusOuter > radiusVirial) then
radiusMaximumCountRadii=int(log10(radiusMaximumScaleVirialMaximum)*dble(radiusMaximumCountRadiiPerDecade)+1.0d0)
- factorStepRadius = log10(radiusMaximumScaleVirialMaximum)/dble(radiusMaximumCountRadii )
+ factorStepRadius = log10(radiusMaximumScaleVirialMaximum)/dble(radiusMaximumCountRadii )
+ coordinatesVirial =[radiusVirial,0.0d0,0.0d0]
do i=1,radiusMaximumCountRadii
radiusMaximum =10.0d0**(log10(radiusVirial)+factorStepRadius*dble(i))
- radiusMaximumFractionDensityVirial=+self%darkMatterProfileDMO_%density(node,radiusMaximum) &
- & /self%darkMatterProfileDMO_%density(node,radiusVirial )
+ coordinatesMaximum =[radiusMaximum,0.0d0,0.0d0]
+ radiusMaximumFractionDensityVirial=+massDistribution_%density(coordinatesMaximum) &
+ & /massDistribution_%density(coordinatesVirial )
if (radiusMaximumFractionDensityVirial < radiusMaximumFractionDensityVirialMinimum) exit
end do
end if
@@ -209,8 +212,9 @@ function tidallyTruncatedNFWFitExtract(self,node,time,instance)
! Tabulate the density ratio relative to an NFW profile.
allocate(fractionDensity(countRadii))
do i=1,countRadii
- fractionDensity(i)=+self%darkMatterProfileDMO_ %density(node,radii(i)) &
- & /self%darkMatterProfileDMONFW_%density(node,radii(i))
+ coordinates=[radii(i),0.0d0,0.0d0]
+ fractionDensity(i)=+massDistribution_ %density(coordinates) &
+ & /massDistributionNFW%density(coordinates)
end do
! Optimize the fit.
allocate(minimizer_)
@@ -230,6 +234,10 @@ function tidallyTruncatedNFWFitExtract(self,node,time,instance)
tidallyTruncatedNFWFitExtract(1)=huge(0.0d0)
tidallyTruncatedNFWFitExtract(2)= 0.0d0
end if
+ !![
+
+
+ !!]
return
contains
diff --git a/source/nodes.property_extractor.velocity_dispersion.F90 b/source/nodes.property_extractor.velocity_dispersion.F90
index de715a8c61..f65bed6440 100644
--- a/source/nodes.property_extractor.velocity_dispersion.F90
+++ b/source/nodes.property_extractor.velocity_dispersion.F90
@@ -22,9 +22,9 @@
!!}
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass
use :: Galactic_Structure_Radii_Definitions, only : radiusSpecifier
- use :: Galactic_Structure , only : galacticStructureClass
use :: Galactic_Structure_Options , only : enumerationMassTypeType, enumerationComponentTypeType, enumerationWeightByType
-
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
+
!![
@@ -51,7 +51,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
integer :: radiiCount , elementCount_
logical :: includeRadii , integrationFailureIsFatal
double precision :: toleranceRelative
@@ -80,14 +79,18 @@
end interface nodePropertyExtractorVelocityDispersion
! Module-scope variables used in integrands.
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionStellarDisk_ , &
+ & massDistributionWeighted_ , massDistributionStellarSpheroid_ , &
+ & massDistributionTotal_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_ , kinematicsDistributionStellarDisk_, &
+ & kinematicsDistributionStellarSpheroid_
class (nodePropertyExtractorVelocityDispersion), pointer :: self_
- type (treeNode ), pointer :: node_
type (enumerationMassTypeType ) :: massType_
type (enumerationComponentTypeType ) :: componentType_
type (enumerationWeightByType ) :: weightBy_
integer :: weightIndex_
double precision :: radiusImpact_ , radiusOuter_
- !$omp threadprivate(self_,node_,weightBy_,componentType_,massType_,weightIndex_,radiusImpact_,radiusOuter_)
+ !$omp threadprivate(massDistribution_,massDistributionWeighted_,massDistributionStellarDisk_,massDistributionStellarSpheroid_,massDistributionTotal_,kinematicsDistribution_,kinematicsDistributionStellarDisk_,kinematicsDistributionStellarSpheroid_,self_,weightBy_,componentType_,massType_,weightIndex_,radiusImpact_,radiusOuter_)
contains
@@ -97,13 +100,12 @@ function velocityDispersionConstructorParameters(parameters) result(self)
!!}
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
- type (nodePropertyExtractorVelocityDispersion) :: self
- type (inputParameters ), intent(inout) :: parameters
- type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
- double precision :: toleranceRelative
- logical :: includeRadii , integrationFailureIsFatal
+ type (nodePropertyExtractorVelocityDispersion) :: self
+ type (inputParameters ), intent(inout) :: parameters
+ type (varying_string ), allocatable , dimension(:) :: radiusSpecifiers
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ double precision :: toleranceRelative
+ logical :: includeRadii , integrationFailureIsFatal
allocate(radiusSpecifiers(parameters%count('radiusSpecifiers')))
!![
@@ -131,18 +133,16 @@ function velocityDispersionConstructorParameters(parameters) result(self)
-
!!]
- self=nodePropertyExtractorVelocityDispersion(radiusSpecifiers,includeRadii,integrationFailureIsFatal,toleranceRelative,darkMatterHaloScale_,galacticStructure_)
+ self=nodePropertyExtractorVelocityDispersion(radiusSpecifiers,includeRadii,integrationFailureIsFatal,toleranceRelative,darkMatterHaloScale_)
!![
-
!!]
return
end function velocityDispersionConstructorParameters
- function velocityDispersionConstructorInternal(radiusSpecifiers,includeRadii,integrationFailureIsFatal,toleranceRelative,darkMatterHaloScale_,galacticStructure_) result(self)
+ function velocityDispersionConstructorInternal(radiusSpecifiers,includeRadii,integrationFailureIsFatal,toleranceRelative,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily velocityDispersion} property extractor class.
!!}
@@ -151,11 +151,10 @@ function velocityDispersionConstructorInternal(radiusSpecifiers,includeRadii,int
type (nodePropertyExtractorVelocityDispersion) :: self
type (varying_string ), intent(in ), dimension(:) :: radiusSpecifiers
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: includeRadii , integrationFailureIsFatal
double precision , intent(in ) :: toleranceRelative
!![
-
+
!!]
if (includeRadii) then
@@ -185,7 +184,6 @@ subroutine velocityDispersionDestructor(self)
!![
-
!!]
return
end subroutine velocityDispersionDestructor
@@ -222,12 +220,13 @@ function velocityDispersionExtract(self,node,time,instance)
Implement a {\normalfont \ttfamily velocityDispersion} property extractor.
!!}
use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , componentTypeSpheroid , massTypeGalactic , &
- & massTypeStellar , radiusLarge
+ & massTypeStellar , massTypeAll
use :: Galactic_Structure_Radii_Definitions, only : directionLambdaR , directionLineOfSight , directionLineOfSightInteriorAverage, directionRadial , &
& radiusTypeDarkMatterScaleRadius, radiusTypeDiskHalfMassRadius, radiusTypeDiskRadius , radiusTypeGalacticLightFraction, &
& radiusTypeGalacticMassFraction , radiusTypeRadius , radiusTypeSpheroidHalfMassRadius , radiusTypeSpheroidRadius , &
& radiusTypeStellarMassFraction , radiusTypeVirialRadius
use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfile , nodeComponentDisk , nodeComponentSpheroid , treeNode
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Numerical_Integration , only : integrator
use :: Error , only : Error_Report
implicit none
@@ -249,6 +248,7 @@ function velocityDispersionExtract(self,node,time,instance)
logical :: scaleIsZero
type (integrator ) :: integratorVelocitySurfaceDensity , integratorSurfaceDensity, &
& integratorLambdaR2 , integratorLambdaR1
+ type (coordinateSpherical ) :: coordinates
!$GLC attributes unused :: time, instance
integratorVelocitySurfaceDensity=integrator(velocityDispersionVelocitySurfaceDensityIntegrand,toleranceRelative=1.0d-3)
@@ -291,27 +291,35 @@ function velocityDispersionExtract(self,node,time,instance)
scaleIsZero =(spheroid %halfMassRadius() <= 0.0d0)
case (radiusTypeGalacticMassFraction %ID, &
& radiusTypeGalacticLightFraction %ID )
- radiusFromFraction=+self%galacticStructure_%radiusEnclosingMass( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeGalactic, &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
- radius = radius*radiusFromFraction
- radiusOuter_=max(radius,radiusFromFraction)*outerRadiusMultiplier
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeGalactic, &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radiusFromFraction = +massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ radius = +radius*radiusFromFraction
+ radiusOuter_ = max(radius,radiusFromFraction)*outerRadiusMultiplier
+ !![
+
+ !!]
case (radiusTypeStellarMassFraction %ID)
- radiusFromFraction=+self%galacticStructure_%radiusEnclosingMass( &
- & node , &
- & massFractional=self%radii(i)%fraction , &
- & massType = massTypeStellar , &
- & componentType = componentTypeAll, &
- & weightBy =self%radii(i)%weightBy , &
- & weightIndex =self%radii(i)%weightByIndex &
- & )
- radius = radius*radiusFromFraction
- radiusOuter_=max(radius,radiusFromFraction)*outerRadiusMultiplier
+ massDistribution_ => node %massDistribution ( &
+ & massType = massTypeStellar , &
+ & componentType = componentTypeAll, &
+ & weightBy =self%radii(i)%weightBy , &
+ & weightIndex =self%radii(i)%weightByIndex &
+ & )
+ radiusFromFraction = +massDistribution_%radiusEnclosingMass( &
+ & massFractional=self%radii(i)%fraction &
+ & )
+ radius = +radius*radiusFromFraction
+ radiusOuter_ = max(radius,radiusFromFraction)*outerRadiusMultiplier
+ !![
+
+ !!]
case default
call Error_Report('unrecognized radius type'//{introspection:location})
end select
@@ -319,20 +327,18 @@ function velocityDispersionExtract(self,node,time,instance)
! Do not compute dispersions if the component scale is zero.
velocityDispersionExtract(i,1)=0.0d0
else
+ massDistribution_ => node % massDistribution(componentType=self%radii(i)%component ,massType=self%radii(i)%mass )
+ massDistributionWeighted_ => node % massDistribution(componentType=self%radii(i)%component ,massType=self%radii(i)%mass ,weightBy=self%radii(i)%weightBy,weightIndex=self%radii(i)%weightByIndex)
+ massDistributionTotal_ => node % massDistribution(componentType= componentTypeAll,massType= massTypeAll )
+ kinematicsDistribution_ => massDistribution_%kinematicsDistribution( )
select case (self%radii(i)%direction%ID)
case (directionRadial %ID)
! Radial velocity dispersion.
- velocityDispersionExtract(i,1)=self%galacticStructure_%velocityDispersion( &
- & node , &
- & radius , &
- & radiusOuter_ , &
- & massType =self%radii(i)%mass , &
- & componentType=self%radii(i)%component &
- & )
+ coordinates =[radius,0.0d0,0.0d0]
+ velocityDispersionExtract(i,1)=kinematicsDistribution_%velocityDispersion1D(coordinates,massDistributionTotal_)
case (directionLineOfSight %ID)
! Line-of-sight velocity dispersion.
self_ => self
- node_ => node
massType_ = self%radii(i)%mass
componentType_ = self%radii(i)%component
weightBy_ = self%radii(i)%integralWeightBy
@@ -342,7 +348,6 @@ function velocityDispersionExtract(self,node,time,instance)
case (directionLineOfSightInteriorAverage%ID)
! Average over the line-of-sight velocity dispersion within the radius.
self_ => self
- node_ => node
massType_ = self%radii(i)%mass
componentType_ = self%radii(i)%component
weightBy_ = self%radii(i)%integralWeightBy
@@ -359,45 +364,42 @@ function velocityDispersionExtract(self,node,time,instance)
case (directionLambdaR %ID)
! The "lambdaR" parameter of Cappellari et al. (2007; MNRAS; 379; 418)
self_ => self
- node_ => node
massType_ = self%radii(i)%mass
componentType_ = self%radii(i)%component
weightBy_ = self%radii(i)%integralWeightBy
weightIndex_ = self%radii(i)%integralWeightByIndex
! Check the total masses of the disk and spheroid components. If either is zero we can use the solutions for the
! appropriate limiting case.
- massSpheroid=self%galacticStructure_%massEnclosed( &
- & node , &
- & radiusLarge , &
- & massType =massTypeStellar , &
- & componentType=componentTypeSpheroid , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & )
- massDisk =self%galacticStructure_%massEnclosed( &
- & node , &
- & radiusLarge , &
- & massType =massTypeStellar , &
- & componentType=componentTypeDisk , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & )
+ massDistributionStellarDisk_ => node%massDistribution(componentType=componentTypeDisk ,massType=massTypeStellar,weightBy=weightBy_,weightIndex=weightIndex_)
+ massDistributionStellarSpheroid_ => node%massDistribution(componentType=componentTypeSpheroid,massType=massTypeStellar,weightBy=weightBy_,weightIndex=weightIndex_)
+ massSpheroid=massDistributionStellarSpheroid_%massTotal()
+ massDisk =massDistributionStellarDisk_ %massTotal()
if (massDisk <= 0.0d0) then
velocityDispersionExtract(i,1)=0.0d0
else if (massSpheroid <= 0.0d0) then
velocityDispersionExtract(i,1)=1.0d0
else
! Full calculation is required.
- radiusZero=0.0d0
- numerator =integratorLambdaR2%integrate(radiusZero,radius)
- denominator=integratorLambdaR1%integrate(radiusZero,radius)
+ radiusZero = 0.0d0
+ numerator = integratorLambdaR2%integrate(radiusZero,radius)
+ denominator = integratorLambdaR1%integrate(radiusZero,radius)
if (denominator <= 0.0d0) then
velocityDispersionExtract(i,1)=0.0d0
else
velocityDispersionExtract(i,1)=numerator/denominator
end if
end if
+ !![
+
+
+ !!]
end select
+ !![
+
+
+
+
+ !!]
end if
if (self%includeRadii) &
& velocityDispersionExtract(i,2)=radius
@@ -479,7 +481,6 @@ function velocityDispersionUnitsInSI(self,time)
return
end function velocityDispersionUnitsInSI
-
double precision function velocityDispersionLambdaRIntegrand1(radius)
!!{
Integrand function used for integrating the $\lambda_\mathrm{R}$ statistic of \cite{cappellari_sauron_2007}. In this case we
@@ -501,37 +502,27 @@ width $\sigma_\mathrm{s}(r)$ and normalized area $\Sigma_\mathrm{s}(r)$, and a d
\sigma^2(r) = \left. \int_{-\infty}^{+\infty} P(V) [V-V(r)]^2 \mathrm{d}V \right/ \int_{-\infty}^{+\infty} P(V) \mathrm{d}V = { \Sigma_\mathrm{s}(r) [\sigma_\mathrm{s}^2(r)] + \Sigma_\mathrm{d}(r) [V_\mathrm{d}(r)-V(r)]^2 \over [\Sigma_\mathrm{d}(r)+\Sigma_\mathrm{s}(r)]}.
\end{equation}
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll, componentTypeDisk, massTypeAll, massTypeStellar
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Integration , only : integrator
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Numerical_Integration , only : integrator
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
implicit none
- double precision , intent(in ) :: radius
- double precision , parameter :: fractionSmall =1.0d-3
- type (integrator) :: integratorDensity , integratorVelocityDensity
- double precision :: sigmaLineOfSightSquaredSpheroidDensity , densitySpheroid , &
- & densityDisk , velocityDisk , &
- & velocityMean , sigmaLineOfSightSquared
+ double precision , intent(in ) :: radius
+ double precision , parameter :: fractionSmall =1.0d-3
+ type (integrator ) :: integratorDensity , integratorVelocityDensity
+ double precision :: sigmaLineOfSightSquaredSpheroidDensity , densitySpheroid , &
+ & densityDisk , velocityDisk , &
+ & velocityMean , sigmaLineOfSightSquared
+ type (coordinateCylindrical) :: coordinates
if (radius <= 0.0d0) then
velocityDispersionLambdaRIntegrand1=0.0d0
else
- radiusImpact_=radius
- integratorDensity =integrator (velocityDispersionDensityIntegrand,toleranceRelative=1.0d-2)
- densitySpheroid =integratorDensity %integrate (radius ,radiusOuter_ )
- densityDisk =self_%galacticStructure_%surfaceDensity ( &
- & node_ , &
- & [radius,0.0d0,0.0d0], &
- & massType =massTypeStellar , &
- & componentType=componentTypeDisk , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & )
- velocityDisk =self_%galacticStructure_%velocityRotation( &
- & node_ , &
- & radius , &
- & massType =massTypeAll , &
- & componentType=componentTypeAll &
- & )
+ radiusImpact_ = radius
+ coordinates =[radius,0.0d0,0.0d0]
+ integratorDensity =integrator (velocityDispersionDensityIntegrand,toleranceRelative=1.0d-2)
+ densitySpheroid =integratorDensity %integrate (radius ,radiusOuter_ )
+ densityDisk =massDistributionStellarDisk_%surfaceDensity(coordinates )
+ velocityDisk =massDistributionTotal_ %rotationCurve (radius )
! Test if the spheroid density is significant....
if (densitySpheroid < fractionSmall*densityDisk) then
! ...it is not, so we can avoid computing the spheroid velocity dispersion.
@@ -585,29 +576,19 @@ $\Sigma_\mathrm{s}(r)$, and a delta function at $V_\mathrm{d}(r)$ with normalize
V(r) = \left. \int_{-\infty}^{+\infty} P(V) V \mathrm{d}V \right/ \int_{-\infty}^{+\infty} P(V) \mathrm{d}V = {\Sigma_\mathrm{d}(r) V_\mathrm{d}(r) \over [\Sigma_\mathrm{d}(r)+\Sigma_\mathrm{s}(r)]}.
\end{equation}
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll, componentTypeDisk, massTypeAll, massTypeStellar
- use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Math, only : Pi
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
implicit none
- double precision, intent(in ) :: radius
- double precision :: densityDisk, velocityDisk
+ double precision , intent(in ) :: radius
+ double precision :: densityDisk, velocityDisk
+ type (coordinateCylindrical) :: coordinates
if (radius <= 0.0d0) then
velocityDispersionLambdaRIntegrand2=0.0d0
else
- densityDisk=self_%galacticStructure_%surfaceDensity ( &
- & node_ , &
- & [radius,0.0d0,0.0d0], &
- & massType =massTypeStellar , &
- & componentType=componentTypeDisk , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & )
- velocityDisk=self_%galacticStructure_%velocityRotation( &
- & node_ , &
- & radius , &
- & massType =massTypeAll , &
- & componentType=componentTypeAll &
- & )
+ coordinates =[radius,0.0d0,0.0d0]
+ densityDisk =massDistributionStellarDisk_%surfaceDensity(coordinates)
+ velocityDisk =massDistributionTotal_ %rotationCurve (radius )
velocityDispersionLambdaRIntegrand2=+2.0d0 &
& *Pi &
& *radius &
@@ -621,31 +602,19 @@ double precision function velocityDispersionVelocitySurfaceDensityIntegrand(radi
!!{
Integrand function used for integrating line-of-sight velocity dispersion over surface density.
!!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
implicit none
- double precision, intent(in ) :: radius
-
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
+
if (radius <= 0.0d0) then
velocityDispersionVelocitySurfaceDensityIntegrand=0.0d0
else
- velocityDispersionVelocitySurfaceDensityIntegrand=+velocityDispersionSolidAngleInCylinder ( &
- & radius &
- & ) &
- & * radius**2 &
- & *self_%galacticStructure_%density ( &
- & node_ , &
- & [radius,0.0d0,0.0d0], &
- & massType =massType_ , &
- & componentType=componentType_ , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & ) &
- & *self_%galacticStructure_%velocityDispersion( &
- & node_ , &
- & radius , &
- & radiusOuter_ , &
- & massType =massType_ , &
- & componentType=componentType_ &
- & )**2
+ coordinates =[radius,0.0d0,0.0d0]
+ velocityDispersionVelocitySurfaceDensityIntegrand=+ velocityDispersionSolidAngleInCylinder(radius ) &
+ & * radius **2 &
+ & *massDistributionWeighted_%density (coordinates ) &
+ & *kinematicsDistribution_ %velocityDispersion1D (coordinates,massDistributionTotal_)**2
end if
return
end function velocityDispersionVelocitySurfaceDensityIntegrand
@@ -654,24 +623,18 @@ double precision function velocityDispersionSurfaceDensityIntegrand(radius)
!!{
Integrand function used for integrating line-of-sight surface density dispersion over area.
!!}
+ use :: Coordinates, only : coordinateSpherical, assignment(=)
implicit none
- double precision, intent(in ) :: radius
+ double precision , intent(in ) :: radius
+ type (coordinateSpherical) :: coordinates
if (radius <= 0.0d0) then
velocityDispersionSurfaceDensityIntegrand=+0.0d0
else
- velocityDispersionSurfaceDensityIntegrand=+velocityDispersionSolidAngleInCylinder( &
- & radius &
- & ) &
- & * radius **2 &
- & *self_%galacticStructure_%density ( &
- & node_ , &
- & [radius,0.0d0,0.0d0], &
- & massType =massType_ , &
- & componentType=componentType_ , &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & )
+ coordinates =[radius,0.0d0,0.0d0]
+ velocityDispersionSurfaceDensityIntegrand=+ velocityDispersionSolidAngleInCylinder(radius ) &
+ & * radius **2 &
+ & *massDistributionWeighted_%density (coordinates)
end if
return
end function velocityDispersionSurfaceDensityIntegrand
@@ -739,15 +702,8 @@ Integrand function used for computing line-of-sight velocity dispersions.
if (radius <= radiusImpact_) then
velocityDispersionDensityIntegrand=+0.0d0
else
- velocityDispersionDensityIntegrand=+self_%galacticStructure_%densitySphericalAverage( &
- & node_ , &
- & radius , &
- & massType =massType_ , &
- & componentType=componentType_, &
- & weightBy =weightBy_ , &
- & weightIndex =weightIndex_ &
- & ) &
- & * radius &
+ velocityDispersionDensityIntegrand=+massDistributionWeighted_%densitySphericalAverage(radius) &
+ & * radius &
& /sqrt(radius**2-radiusImpact_**2)
end if
return
@@ -780,7 +736,6 @@ Integrand function used for computing line-of-sight velocity dispersions. Specif
\int_{r_\mathrm{i}}^{r_\mathrm{o}} {\mathrm{G} M( self %darkMatterProfileDMO_%get (node)
+ velocityMaximumExtract = massDistribution_ %velocityRotationCurveMaximum( )
+ !![
+
+ !!]
+ return
end function velocityMaximumExtract
function velocityMaximumName(self)
diff --git a/source/numerical.ODE_solver.F90 b/source/numerical.ODE_solver.F90
index b2a4e87657..b86ae49b49 100644
--- a/source/numerical.ODE_solver.F90
+++ b/source/numerical.ODE_solver.F90
@@ -446,7 +446,7 @@ subroutine odeSolverSolve(self,x0,x1,y,z,xStep,status)
evolveForward=x1 > x0
! Reset the driver.
status_ =GSL_ODEIV2_Driver_Reset (self%gsl_odeiv2_driver )
- if (status_ /= GSL_Success) call Error_Report('failed to reset ODE driver' //{introspection:location})
+ if (status_ /= GSL_Success) call Error_Report('failed to reset ODE driver' //{introspection:location})
if (xStep_ /= 0.0d0) then
status_=GSL_ODEIV2_Driver_Reset_hStart(self%gsl_odeiv2_driver,xStep_)
if (status_ /= GSL_Success) call Error_Report('failed to reset ODE step size'//{introspection:location})
@@ -456,7 +456,7 @@ subroutine odeSolverSolve(self,x0,x1,y,z,xStep,status)
zCount=size(z)
allocate(z0(zCount))
z0=z
- call self%integrator%integrandSet (zCount,integrandsWrapper)
+ call self%integrator%integrandSet(zCount,integrandsWrapper)
latentIntegrator_=C_FunLoc(latentIntegrator)
else
allocate(z0(0))
diff --git a/source/numerical.constants.math.F90 b/source/numerical.constants.math.F90
index b41bda54e4..0d67783265 100644
--- a/source/numerical.constants.math.F90
+++ b/source/numerical.constants.math.F90
@@ -58,4 +58,8 @@ module Numerical_Constants_Math
!! ζ(3) - https://oeis.org/A002117
double precision, public, parameter :: riemannZeta3=1.20205690315959428539973816151144999076d0
+ ! Catalan's constant
+ !! G - https://oeis.org/A006752
+ double precision, public, parameter :: catalan =0.91596559417721901505460351493238411077d0
+
end module Numerical_Constants_Math
diff --git a/source/objects.coordinates.F90 b/source/objects.coordinates.F90
index 01e77824b9..90cbddc050 100644
--- a/source/objects.coordinates.F90
+++ b/source/objects.coordinates.F90
@@ -53,6 +53,7 @@ module Coordinates
+
!!]
procedure :: toCartesian => Coordinates_Null_To
@@ -64,6 +65,7 @@ module Coordinates
procedure(scalarDivideTemplate ), deferred :: scalarDivide
generic :: operator(*) => scalarMultiply
generic :: operator(/) => scalarDivide
+ procedure(scaleTemplate ), deferred :: scale
end type coordinate
type, public, extends(coordinate) :: coordinateCartesian
@@ -92,6 +94,7 @@ module Coordinates
procedure :: rSphericalSquared => Coordinates_Cartesian_R_Spherical_Squared
procedure :: scalarMultiply => Coordinates_Cartesian_Scalar_Multiply
procedure :: scalarDivide => Coordinates_Cartesian_Scalar_Divide
+ procedure :: scale => Coordinates_Cartesian_Scale
end type coordinateCartesian
type, public, extends(coordinate) :: coordinateSpherical
@@ -121,6 +124,7 @@ module Coordinates
procedure :: rSphericalSquared => Coordinates_Spherical_R_Spherical_Squared
procedure :: scalarMultiply => Coordinates_Spherical_Scalar_Multiply
procedure :: scalarDivide => Coordinates_Spherical_Scalar_Divide
+ procedure :: scale => Coordinates_Spherical_Scale
end type coordinateSpherical
type, public, extends(coordinate) :: coordinateCylindrical
@@ -149,6 +153,7 @@ module Coordinates
procedure :: rSphericalSquared => Coordinates_Cylindrical_R_Spherical_Squared
procedure :: scalarMultiply => Coordinates_Cylindrical_Scalar_Multiply
procedure :: scalarDivide => Coordinates_Cylindrical_Scalar_Divide
+ procedure :: scale => Coordinates_Cylindrical_Scale
end type coordinateCylindrical
abstract interface
@@ -176,6 +181,15 @@ function scalarDivideTemplate(self,divisor)
end function scalarDivideTemplate
end interface
+ abstract interface
+ subroutine scaleTemplate(self,scalar,selfScaled)
+ import coordinate
+ class (coordinate), intent(in ) :: self
+ double precision , intent(in ) :: scalar
+ class (coordinate), intent(inout), allocatable :: selfScaled
+ end subroutine scaleTemplate
+ end interface
+
! Interface to multiplication operators with coordinate objects as their second argument.
interface operator(*)
module procedure Coordinates_Scalar_Multiply_Switched
@@ -359,7 +373,7 @@ function Coordinates_Cartesian_Scalar_Multiply(self,multiplier) result(scaled)
class (coordinateCartesian), intent(in ) :: self
double precision , intent(in ) :: multiplier
- allocate(scaled,mold=self)
+ allocate(scaled,source=self)
scaled%position=+self%position &
& * multiplier
return
@@ -374,11 +388,35 @@ function Coordinates_Cartesian_Scalar_Divide(self,divisor) result(scaled)
class (coordinateCartesian), intent(in ) :: self
double precision , intent(in ) :: divisor
- allocate(scaled,mold=self)
+ allocate(scaled,source=self)
scaled%position=+self%position &
& / divisor
return
end function Coordinates_Cartesian_Scalar_Divide
+
+ !![
+
+
+ This function is needed to allow scaling of coordinate objects. It should not be needed as we overload the * and / operators
+ for coordinate objects. But, until finalization is completed, function results are not finalized causing the overloaded *
+ and / operators to leak memory. This is a workaround to avoid that.
+
+
+ !!]
+ subroutine Coordinates_Cartesian_Scale(self,scalar,selfScaled)
+ !!{
+ Scale a Cartesian {\normalfont \ttfamily coordinate} object by a scalar.
+ !!}
+ implicit none
+ class (coordinateCartesian), intent(in ) :: self
+ double precision , intent(in ) :: scalar
+ class (coordinate ), intent(inout), allocatable :: selfScaled
+
+ allocate(selfScaled,source=self)
+ selfScaled%position=+selfScaled%position &
+ & * scalar
+ return
+ end subroutine Coordinates_Cartesian_Scale
double precision function Coordinates_Cartesian_R_Spherical_Squared(self)
!!{
@@ -537,7 +575,7 @@ function Coordinates_Spherical_Scalar_Multiply(self,multiplier) result(scaled)
class (coordinateSpherical), intent(in ) :: self
double precision , intent(in ) :: multiplier
- allocate(scaled,mold=self)
+ allocate(scaled,source=self)
scaled%position =+self %position
scaled%position(1)=+scaled%position(1) &
& *multiplier
@@ -553,13 +591,36 @@ function Coordinates_Spherical_Scalar_Divide(self,divisor) result(scaled)
class (coordinateSpherical), intent(in ) :: self
double precision , intent(in ) :: divisor
- allocate(scaled,mold=self)
- scaled%position =+self %position
+ allocate(scaled,source=self)
scaled%position(1)=+scaled%position(1) &
& /divisor
return
end function Coordinates_Spherical_Scalar_Divide
+ !![
+
+
+ This function is needed to allow scaling of coordinate objects. It should not be needed as we overload the * and / operators
+ for coordinate objects. But, until finalization is completed, function results are not finalized causing the overloaded *
+ and / operators to leak memory. This is a workaround to avoid that.
+
+
+ !!]
+ subroutine Coordinates_Spherical_Scale(self,scalar,selfScaled)
+ !!{
+ Scale a spherical {\normalfont \ttfamily coordinate} object by a scalar.
+ !!}
+ implicit none
+ class (coordinateSpherical), intent(in ) :: self
+ double precision , intent(in ) :: scalar
+ class (coordinate ), intent(inout), allocatable :: selfScaled
+
+ allocate(selfScaled,source=self)
+ selfScaled%position(1)=+selfScaled%position(1) &
+ & * scalar
+ return
+ end subroutine Coordinates_Spherical_Scale
+
! Cylindrical coordinate object.
subroutine Coordinates_Cylindrical_From_Cartesian(self,x)
!!{
@@ -688,8 +749,7 @@ function Coordinates_Cylindrical_Scalar_Multiply(self,multiplier) result(scaled)
class (coordinateCylindrical), intent(in ) :: self
double precision , intent(in ) :: multiplier
- allocate(scaled,mold=self)
- scaled%position =+self %position
+ allocate(scaled,source=self)
scaled%position(1)=+scaled%position(1) &
& *multiplier
scaled%position(3)=+scaled%position(3) &
@@ -706,8 +766,7 @@ function Coordinates_Cylindrical_Scalar_Divide(self,divisor) result(scaled)
class (coordinateCylindrical), intent(in ) :: self
double precision , intent(in ) :: divisor
- allocate(scaled,mold=self)
- scaled%position =+self %position
+ allocate(scaled,source=self)
scaled%position(1)=+scaled%position(1) &
& /divisor
scaled%position(3)=+scaled%position(3) &
@@ -715,6 +774,32 @@ function Coordinates_Cylindrical_Scalar_Divide(self,divisor) result(scaled)
return
end function Coordinates_Cylindrical_Scalar_Divide
+ !![
+
+
+ This function is needed to allow scaling of coordinate objects. It should not be needed as we overload the * and / operators
+ for coordinate objects. But, until finalization is completed, function results are not finalized causing the overloaded *
+ and / operators to leak memory. This is a workaround to avoid that.
+
+
+ !!]
+ subroutine Coordinates_Cylindrical_Scale(self,scalar,selfScaled)
+ !!{
+ Scale a cylindrical {\normalfont \ttfamily coordinate} object by a scalar.
+ !!}
+ implicit none
+ class (coordinateCylindrical), intent(in ) :: self
+ double precision , intent(in ) :: scalar
+ class (coordinate ), intent(inout), allocatable :: selfScaled
+
+ allocate(selfScaled,source=self)
+ selfScaled%position(1)=+selfScaled%position(1) &
+ & * scalar
+ selfScaled%position(3)=+selfScaled%position(3) &
+ & * scalar
+ return
+ end subroutine Coordinates_Cylindrical_Scale
+
! General functions.
double precision function Coordinates_Radius_Cylindrical(self)
implicit none
@@ -746,11 +831,10 @@ function Coordinates_Scalar_Multiply_Switched(multiplier,self) result(scaled)
Multiply a Cartesian {\normalfont \ttfamily coordinate} object by a scalar.
!!}
implicit none
- class (coordinate), allocatable :: scaled
+ class (coordinate), allocatable :: scaled
class (coordinate), intent(in ) :: self
- double precision , intent(in ) :: multiplier
+ double precision , intent(in ) :: multiplier
- allocate(scaled,mold=self)
scaled=self*multiplier
return
end function Coordinates_Scalar_Multiply_Switched
diff --git a/source/objects.kepler_orbits.F90 b/source/objects.kepler_orbits.F90
index 1bf437fe11..50c13729f3 100644
--- a/source/objects.kepler_orbits.F90
+++ b/source/objects.kepler_orbits.F90
@@ -40,24 +40,24 @@ module Kepler_Orbits
yes
yes
yes
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
!!]
diff --git a/source/objects.nodes.F90 b/source/objects.nodes.F90
index 0ede0ca2a7..5e90c48645 100644
--- a/source/objects.nodes.F90
+++ b/source/objects.nodes.F90
@@ -28,7 +28,7 @@ module Galacticus_Nodes
!!}
use :: Abundances_Structure , only : abundances
use :: Chemical_Abundances_Structure , only : chemicalAbundances
- use :: Galactic_Structure_Options , only : enumerationComponentTypeType , enumerationMassTypeType
+ use :: Galactic_Structure_Options , only : enumerationComponentTypeType , enumerationMassTypeType , enumerationWeightByType
use :: Hashes , only : doubleHash , genericHash
use :: Histories , only : history , longIntegerHistory
use :: IO_HDF5 , only : hdf5Object
@@ -36,15 +36,17 @@ module Galacticus_Nodes
use :: ISO_Varying_String , only : varying_string
use :: Kepler_Orbits , only : keplerOrbit
use :: Kind_Numbers , only : kind_int8
+ use :: Mass_Distributions , only : massDistributionClass
use :: Merger_Trees_Evolve_Deadlock_Status, only : enumerationDeadlockStatusType
- use :: Numerical_Constants_Astronomical , only : gigaYear , luminosityZeroPointAB , massSolar, megaParsec
+ use :: Numerical_Constants_Astronomical , only : gigaYear , luminosityZeroPointAB , massSolar , megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
use :: Stellar_Luminosities_Structure , only : stellarLuminosities
use :: Tensors , only : tensorNullR2D3Sym , tensorRank2Dimension3Symmetric
private
- public :: nodeClassHierarchyInitialize, nodeClassHierarchyFinalize, Galacticus_Nodes_Unique_ID_Set, interruptTask , &
- & nodeEventBuildFromRaw , propertyEvaluate , propertyActive , propertyInactive
+ public :: nodeClassHierarchyInitialize , nodeClassHierarchyFinalize, Galacticus_Nodes_Unique_ID_Set, interruptTask , &
+ & nodeEventBuildFromRaw , propertyEvaluate , propertyActive , propertyInactive, &
+ & massDistributionCalculationReset, massDistributionsLast , massDistributionsDestroy
type, public :: treeNodeList
!!{
@@ -241,6 +243,21 @@ end function universeEventTask
integer , public :: rateComputeState =propertyTypeActive
!$omp threadprivate(rateComputeState)
+ ! Memoized massDistributions
+ type :: massDistributionArray
+ private
+ integer(kind_int8 ) :: uniqueID = -huge(kind_int8)
+ type (enumerationComponentTypeType) :: componentType
+ type (enumerationMassTypeType ) :: massType
+ type (enumerationWeightByType ) :: weightBy
+ integer :: weightIndex
+ class (massDistributionClass ), pointer :: massDistribution_ => null( )
+ end type massDistributionArray
+ integer , parameter :: massDistributionsCount=20
+ integer :: massDistributionsLast = 0
+ type (massDistributionArray), dimension(massDistributionsCount) :: massDistributions__
+ !$omp threadprivate(massDistributions__,massDistributionsLast)
+
! Define a constructor for treeNodes.
interface treeNode
module procedure Tree_Node_Constructor
@@ -1318,75 +1335,35 @@ A null {\normalfont \ttfamily tensorRank2Dimension3Symmetric} function for {\nor
return
end function Node_Component_Null_TensorR2D3_InOut
- double precision function Node_Component_Enclosed_Mass_Null(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- A null implementation of the enclosed mass in a component. Always returns zero.
- !!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- implicit none
- class (nodeComponent ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, radius, componentType, massType, weightBy, weightIndex
-
- Node_Component_Enclosed_Mass_Null=0.0d0
- return
- end function Node_Component_Enclosed_Mass_Null
-
- function Node_Component_Acceleration_Null(self,positionCartesian,componentType,massType)
- !!{
- A null implementation of the acceleration due to a component. Always returns zero.
- !!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- implicit none
- double precision , dimension(3) :: Node_Component_Acceleration_Null
- class (nodeComponent ) , intent(inout) :: self
- type (enumerationComponentTypeType) , intent(in ) :: componentType
- type (enumerationMassTypeType ) , intent(in ) :: massType
- double precision , dimension(3), intent(in ) :: positionCartesian
- !$GLC attributes unused :: self, positionCartesian, componentType, massType
-
- Node_Component_Acceleration_Null=0.0d0
- return
- end function Node_Component_Acceleration_Null
-
- function Node_Component_Chandrasekhar_Integral_Null(self,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
+ function Node_Component_Mass_Distribution_Null(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- A null implementation of the acceleration due to a component. Always returns zero.
+ A null implementation of the mass distribution factory for a component. Always returns null.
!!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
+ use :: Galactic_Structure_Options, only : enumerationWeightByType, enumerationComponentTypeType, enumerationMassTypeType
implicit none
- double precision , dimension(3) :: Node_Component_Chandrasekhar_Integral_Null
- class (nodeComponent ) , intent(inout) :: self
- type (treeNode ) , intent(inout) :: nodeSatellite
- type (enumerationComponentTypeType) , intent(in ) :: componentType
- type (enumerationMassTypeType ) , intent(in ) :: massType
- double precision , dimension(3), intent(in ) :: positionCartesian , velocityCartesian
- !$GLC attributes unused :: self, nodeSatellite, positionCartesian, velocityCartesian, componentType, massType
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponent ), intent(inout) :: self
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !$GLC attributes unused :: self, componentType, massType, weightBy, weightIndex
- Node_Component_Chandrasekhar_Integral_Null=0.0d0
+ massDistribution_ => null()
return
- end function Node_Component_Chandrasekhar_Integral_Null
+ end function Node_Component_Mass_Distribution_Null
- function Node_Component_Tidal_Tensor_Null(self,positionCartesian,componentType,massType)
+ double precision function Node_Component_Mass_Baryonic_Null(self)
!!{
- A null implementation of the tidal tensor due to a component. Always returns zero.
+ A null implementation of the total baryonic mass distribution. Always returns zero.
!!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Tidal_Tensor_Null
- class (nodeComponent ) , intent(inout) :: self
- type (enumerationComponentTypeType ) , intent(in ) :: componentType
- type (enumerationMassTypeType ) , intent(in ) :: massType
- double precision , dimension(3), intent(in ) :: positionCartesian
- !$GLC attributes unused :: self, positionCartesian, componentType, massType
+ class(nodeComponent), intent(inout):: self
+ !$GLC attributes unused :: self
- Node_Component_Tidal_Tensor_Null=tensorNullR2D3Sym
+ Node_Component_Mass_Baryonic_Null=0.0d0
return
- end function Node_Component_Tidal_Tensor_Null
+ end function Node_Component_Mass_Baryonic_Null
double precision function Node_Component_Density_Null(self,positionSpherical,componentType,massType,weightBy,weightIndex)
!!{
@@ -1442,55 +1419,6 @@ double precision function Node_Component_Surface_Density_Null(self,positionCylin
return
end function Node_Component_Surface_Density_Null
- double precision function Node_Component_Potential_Null(self,radius,componentType,massType,status)
- !!{
- A null implementation of the gravitational potential in a component. Always returns zero.
- !!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType, enumerationStructureErrorCodeType
- implicit none
- class (nodeComponent ), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- !$GLC attributes unused :: self, radius, componentType, massType, status
-
- Node_Component_Potential_Null=0.0d0
- return
- end function Node_Component_Potential_Null
-
- double precision function Node_Component_Rotation_Curve_Null(self,radius,componentType,massType)
- !!{
- A null implementation of the rotation curve due to a component. Always returns zero.
- !!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- implicit none
- class (nodeComponent ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, radius, componentType, massType
-
- Node_Component_Rotation_Curve_Null=0.0d0
- return
- end function Node_Component_Rotation_Curve_Null
-
- double precision function Node_Component_Rotation_Curve_Gradient_Null(self,radius,componentType,massType)
- !!{
- A null implementation of the gradient of the rotation curve due to a component. Always returns zero.
- !!}
- use :: Galactic_Structure_Options, only : enumerationComponentTypeType, enumerationMassTypeType
- implicit none
- class (nodeComponent ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, radius, componentType, massType
-
- Node_Component_Rotation_Curve_Gradient_Null=0.0d0
- return
- end function Node_Component_Rotation_Curve_Gradient_Null
-
! Simple Boolean functions.
logical function Boolean_False()
!!{
@@ -1929,5 +1857,40 @@ logical function propertyEvaluate(propertyType,propertyIsInactive)
& (propertyType == propertyTypeInactive .and. propertyIsInactive)
return
end function propertyEvaluate
+
+ subroutine massDistributionCalculationReset(massDistributionsLast,node,uniqueID)
+ !!{
+ Reset the memoized {\normalfont \ttfamily massDistribution} due to a {\normalfont \ttfamily calculationReset} event.
+ !!}
+ implicit none
+ integer , intent(inout) :: massDistributionsLast
+ type (treeNode ), intent(inout) :: node
+ integer(kind_int8), intent(in ) :: uniqueID
+ integer :: i
+ !$GLC attributes unused :: massDistributionsLast, node, uniqueID
+
+ do i=1,massDistributionsCount
+ !![
+
+ !!]
+ massDistributions__(i)%uniqueID=-huge(kind_int8)
+ end do
+ return
+ end subroutine massDistributionCalculationReset
+
+ subroutine massDistributionsDestroy()
+ !!{
+ Destroy memoized {\normalfont \ttfamily massDistributions}.
+ !!}
+ implicit none
+ integer :: i
+
+ do i=1,massDistributionsCount
+ !![
+
+ !!]
+ end do
+ return
+ end subroutine massDistributionsDestroy
end module Galacticus_Nodes
diff --git a/source/objects.nodes.components.F90 b/source/objects.nodes.components.F90
index c72945eaff..2cb6305c14 100644
--- a/source/objects.nodes.components.F90
+++ b/source/objects.nodes.components.F90
@@ -68,7 +68,8 @@ subroutine Node_Components_Thread_Initialize(parameters)
Perform per-thread initialization tasks for node components.
!!}
use :: Input_Parameters, only : inputParameters
- use :: Events_Hooks , only : eventsHooksAtLevelToAllLevels
+ use :: Events_Hooks , only : eventsHooksAtLevelToAllLevels , calculationResetEvent , openMPThreadBindingAllLevels
+ use :: Galacticus_Nodes, only : massDistributionCalculationReset, massDistributionsDestroy, massDistributionsLast
!![
!!]
@@ -91,6 +92,10 @@ subroutine Node_Components_Thread_Initialize(parameters)
!![
!!]
+ ! Attach to an event that will be used to reset massDistributions of treeNodes during evolution.
+ call calculationResetEvent%attach(massDistributionsLast,massDistributionCalculationReset,openMPThreadBindingAllLevels,label='massDistribution')
+ call massDistributionsDestroy()
+ ! Restore event hooking to standard behavior.
call eventsHooksAtLevelToAllLevels(.false.)
end if
initializationThreadCount=initializationThreadCount+1
@@ -118,6 +123,8 @@ subroutine Node_Components_Thread_Uninitialize()
!![
!!]
+ use :: Events_Hooks , only : calculationResetEvent
+ use :: Galacticus_Nodes, only : massDistributionCalculationReset, massDistributionsLast
implicit none
initializationThreadCount=initializationThreadCount-1
@@ -129,6 +136,7 @@ subroutine Node_Components_Thread_Uninitialize()
!![
!!]
+ if (calculationResetEvent%isAttached(massDistributionsLast,massDistributionCalculationReset)) call calculationResetEvent%detach(massDistributionsLast,massDistributionCalculationReset)
end if
return
end subroutine Node_Components_Thread_Uninitialize
diff --git a/source/objects.nodes.components.black_hole.non_central.F90 b/source/objects.nodes.components.black_hole.non_central.F90
index f2a3cc0be3..86a2006d33 100644
--- a/source/objects.nodes.components.black_hole.non_central.F90
+++ b/source/objects.nodes.components.black_hole.non_central.F90
@@ -29,7 +29,6 @@ module Node_Component_Black_Hole_Noncentral
use :: Black_Hole_Binary_Recoil_Velocities, only : blackHoleBinaryRecoilClass
use :: Black_Hole_Binary_Separations , only : blackHoleBinarySeparationGrowthRateClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
implicit none
private
public :: Node_Component_Black_Hole_Noncentral_Rate_Compute , Node_Component_Black_Hole_Noncentral_Scale_Set , &
@@ -63,8 +62,7 @@ module Node_Component_Black_Hole_Noncentral
class(blackHoleBinaryRecoilClass ), pointer :: blackHoleBinaryRecoil_
class(blackHoleBinaryMergerClass ), pointer :: blackHoleBinaryMerger_
class(blackHoleBinarySeparationGrowthRateClass), pointer :: blackHoleBinarySeparationGrowthRate_
- class(galacticStructureClass ), pointer :: galacticStructure_
- !$omp threadprivate(darkMatterHaloScale_,blackHoleBinaryRecoil_,blackHoleBinaryMerger_,blackHoleBinarySeparationGrowthRate_,galacticStructure_)
+ !$omp threadprivate(darkMatterHaloScale_,blackHoleBinaryRecoil_,blackHoleBinaryMerger_,blackHoleBinarySeparationGrowthRate_)
! Option specifying whether the triple black hole interaction should be used.
logical :: tripleInteraction
@@ -129,7 +127,6 @@ subroutine Node_Component_Black_Hole_Noncentral_Thread_Initialize(parameters)
-
!!]
end if
return
@@ -153,7 +150,6 @@ subroutine Node_Component_Black_Hole_Noncentral_Thread_Uninitialize()
-
!!]
end if
return
@@ -494,35 +490,43 @@ logical function Node_Component_Black_Hole_Noncentral_Recoil_Escapes(node,veloci
!!{
Return true if the given recoil velocity is sufficient to eject a black hole from the halo.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions , only : massDistributionClass
use :: Galactic_Structure_Options, only : componentTypeBlackHole
use :: Galacticus_Nodes , only : treeNode
implicit none
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: velocityRecoil , radius
- logical , intent(in ) :: ignoreCentralBlackHole
- double precision :: potentialCentral , potentialCentralSelf, &
- & potentialHalo , potentialHaloSelf
-
+ type (treeNode ), intent(inout) :: node
+ double precision , intent(in ) :: velocityRecoil , radius
+ logical , intent(in ) :: ignoreCentralBlackHole
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: potential , potentialSelf
+ type (coordinateSpherical ) :: coordinates , coordinatesVirial
+
! Compute relevant potentials.
- potentialCentral =galacticStructure_%potential(node,radius )
- potentialHalo =galacticStructure_%potential(node,darkMatterHaloScale_%radiusVirial(node) )
+ coordinates = [ radius ,0.0d0,0.0d0]
+ coordinatesVirial = [darkMatterHaloScale_%radiusVirial(node),0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution ( )
+ potential = massDistribution_%potentialDifference(coordinates,coordinatesVirial)
+ !![
+
+ !!]
if (ignoreCentralBlackHole) then
! Compute potential of central black hole to be subtracted off of total value.
- potentialCentralSelf=galacticStructure_%potential(node,radius ,componentType=componentTypeBlackHole)
- potentialHaloSelf =galacticStructure_%potential(node,darkMatterHaloScale_%radiusVirial(node),componentType=componentTypeBlackHole)
+ massDistribution_ => node %massDistribution (componentType=componentTypeBlackHole )
+ potentialSelf = massDistribution_%potentialDifference( coordinates ,coordinatesVirial)
+ !![
+
+ !!]
else
! No correction for central black hole as it is to be included.
- potentialCentralSelf=0.0d0
- potentialHaloSelf =0.0d0
+ potentialSelf=0.0d0
end if
! Evaluate the escape condition.
Node_Component_Black_Hole_Noncentral_Recoil_Escapes= &
- & +0.5d0*velocityRecoil **2 &
- & + potentialCentral &
- & - potentialCentralSelf &
+ & +0.5d0*velocityRecoil**2 &
+ & + potential &
& > &
- & + potentialHalo &
- & - potentialHaloSelf
+ & + potentialSelf
return
end function Node_Component_Black_Hole_Noncentral_Recoil_Escapes
@@ -544,7 +548,7 @@ subroutine Node_Component_Black_Hole_NonCentral_State_Store(stateFile,gslStateFi
call displayMessage('Storing state for: componentBlackHole -> nonCentral',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Black_Hole_NonCentral_State_Store
@@ -567,7 +571,7 @@ subroutine Node_Component_Black_Hole_NonCentral_State_Restore(stateFile,gslState
call displayMessage('Retrieving state for: componentBlackHole -> nonCentral',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Black_Hole_NonCentral_State_Restore
diff --git a/source/objects.nodes.components.black_hole.simple.F90 b/source/objects.nodes.components.black_hole.simple.F90
index ddc6933937..8f535cb0ba 100644
--- a/source/objects.nodes.components.black_hole.simple.F90
+++ b/source/objects.nodes.components.black_hole.simple.F90
@@ -58,9 +58,8 @@ module Node_Component_Black_Hole_Simple
-
-
-
+
+
objects.nodes.components.black_hole.simple.bound_functions.inc
@@ -226,7 +225,7 @@ subroutine Node_Component_Black_Hole_Simple_Scale_Set(node)
if (.not.defaultBlackHoleComponent%simpleIsActive()) return
! Get the black hole component.
blackHole => node%blackHole()
- ! Ensure that it is of the standard class.
+ ! Ensure that it is of the simple class.
select type (blackHole)
class is (nodeComponentBlackHoleSimple)
! Get the spheroid component.
diff --git a/source/objects.nodes.components.black_hole.simple.bound_functions.Inc b/source/objects.nodes.components.black_hole.simple.bound_functions.Inc
index d048b94f64..6004480194 100644
--- a/source/objects.nodes.components.black_hole.simple.bound_functions.Inc
+++ b/source/objects.nodes.components.black_hole.simple.bound_functions.Inc
@@ -21,103 +21,63 @@
Contains custom functions for the simple black hole component.
!!}
-double precision function Node_Component_Black_Hole_Simple_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
+double precision function Node_Component_Black_Hole_Simple_Mass_Baryonic(self) result(massBaryonic)
!!{
- Computes the mass within a given radius for a central black hole. Black hole is treated as a point mass.
+ Return the baryonic mass for the simple black hole component.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeBlackHole, massTypeAll , massTypeBlackHole , &
- & weightByMass , massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
implicit none
- class (nodeComponentBlackHoleSimple), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: weightIndex
-
- ! Set zero enclosed mass by default.
- Node_Component_Black_Hole_Simple_Enclosed_Mass=0.0d0
- ! Return the black hole mass only if massType and componentType are of black hole type.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- if (.not.(weightBy == weightByMass )) return
- if ( radius < 0.0d0 ) return
- ! Return the mass of the black hole.
- Node_Component_Black_Hole_Simple_Enclosed_Mass=self%mass()
- return
-end function Node_Component_Black_Hole_Simple_Enclosed_Mass
+ class(nodeComponentBlackHoleSimple), intent(inout) :: self
- function Node_Component_Black_Hole_Simple_Acceleration(self,positionCartesian,componentType,massType)
- !!{
- Computes the acceleration due to a central black hole. Black hole is treated as a point mass.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole, &
- & massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus, gigaYear , megaParsec
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: Node_Component_Black_Hole_Simple_Acceleration
- class (nodeComponentBlackHoleSimple), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
-
- ! Set zero enclosed acceleration by default.
- Node_Component_Black_Hole_Simple_Acceleration=0.0d0
- ! Return the black hole mass only if massType and componentType are of black hole type.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- radius=sqrt(sum(positionCartesian**2))
- if ( radius < 0.0d0 ) return
- ! Compute the acceleration.
- Node_Component_Black_Hole_Simple_Acceleration=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *self%mass() &
- & *positionCartesian &
- & /radius**3
+ massBaryonic=max(0.0d0,self%mass())
return
-end function Node_Component_Black_Hole_Simple_Acceleration
+end function Node_Component_Black_Hole_Simple_Mass_Baryonic
-function Node_Component_Black_Hole_Simple_Tidal_Tensor(self,positionCartesian,componentType,massType)
+function Node_Component_Black_Hole_Simple_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- Computes the acceleration due to a central black hole. Black hole is treated as a point mass.
+ Return the mass distribution for the simple black hole component.
!!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole, &
- & massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorNullR2D3Sym , tensorIdentityR2D3Sym , assignment(=) , &
- & operator(*)
- use :: Vectors , only : Vector_Outer_Product
+ use :: Galactic_Structure_Options, only : componentTypeBlackHole , massTypeBlackHole , weightByMass , weightByLuminosity , &
+ & enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType
+ use :: Mass_Distributions , only : massDistributionBlackHole, massDistributionClass , massDistributionMatches_, kinematicsDistributionLocal
implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Black_Hole_Simple_Tidal_Tensor
- class (nodeComponentBlackHoleSimple ), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
- type (tensorRank2Dimension3Symmetric) :: positionTensor
-
- ! Set zero enclosed acceleration by default.
- Node_Component_Black_Hole_Simple_Tidal_Tensor=tensorNullR2D3Sym
- ! Return the black hole mass only if massType and componentType are of black hole type.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- radius=sqrt(sum(positionCartesian**2))
- if ( radius < 0.0d0 ) return
- ! Compute the tidal tensor.
- if (self%radialPosition() <= 0.0d0) then
- positionTensor = Vector_Outer_Product(positionCartesian,symmetrize=.true.)
- Node_Component_Black_Hole_Simple_Tidal_Tensor=+gravitationalConstantGalacticus &
- & *( &
- & -(self%mass() /radius**3)*tensorIdentityR2D3Sym &
- & +(self%mass()*3.0d0/radius**5)*positionTensor &
- & )
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionLocal ), pointer :: kinematicsDistribution_
+ class (nodeComponentBlackHoleSimple), intent(inout) :: self
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !$GLC attributes unused :: weightIndex
+ !![
+
+ !!]
+
+ if ( &
+ & massDistributionMatches_(componentTypeBlackHole,massTypeBlackHole,componentType,massType) &
+ & .and. &
+ & weightBy_ == weightByMass &
+ & .and. &
+ & self%mass() > 0.0d0 &
+ & ) then
+ ! Create a black hole mass distribution.
+ allocate(massDistributionBlackHole :: massDistribution_)
+ select type (massDistribution_)
+ type is (massDistributionBlackHole)
+ !![
+
+ !!]
+ end select
+ ! Construct the kinematic distribution.
+ allocate(kinematicsDistribution_)
+ !![
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ else
+ massDistribution_ => null()
end if
return
-end function Node_Component_Black_Hole_Simple_Tidal_Tensor
+end function Node_Component_Black_Hole_Simple_Mass_Distribution
diff --git a/source/objects.nodes.components.black_hole.simple.structure.F90 b/source/objects.nodes.components.black_hole.simple.structure.F90
deleted file mode 100644
index 849e93dfbc..0000000000
--- a/source/objects.nodes.components.black_hole.simple.structure.F90
+++ /dev/null
@@ -1,168 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 the structure tasks for the simple black hole node component.
-!!}
-
-module Node_Component_Black_Hole_Simple_Structure
- !!{
- Implements the structure tasks for the simple black hole node component.
- !!}
- implicit none
- private
- public :: Node_Component_Black_Hole_Simple_Rotation_Curve, Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient, &
- & Node_Component_Black_Hole_Simple_Potential
-
-contains
-
- !![
-
- Node_Component_Black_Hole_Simple_Rotation_Curve
-
- !!]
- double precision function Node_Component_Black_Hole_Simple_Rotation_Curve(node,radius,componentType,massType)
- !!{
- Computes the rotation curve for the central black hole. Assumes a point mass black hole with a Keplerian rotation curve,
- \emph{except} that the rotation speed is limited to never exceed the speed of light.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleSimple, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Physical , only : speedLight
- use :: Numerical_Constants_Prefixes , only : milli
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Black_Hole_Simple_Rotation_Curve=0.0d0
- ! Get the black hole component and check that it is of the simple class.
- blackHole => node%blackHole()
- select type (blackHole)
- class is (nodeComponentBlackHoleSimple)
- ! Check if the radius exceeds the gravitational radius.
- if (radius > Black_Hole_Gravitational_Radius(blackHole)/(milli*speedLight)**2) then
- ! Radius is larger than the gravitational radius - compute the rotation speed.
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass&
- &,weightIndexNull)
- if (componentMass > 0.0d0) Node_Component_Black_Hole_Simple_Rotation_Curve=sqrt(gravitationalConstantGalacticus&
- &*componentMass/radius)
- else
- ! Radius is less than the gravitational radius - return the speed of light.
- Node_Component_Black_Hole_Simple_Rotation_Curve=speedLight*milli
- end if
- end select
- return
- end function Node_Component_Black_Hole_Simple_Rotation_Curve
-
- !![
-
- Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient
-
- !!]
- double precision function Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient(node,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient for the central black hole. Assumes a point mass black hole with a Keplerian
- rotation curve, \emph{except} that the rotation speed is limited to never exceed the speed of light.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole , &
- & weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleSimple, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBlackHole )) return
- if ( radius <= 0.0d0 ) return
- ! Get the black hole component and check that it is of the simple class.
- blackHole => node%blackHole()
- select type (blackHole)
- class is (nodeComponentBlackHoleSimple)
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass ==0.0d0 ) return
- if (radius > Black_Hole_Gravitational_Radius(blackHole)) then
- Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient= &
- & -gravitationalConstantGalacticus &
- & *componentMass &
- & /radius**2
- else
- Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient=0.0d0
- end if
- end select
- return
- end function Node_Component_Black_Hole_Simple_Rotation_Curve_Gradient
-
- !![
-
- Node_Component_Black_Hole_Simple_Potential
-
- !!]
- double precision function Node_Component_Black_Hole_Simple_Potential(node,radius,componentType,massType,status)
- !!{
- Compute the gravitational potential due to a black hole.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole , &
- & weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationStructureErrorCodeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleSimple, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
- !$GLC attributes unused :: status
-
- ! Set to zero by default.
- Node_Component_Black_Hole_Simple_Potential=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBlackHole )) return
- ! Get the black hole component and check that it is of the simple class.
- blackHole => node%blackHole()
- select type (blackHole)
- class is (nodeComponentBlackHoleSimple)
- if (Black_Hole_Gravitational_Radius(blackHole) <= 0.0d0) return
- ! Compute the potential - limit the radius to the gravitational radius to avoid divergent potentials.
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass,weightIndexNull)
- Node_Component_Black_Hole_Simple_Potential=-gravitationalConstantGalacticus*componentMass/max(radius &
- &,Black_Hole_Gravitational_Radius(blackHole))
- end select
- return
- end function Node_Component_Black_Hole_Simple_Potential
-
-end module Node_Component_Black_Hole_Simple_Structure
diff --git a/source/objects.nodes.components.black_hole.standard.F90 b/source/objects.nodes.components.black_hole.standard.F90
index 7cc9192a96..98875e824a 100644
--- a/source/objects.nodes.components.black_hole.standard.F90
+++ b/source/objects.nodes.components.black_hole.standard.F90
@@ -33,7 +33,6 @@ module Node_Component_Black_Hole_Standard
use :: Black_Hole_Accretion_Rates , only : blackHoleAccretionRateClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
implicit none
private
public :: Node_Component_Black_Hole_Standard_Rate_Compute , Node_Component_Black_Hole_Standard_Scale_Set , &
@@ -104,9 +103,8 @@ module Node_Component_Black_Hole_Standard
-
-
-
+
+
objects.nodes.components.black_hole.standard.bound_functions.inc
@@ -120,8 +118,7 @@ module Node_Component_Black_Hole_Standard
class(blackHoleBinaryMergerClass ), pointer :: blackHoleBinaryMerger_
class(blackHoleBinarySeparationGrowthRateClass), pointer :: blackHoleBinarySeparationGrowthRate_
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(galacticStructureClass ), pointer :: galacticStructure_
- !$omp threadprivate(accretionDisks_,blackHoleAccretionRate_,blackHoleBinaryRecoil_,blackHoleBinaryInitialSeparation_,blackHoleBinaryMerger_,blackHoleBinarySeparationGrowthRate_,darkMatterHaloScale_,galacticStructure_)
+ !$omp threadprivate(accretionDisks_,blackHoleAccretionRate_,blackHoleBinaryRecoil_,blackHoleBinaryInitialSeparation_,blackHoleBinaryMerger_,blackHoleBinarySeparationGrowthRate_,darkMatterHaloScale_)
! Accretion model parameters.
! Enhancement factors for the accretion rate.
@@ -270,9 +267,9 @@ subroutine Node_Component_Black_Hole_Standard_Thread_Initialize(parameters)
!!{
Initializes the tree node standard black hole module.
!!}
- use :: Events_Hooks , only : satelliteMergerEvent , openMPThreadBindingAtLevel, dependencyRegEx, dependencyDirectionBefore
- use :: Galacticus_Nodes, only : defaultBlackHoleComponent
- use :: Input_Parameters, only : inputParameter , inputParameters
+ use :: Events_Hooks , only : satelliteMergerEvent , openMPThreadBindingAtLevel, dependencyRegEx, dependencyDirectionBefore
+ use :: Galacticus_Nodes , only : defaultBlackHoleComponent
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type(inputParameters), intent(inout) :: parameters
type(dependencyRegEx), dimension(1) :: dependencies
@@ -291,8 +288,7 @@ subroutine Node_Component_Black_Hole_Standard_Thread_Initialize(parameters)
-
- !!]
+ !!]
end if
return
end subroutine Node_Component_Black_Hole_Standard_Thread_Initialize
@@ -320,7 +316,6 @@ subroutine Node_Component_Black_Hole_Standard_Thread_Uninitialize()
-
!!]
end if
return
@@ -599,14 +594,17 @@ logical function Node_Component_Black_Hole_Standard_Recoil_Escapes(node,velocity
!!{
Return true if the given recoil velocity is sufficient to eject a black hole from the halo.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions , only : massDistributionClass
use :: Galactic_Structure_Options, only : componentTypeBlackHole
use :: Galacticus_Nodes , only : treeNode
implicit none
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: velocityRecoil , radius
- logical , intent(in ) :: ignoreCentralBlackHole
- double precision :: potentialCentral , potentialCentralSelf, &
- & potentialHalo , potentialHaloSelf
+ type (treeNode ), intent(inout) :: node
+ double precision , intent(in ) :: velocityRecoil , radius
+ logical , intent(in ) :: ignoreCentralBlackHole
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: potential , potentialSelf
+ type (coordinateSpherical ) :: coordinates , coordinatesVirial
! Return false immediately if the recoil velocity is zero.
if (velocityRecoil <= 0.0d0) then
@@ -614,25 +612,30 @@ logical function Node_Component_Black_Hole_Standard_Recoil_Escapes(node,velocity
return
end if
! Compute relevant potentials.
- potentialCentral =galacticStructure_%potential(node,radius )
- potentialHalo =galacticStructure_%potential(node,darkMatterHaloScale_%radiusVirial(node) )
+ coordinates = [ radius ,0.0d0,0.0d0]
+ coordinatesVirial = [darkMatterHaloScale_%radiusVirial(node),0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution ( )
+ potential = massDistribution_%potentialDifference(coordinates,coordinatesVirial)
+ !![
+
+ !!]
if (ignoreCentralBlackHole) then
! Compute potential of central black hole to be subtracted off of total value.
- potentialCentralSelf=galacticStructure_%potential(node,radius ,componentType=componentTypeBlackHole)
- potentialHaloSelf =galacticStructure_%potential(node,darkMatterHaloScale_%radiusVirial(node),componentType=componentTypeBlackHole)
+ massDistribution_ => node %massDistribution (componentType=componentTypeBlackHole )
+ potentialSelf = massDistribution_%potentialDifference( coordinates ,coordinatesVirial)
+ !![
+
+ !!]
else
! No correction for central black hole as it is to be included.
- potentialCentralSelf=0.0d0
- potentialHaloSelf =0.0d0
+ potentialSelf=0.0d0
end if
! Evaluate the escape condition.
Node_Component_Black_Hole_Standard_Recoil_Escapes= &
- & +0.5d0*velocityRecoil **2 &
- & + potentialCentral &
- & - potentialCentralSelf &
+ & +0.5d0*velocityRecoil**2 &
+ & + potential &
& > &
- & + potentialHalo &
- & - potentialHaloSelf
+ & + potentialSelf
return
end function Node_Component_Black_Hole_Standard_Recoil_Escapes
@@ -798,7 +801,7 @@ subroutine Node_Component_Black_Hole_Standard_State_Store(stateFile,gslStateFile
call displayMessage('Storing state for: componentBlackHole -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Black_Hole_Standard_State_Store
@@ -821,7 +824,7 @@ subroutine Node_Component_Black_Hole_Standard_State_Restore(stateFile,gslStateFi
call displayMessage('Retrieving state for: componentBlackHole -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Black_Hole_Standard_State_Restore
diff --git a/source/objects.nodes.components.black_hole.standard.bound_functions.Inc b/source/objects.nodes.components.black_hole.standard.bound_functions.Inc
index e345f0ba11..8a7c164ac9 100644
--- a/source/objects.nodes.components.black_hole.standard.bound_functions.Inc
+++ b/source/objects.nodes.components.black_hole.standard.bound_functions.Inc
@@ -46,106 +46,63 @@ double precision function Node_Component_Black_Hole_Standard_Spin(self)
return
end function Node_Component_Black_Hole_Standard_Spin
-double precision function Node_Component_Black_Hole_Standard_Enclosed_Mass(self,radius,componentType,massType,weightBy&
- &,weightIndex)
+double precision function Node_Component_Black_Hole_Standard_Mass_Baryonic(self) result(massBaryonic)
!!{
- Computes the mass within a given radius for a central black hole. Black hole is treated as a point mass.
+ Return the baryonic mass for the standard black hole component.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeBlackHole, massTypeAll , massTypeBlackHole , &
- & weightByMass , massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
implicit none
- class (nodeComponentBlackHoleStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: weightIndex
+ class(nodeComponentBlackHoleStandard), intent(inout) :: self
- ! Set zero enclosed mass by default.
- Node_Component_Black_Hole_Standard_Enclosed_Mass=0.0d0
- ! Return if mass is not to be counted, or if radius is negative.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- if (.not.(weightBy == weightByMass )) return
- if ( radius < 0.0d0 ) return
- ! Set the mass if the black hole is at the galactic center.
- if (self%radialPosition() <= 0.0d0) Node_Component_Black_Hole_Standard_Enclosed_Mass=self%mass()
+ massBaryonic=max(0.0d0,self%mass())
return
-end function Node_Component_Black_Hole_Standard_Enclosed_Mass
+end function Node_Component_Black_Hole_Standard_Mass_Baryonic
- function Node_Component_Black_Hole_Standard_Acceleration(self,positionCartesian,componentType,massType)
+function Node_Component_Black_Hole_Standard_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- Computes the acceleration due to a central black hole. Black hole is treated as a point mass.
+ Return the mass distribution for the standard black hole component.
!!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole, &
- & massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus, gigaYear , megaParsec
- use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Galactic_Structure_Options, only : componentTypeBlackHole , massTypeBlackHole , weightByMass , weightByLuminosity , &
+ & enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType
+ use :: Mass_Distributions , only : massDistributionBlackHole, massDistributionClass , massDistributionMatches_, kinematicsDistributionLocal
implicit none
- double precision , dimension(3) :: Node_Component_Black_Hole_Standard_Acceleration
- class (nodeComponentBlackHoleStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
-
- ! Set zero enclosed acceleration by default.
- Node_Component_Black_Hole_Standard_Acceleration=0.0d0
- ! Return the black hole mass only if massType and componentType are of black hole type.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- radius=sqrt(sum(positionCartesian**2))
- if ( radius < 0.0d0 ) return
- ! Compute the acceleration.
- if (self%radialPosition() <= 0.0d0) &
- & Node_Component_Black_Hole_Standard_Acceleration=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *self%mass() &
- & *positionCartesian &
- & /radius**3
- return
-end function Node_Component_Black_Hole_Standard_Acceleration
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (kinematicsDistributionLocal ), pointer :: kinematicsDistribution_
+ class (nodeComponentBlackHoleStandard), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !![
+
+ !!]
+ !$GLC attributes unused :: weightIndex, componentType, massType
-function Node_Component_Black_Hole_Standard_Tidal_Tensor(self,positionCartesian,componentType,massType)
- !!{
- Computes the acceleration due to a central black hole. Black hole is treated as a point mass.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole, &
- & massTypeGalactic , enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorNullR2D3Sym , tensorIdentityR2D3Sym , assignment(=) , &
- & operator(*)
- use :: Vectors , only : Vector_Outer_Product
- implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Black_Hole_Standard_Tidal_Tensor
- class (nodeComponentBlackHoleStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
- type (tensorRank2Dimension3Symmetric), save :: positionTensor
- !$omp threadprivate(positionTensor)
-
- ! Set zero enclosed acceleration by default.
- Node_Component_Black_Hole_Standard_Tidal_Tensor=tensorNullR2D3Sym
- ! Return the black hole mass only if massType and componentType are of black hole type.
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeGalactic .or. massType == massTypeBlackHole )) return
- radius=sqrt(sum(positionCartesian**2))
- if ( radius < 0.0d0 ) return
- ! Compute the tidal tensor.
- if (self%radialPosition() <= 0.0d0) then
- positionTensor = Vector_Outer_Product(positionCartesian,symmetrize=.true.)
- Node_Component_Black_Hole_Standard_Tidal_Tensor=+gravitationalConstantGalacticus &
- & *( &
- & -(self%mass() /radius**3)*tensorIdentityR2D3Sym &
- & +(self%mass()*3.0d0/radius**5)*positionTensor &
- & )
+ if ( &
+ & massDistributionMatches_(componentTypeBlackHole,massTypeBlackHole,componentType,massType) &
+ & .and. &
+ & weightBy_ == weightByMass &
+ & .and. &
+ & self%mass() > 0.0d0 &
+ & ) then
+ ! Create a black hole mass distribution.
+ allocate(massDistributionBlackHole :: massDistribution_)
+ select type (massDistribution_)
+ type is (massDistributionBlackHole)
+ !![
+
+ !!]
+ end select
+ ! Construct the kinematic distribution.
+ allocate(kinematicsDistribution_)
+ !![
+
+ !!]
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ else
+ massDistribution_ => null()
end if
return
-end function Node_Component_Black_Hole_Standard_Tidal_Tensor
+end function Node_Component_Black_Hole_Standard_Mass_Distribution
diff --git a/source/objects.nodes.components.black_hole.standard.structure_tasks.F90 b/source/objects.nodes.components.black_hole.standard.structure_tasks.F90
deleted file mode 100644
index 39a029be5e..0000000000
--- a/source/objects.nodes.components.black_hole.standard.structure_tasks.F90
+++ /dev/null
@@ -1,170 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
-!+ Contributions to this file made by: Stéphane Mangeon, Andrew Benson.
-
-!!{
-Contains a module which implements galactic structure tasks for the standard black hole node component.
-!!}
-
-module Node_Component_Black_Hole_Standard_Structure_Tasks
- !!{
- Implements galactic structure tasks for the standard black hole tree node component.
- !!}
- implicit none
- private
- public :: Node_Component_Black_Hole_Standard_Rotation_Curve , Node_Component_Black_Hole_Standard_Potential, &
- & Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient
-
-contains
-
- !![
-
- Node_Component_Black_Hole_Standard_Rotation_Curve
-
- !!]
- double precision function Node_Component_Black_Hole_Standard_Rotation_Curve(node,radius,componentType,massType)
- !!{
- Computes the rotation curve for the central black hole. Assumes a point mass black hole with a Keplerian rotation curve,
- \emph{except} that the rotation speed is limited to never exceed the speed of light.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleStandard, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Physical , only : speedLight
- use :: Numerical_Constants_Prefixes , only : milli
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Black_Hole_Standard_Rotation_Curve=0.0d0
- ! Get the black hole component and check that it is of the standard class.
- blackHole => node%blackHole(instance=1)
- select type (blackHole)
- class is (nodeComponentBlackHoleStandard)
- ! Check if the radius exceeds the gravitational radius.
- if (radius > Black_Hole_Gravitational_Radius(blackHole)) then
- ! Radius is larger than the gravitational radius - compute the rotation speed.
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass &
- &,weightIndexNull)
- if (componentMass > 0.0d0) Node_Component_Black_Hole_Standard_Rotation_Curve=sqrt(gravitationalConstantGalacticus&
- &*componentMass/radius)
- else
- ! Radius is less than the gravitational radius - return the speed of light.
- Node_Component_Black_Hole_Standard_Rotation_Curve=speedLight*milli
- end if
- end select
- return
- end function Node_Component_Black_Hole_Standard_Rotation_Curve
-
- !![
-
- Node_Component_Black_Hole_Standard_Potential
-
- !!]
- double precision function Node_Component_Black_Hole_Standard_Potential(node,radius,componentType,massType,status)
- !!{
- Compute the gravitational potential due to a black hole.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole , &
- & weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationStructureErrorCodeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleStandard, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
- !$GLC attributes unused :: status
-
- Node_Component_Black_Hole_Standard_Potential=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBlackHole )) return
- ! Get the black hole component and check that it is of the standard class.
- blackHole => node%blackHole(instance=1)
- select type (blackHole)
- class is (nodeComponentBlackHoleStandard)
- if (Black_Hole_Gravitational_Radius(blackHole) <=0.0d0) return
- ! Computes the potential - limit the radius to the gravitational radius to avoid divergent potentials.
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass&
- &,weightIndexNull)
- Node_Component_Black_Hole_Standard_Potential=-gravitationalConstantGalacticus*componentMass/max(radius &
- &,Black_Hole_Gravitational_Radius(blackHole))
- end select
- return
- end function Node_Component_Black_Hole_Standard_Potential
-
- !![
-
- Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient
-
- !!]
- double precision function Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient(node,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient for the central black hole. Assumes a point mass black hole with a Keplerian
- rotation curve, \emph{except} that the rotation speed is limited to never exceed the speed of light.
- !!}
- use :: Black_Hole_Fundamentals , only : Black_Hole_Gravitational_Radius
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeBlackHole , massTypeAll , massTypeBlackHole , &
- & weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : nodeComponentBlackHole , nodeComponentBlackHoleStandard, treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- class (nodeComponentBlackHole ) , pointer :: blackHole
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeBlackHole)) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBlackHole )) return
- if ( radius <= 0.0d0 ) return
- ! Get the black hole component and check that it is of the standard class.
- blackHole => node%blackHole(instance=1)
- select type (blackHole)
- class is (nodeComponentBlackHoleStandard)
- componentMass=blackHole%enclosedMass(radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass == 0.0d0) return
- if (radius > Black_Hole_Gravitational_Radius(blackHole)) then
- Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient= &
- & -gravitationalConstantGalacticus &
- & *componentMass &
- & /radius**2
- else
- Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient=0.0d0
- end if
- end select
- return
- end function Node_Component_Black_Hole_Standard_Rotation_Curve_Gradient
-
-end module Node_Component_Black_Hole_Standard_Structure_Tasks
diff --git a/source/objects.nodes.components.dark_matter_halo.spin.scalar.F90 b/source/objects.nodes.components.dark_matter_halo.spin.scalar.F90
index 01215b638a..12f72d263c 100644
--- a/source/objects.nodes.components.dark_matter_halo.spin.scalar.F90
+++ b/source/objects.nodes.components.dark_matter_halo.spin.scalar.F90
@@ -128,7 +128,7 @@ subroutine Node_Component_Halo_Angular_Momentum_Scalar_Scale_Set(node)
select type (spin)
class is (nodeComponentSpinScalar)
! Set scale for spin.
- call spin%angularMomentumScale(max(spin%angularMomentum(),spinMinimum*Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterHaloScale_=darkMatterHaloScale_,useBullockDefinition=.true.)))
+ call spin%angularMomentumScale(max(spin%angularMomentum(),spinMinimum*Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterHaloScale_,useBullockDefinition=.true.)))
end select
return
end subroutine Node_Component_Halo_Angular_Momentum_Scalar_Scale_Set
diff --git a/source/objects.nodes.components.dark_matter_halo.spin.vector.F90 b/source/objects.nodes.components.dark_matter_halo.spin.vector.F90
index 21aba41e7c..eb246ae1ca 100644
--- a/source/objects.nodes.components.dark_matter_halo.spin.vector.F90
+++ b/source/objects.nodes.components.dark_matter_halo.spin.vector.F90
@@ -160,7 +160,7 @@ subroutine Node_Component_Halo_Angular_Momentum_Vector_Scale_Set(node)
select type (spin)
class is (nodeComponentSpinVector)
! Set scale for spin.
- call spin%angularMomentumVectorScale([1.0d0,1.0d0,1.0d0]*max(spin%angularMomentum(),spinMinimum*Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterHaloScale_=darkMatterHaloScale_,useBullockDefinition=.true.)))
+ call spin%angularMomentumVectorScale([1.0d0,1.0d0,1.0d0]*max(spin%angularMomentum(),spinMinimum*Dark_Matter_Halo_Angular_Momentum_Scale(node,darkMatterHaloScale_,useBullockDefinition=.true.)))
end select
return
end subroutine Node_Component_Halo_Angular_Momentum_Vector_Scale_Set
diff --git a/source/objects.nodes.components.dark_matter_profile.scale.F90 b/source/objects.nodes.components.dark_matter_profile.scale.F90
index c250d1184d..4074337e5f 100644
--- a/source/objects.nodes.components.dark_matter_profile.scale.F90
+++ b/source/objects.nodes.components.dark_matter_profile.scale.F90
@@ -25,12 +25,16 @@ module Node_Component_Dark_Matter_Profile_Scale
!!{
Implements a dark matter profile method that provides a scale radius.
!!}
- use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Dark_Matter_Profiles , only : darkMatterProfileClass
+ use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
private
public :: Node_Component_Dark_Matter_Profile_Scale_Scale_Set , Node_Component_Dark_Matter_Profile_Scale_Plausibility , &
& Node_Component_Dark_Matter_Profile_Scale_Thread_Initialize, Node_Component_Dark_Matter_Profile_Scale_Thread_Uninitialize, &
- & Node_Component_Dark_Matter_Profile_Scale_State_Store , Node_Component_Dark_Matter_Profile_Scale_State_Restore
+ & Node_Component_Dark_Matter_Profile_Scale_State_Store , Node_Component_Dark_Matter_Profile_Scale_State_Restore , &
+ & Node_Component_Dark_Matter_Profile_Scale_Initialize
!![
@@ -47,15 +51,63 @@ module Node_Component_Dark_Matter_Profile_Scale
-1.0d0
+
+
+
+ class(massDistributionClass), pointer
+ 0
+ Galactic_Structure_Options, only : enumerationWeightByType, enumerationComponentTypeType, enumerationMassTypeType
+ Mass_Distributions , only : massDistributionClass
+
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+
+
!!]
! Objects used by this component.
- class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_
- !$omp threadprivate(darkMatterHaloScale_)
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class(darkMatterProfileClass ), pointer :: darkMatterProfile_
+ class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_
+ !$omp threadprivate(darkMatterHaloScale_,darkMatterProfile_,darkMatterProfileDMO_)
+
+ ! Procedure pointers to mass distribution functions.
+ procedure(Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution), pointer :: Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution_
+
+ ! Mass distribution pointer used for post-construction initialization.
+ class(massDistributionClass), pointer :: massDistribution__
+ !$omp threadprivate(massDistribution__)
contains
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Initialize
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Initialize(parameters)
+ !!{
+ Initializes the scale dark matter profile component.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ use :: Galacticus_Nodes, only : defaultDarkMatterProfileComponent, nodeComponentDarkMatterProfileScale
+ type(inputParameters ), intent(inout) :: parameters
+ type(nodeComponentDarkMatterProfileScale) :: darkMatterProfile
+ !$GLC attributes unused :: parameters
+
+ !$omp critical (Node_Component_Dark_Matter_Profile_Initialize)
+ if (defaultDarkMatterProfileComponent%scaleIsActive()) then
+ Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution_ => Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution
+ call darkMatterProfile%massDistributionFunction (Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution_)
+ end if
+ !$omp end critical (Node_Component_Dark_Matter_Profile_Initialize)
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Initialize
+
!![
Node_Component_Dark_Matter_Profile_Scale_Thread_Initialize
@@ -73,7 +125,9 @@ subroutine Node_Component_Dark_Matter_Profile_Scale_Thread_Initialize(parameters
if (defaultDarkMatterProfileComponent%scaleIsActive()) then
!![
-
+
+
+
!!]
end if
return
@@ -93,7 +147,9 @@ subroutine Node_Component_Dark_Matter_Profile_Scale_Thread_Uninitialize()
if (defaultDarkMatterProfileComponent%scaleIsActive()) then
!![
-
+
+
+
!!]
end if
return
@@ -177,7 +233,7 @@ subroutine Node_Component_Dark_Matter_Profile_Scale_State_Store(stateFile,gslSta
call displayMessage('Storing state for: componentDarkMatterProfile -> scale',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Dark_Matter_Profile_Scale_State_Store
@@ -198,11 +254,55 @@ subroutine Node_Component_Dark_Matter_Profile_Scale_State_Restore(stateFile,gslS
integer(c_size_t), intent(in ) :: stateOperationID
type (c_ptr ), intent(in ) :: gslStateFile
- call displayMessage('Retrieving state for: componentDarkMatterProfile -> scale',verbosity=verbosityLevelInfo)
+ call displayMessage('Retrieving state for: componentDar -> scale',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Dark_Matter_Profile_Scale_State_Restore
+ function Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the mass distribution associated with the dark matter profile.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfileScale
+ use :: Galactic_Structure_Options, only : enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType, componentTypeAll, &
+ & componentTypeDarkHalo , componentTypeDarkMatterOnly , massTypeAll , massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponentDarkMatterProfileScale), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !![
+
+
+ !!]
+
+ massDistribution_ => null()
+ massDistribution__ => null()
+ if ( &
+ & massType_ == massTypeAll &
+ & .or. &
+ & massType_ == massTypeDark &
+ & ) then
+ if ( &
+ & componentType_ == componentTypeAll &
+ & .or. &
+ & componentType_ == componentTypeDarkHalo &
+ & ) then
+ massDistribution_ => darkMatterProfile_ %get(self%hostNode,weightBy,weightIndex)
+ massDistribution__ => massDistribution_
+ else if ( &
+ & componentType_ == componentTypeDarkMatterOnly &
+ & ) then
+ massDistribution_ => darkMatterProfileDMO_%get(self%hostNode,weightBy,weightIndex)
+ call massDistribution_%setTypes(componentType=componentTypeDarkMatterOnly)
+ end if
+ end if
+ return
+ end function Node_Component_Dark_Matter_Profile_Scale_Mass_Distribution
+
end module Node_Component_Dark_Matter_Profile_Scale
diff --git a/source/objects.nodes.components.dark_matter_profile.scale_free.F90 b/source/objects.nodes.components.dark_matter_profile.scale_free.F90
new file mode 100644
index 0000000000..732fd52a9b
--- /dev/null
+++ b/source/objects.nodes.components.dark_matter_profile.scale_free.F90
@@ -0,0 +1,239 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 dark matter profile method that provides no properties (but does provide a mass distribution factory).
+!!}
+
+module Node_Component_Dark_Matter_Profile_Scale_Free
+ !!{
+ Implements a dark matter profile method that provides no properties (but does provide a mass distribution factory).
+ !!}
+ use :: Dark_Matter_Profiles , only : darkMatterProfileClass
+ use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
+ private
+ public :: Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Init, Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Uninit, &
+ & Node_Component_Dark_Matter_Profile_Scale_Free_State_Store, Node_Component_Dark_Matter_Profile_Scale_Free_State_Restore, &
+ & Node_Component_Dark_Matter_Profile_Scale_Free_Init
+
+ !![
+
+ darkMatterProfile
+ scaleFree
+ false
+
+
+
+ class(massDistributionClass), pointer
+ 0
+ Galactic_Structure_Options, only : enumerationWeightByType, enumerationComponentTypeType, enumerationMassTypeType
+ Mass_Distributions , only : massDistributionClass
+
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+
+
+
+ void
+
+
+
+
+
+ !!]
+
+ ! Objects used by this component.
+ class(darkMatterProfileClass ), pointer :: darkMatterProfile_
+ class(darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_
+ !$omp threadprivate(darkMatterProfile_,darkMatterProfileDMO_)
+
+ ! Procedure pointers to mass distribution functions.
+ procedure(Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist), pointer :: Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist_
+
+ ! Mass distribution pointer used for post-construction initialization.
+ class(massDistributionClass), pointer :: massDistribution__
+ !$omp threadprivate(massDistribution__)
+
+contains
+
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Free_Init
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Init(parameters)
+ !!{
+ Initializes the scale dark matter profile component.
+ !!}
+ use :: Input_Parameters, only : inputParameters
+ use :: Galacticus_Nodes, only : defaultDarkMatterProfileComponent, nodeComponentDarkMatterProfileScaleFree
+ type(inputParameters ), intent(inout) :: parameters
+ type(nodeComponentDarkMatterProfileScaleFree) :: darkMatterProfile
+ !$GLC attributes unused :: parameters
+
+ !$omp critical (Node_Component_Dark_Matter_Profile_Init)
+ if (defaultDarkMatterProfileComponent%scaleFreeIsActive()) then
+ Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist_ => Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist
+ call darkMatterProfile%massDistributionFunction(Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist_)
+ end if
+ !$omp end critical (Node_Component_Dark_Matter_Profile_Init)
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Init
+
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Init
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Init(parameters_)
+ !!{
+ Initializes the tree node scale dark matter profile module.
+ !!}
+ use :: Galacticus_Nodes, only : defaultDarkMatterProfileComponent
+ use :: Input_Parameters, only : inputParameter , inputParameters
+ implicit none
+ type(inputParameters), intent(inout) :: parameters_
+ !$GLC attributes unused :: parameters_
+
+ if (defaultDarkMatterProfileComponent%scaleFreeIsActive()) then
+ !![
+
+
+ !!]
+ end if
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Init
+
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Uninit
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Uninit()
+ !!{
+ Uninitializes the tree node scale dark matter profile module.
+ !!}
+ use :: Galacticus_Nodes, only : defaultDarkMatterProfileComponent
+ implicit none
+
+ if (defaultDarkMatterProfileComponent%scaleFreeIsActive()) then
+ !![
+
+
+ !!]
+ end if
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Free_Thread_Uninit
+
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Free_State_Store
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Free_State_Store(stateFile,gslStateFile,stateOperationID)
+ !!{
+ Store object state,
+ !!}
+ use :: Display , only : displayMessage, verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
+ implicit none
+ integer , intent(in ) :: stateFile
+ integer(c_size_t), intent(in ) :: stateOperationID
+ type (c_ptr ), intent(in ) :: gslStateFile
+
+ call displayMessage('Storing state for: componentDarkMatterProfile -> scaleFree',verbosity=verbosityLevelInfo)
+ !![
+
+ !!]
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Free_State_Store
+
+ !![
+
+ Node_Component_Dark_Matter_Profile_Scale_Free_State_Restore
+
+ !!]
+ subroutine Node_Component_Dark_Matter_Profile_Scale_Free_State_Restore(stateFile,gslStateFile,stateOperationID)
+ !!{
+ Retrieve object state.
+ !!}
+ use :: Display , only : displayMessage, verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding, only : c_ptr , c_size_t
+ implicit none
+ integer , intent(in ) :: stateFile
+ integer(c_size_t), intent(in ) :: stateOperationID
+ type (c_ptr ), intent(in ) :: gslStateFile
+
+ call displayMessage('Retrieving state for: componentDarkProfile -> scaleFree',verbosity=verbosityLevelInfo)
+ !![
+
+ !!]
+ return
+ end subroutine Node_Component_Dark_Matter_Profile_Scale_Free_State_Restore
+
+ function Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the mass distribution associated with the dark matter profile.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentDarkMatterProfileScaleFree
+ use :: Galactic_Structure_Options, only : enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType, componentTypeAll, &
+ & componentTypeDarkHalo , componentTypeDarkMatterOnly , massTypeAll , massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponentDarkMatterProfileScaleFree), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !![
+
+
+ !!]
+
+ massDistribution_ => null()
+ massDistribution__ => null()
+ if ( &
+ & massType_ == massTypeAll &
+ & .or. &
+ & massType_ == massTypeDark &
+ & ) then
+ if ( &
+ & componentType_ == componentTypeAll &
+ & .or. &
+ & componentType_ == componentTypeDarkHalo &
+ & ) then
+ massDistribution_ => darkMatterProfile_ %get(self%hostNode,weightBy,weightIndex)
+ massDistribution__ => massDistribution_
+ else if ( &
+ & componentType_ == componentTypeDarkMatterOnly &
+ & ) then
+ massDistribution_ => darkMatterProfileDMO_%get(self%hostNode,weightBy,weightIndex)
+ call massDistribution_%setTypes(componentType=componentTypeDarkMatterOnly)
+ end if
+ end if
+ return
+ end function Node_Component_Dark_Matter_Profile_Scale_Free_Mass_Dist
+
+end module Node_Component_Dark_Matter_Profile_Scale_Free
diff --git a/source/objects.nodes.components.disk.standard.F90 b/source/objects.nodes.components.disk.standard.F90
index d18aca6fdf..1ee978e9f8 100644
--- a/source/objects.nodes.components.disk.standard.F90
+++ b/source/objects.nodes.components.disk.standard.F90
@@ -29,7 +29,6 @@ module Node_Component_Disk_Standard
use :: Satellite_Merging_Mass_Movements, only : mergerMassMovementsClass
use :: Star_Formation_Histories , only : starFormationHistory , starFormationHistoryClass
use :: Stellar_Population_Properties , only : stellarPopulationPropertiesClass
- use :: Galactic_Structure , only : galacticStructureClass
implicit none
private
public :: Node_Component_Disk_Standard_Scale_Set , Node_Component_Disk_Standard_Pre_Evolve , &
@@ -140,27 +139,9 @@ module Node_Component_Disk_Standard
-
-
-
-
-
-
-
-
-
-
-
-
- double
- 3
-
- type (treeNode ), intent(inout) :: nodeSatellite
- double precision , intent(in ), dimension(3) :: positionCartesian, velocityCartesian
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
-
-
+
+
+
objects.nodes.components.disk.standard.bound_functions.inc
@@ -171,8 +152,7 @@ module Node_Component_Disk_Standard
class(stellarPopulationPropertiesClass), pointer :: stellarPopulationProperties_
class(starFormationHistoryClass ), pointer :: starFormationHistory_
class(mergerMassMovementsClass ), pointer :: mergerMassMovements_
- class(galacticStructureClass ), pointer :: galacticStructure_
- !$omp threadprivate(darkMatterHaloScale_,stellarPopulationProperties_,starFormationHistory_,mergerMassMovements_,galacticStructure_)
+ !$omp threadprivate(darkMatterHaloScale_,stellarPopulationProperties_,starFormationHistory_,mergerMassMovements_)
! Internal count of abundances.
integer :: abundancesCount
@@ -230,8 +210,6 @@ subroutine Node_Component_Disk_Standard_Initialize(parameters)
call diskStandardComponent%attachPipes()
pipesAttached=.true.
end if
- ! Bind the Chandrasekhar integral function.
- call diskStandardComponent%chandrasekharIntegralFunction(Node_Component_Disk_Standard_Chandrasekhar_Integral)
! Find our parameters.
subParameters=parameters%subParameters('componentDisk')
! Read parameters controlling the physical implementation.
@@ -286,13 +264,14 @@ subroutine Node_Component_Disk_Standard_Thread_Initialize(parameters)
!!{
Initializes the standard disk component module for each thread.
!!}
- use :: Events_Hooks , only : dependencyDirectionAfter , dependencyRegEx , openMPThreadBindingAtLevel, postEvolveEvent, &
+ use :: Events_Hooks , only : dependencyDirectionAfter , dependencyRegEx , openMPThreadBindingAtLevel, postEvolveEvent, &
& satelliteMergerEvent , mergerTreeExtraOutputEvent
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : defaultDiskComponent
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Mass_Distributions , only : massDistributionCylindrical
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk , massDistributionDisk_
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_ , massDistributionGas_ , kinematicDistribution_
+ use :: Mass_Distributions , only : massDistributionCylindrical, kinematicsDistributionLocal
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeStellar , massTypeGaseous
implicit none
type (inputParameters), intent(inout) :: parameters
type (dependencyRegEx), dimension(2) :: dependencies
@@ -310,12 +289,11 @@ subroutine Node_Component_Disk_Standard_Thread_Initialize(parameters)
! Find our parameters.
subParameters=parameters%subParameters('componentDisk')
!![
-
-
-
-
-
-
+
+
+
+
+
@@ -324,29 +302,36 @@ subroutine Node_Component_Disk_Standard_Thread_Initialize(parameters)
!!]
! Validate the disk mass distribution.
- select type (massDistributionDisk_)
+ select type (massDistributionStellar_)
class is (massDistributionCylindrical)
- ! Since the disk must be cylindrical, deep-copy it to an object of that class. Then we do not need to perform
- ! type-guards elsewhere in the code.
- allocate(massDistributionDisk,mold=massDistributionDisk_)
- !$omp critical(diskStandardDeepCopy)
- !![
-
-
-
- !!]
- !$omp end critical(diskStandardDeepCopy)
+ ! The disk mass distribution must have cylindrical symmetry. So, this is acceptable.
class default
call Error_Report('only cylindrically symmetric mass distributions are allowed'//{introspection:location})
end select
- if (.not.massDistributionDisk%isDimensionless()) call Error_Report('disk mass distribution must be dimensionless'//{introspection:location})
+ if (.not.massDistributionStellar_%isDimensionless()) call Error_Report('disk mass distribution must be dimensionless'//{introspection:location})
+ ! Duplicate the dimensionless mass distribution to use for the gas component, and set component and mass types in both.
+ !$omp critical(diskStandardDeepCopy)
+ allocate(massDistributionGas_,mold=massDistributionStellar_)
+ !![
+
+
+
+ !!]
+ !$omp end critical(diskStandardDeepCopy)
+ call massDistributionStellar_%setTypes(componentTypeDisk,massTypeStellar)
+ call massDistributionGas_ %setTypes(componentTypeDisk,massTypeGaseous)
+ ! Construct the kinematic distribution.
+ allocate(kinematicDistribution_)
+ !![
+
+ !!]
! Compute the specific angular momentum of the disk at this structure solver radius in units of the mean specific angular
! momentum of the disk assuming a flat rotation curve.
!! Determine the specific angular momentum at the size solver radius in units of the mean specific angular
!! momentum of the disk. This is equal to the ratio of the 1st to 2nd radial moments of the surface density
!! distribution (assuming a flat rotation curve).
- massDistributionDiskDensityMoment1=massDistributionDisk%surfaceDensityRadialMoment(1.0d0,isInfinite=surfaceDensityMoment1IsInfinite)
- massDistributionDiskDensityMoment2=massDistributionDisk%surfaceDensityRadialMoment(2.0d0,isInfinite=surfaceDensityMoment2IsInfinite)
+ massDistributionDiskDensityMoment1=massDistributionStellar_%surfaceDensityRadialMoment(1.0d0,isInfinite=surfaceDensityMoment1IsInfinite)
+ massDistributionDiskDensityMoment2=massDistributionStellar_%surfaceDensityRadialMoment(2.0d0,isInfinite=surfaceDensityMoment2IsInfinite)
if (surfaceDensityMoment1IsInfinite.or.surfaceDensityMoment2IsInfinite) then
! One or both of the moments are infinite. Simply assume a value of 0.5 as a default.
diskStructureSolverSpecificAngularMomentum=0.5d0
@@ -362,9 +347,9 @@ subroutine Node_Component_Disk_Standard_Thread_Initialize(parameters)
! curves for thin disk and a spherical mass distribution.
if (structureSolverUseCole2000Method) then
diskRadiusSolverFlatVsSphericalFactor= &
- & +massDistributionDisk%rotationCurve (radiusStructureSolver)**2 &
- & * radiusStructureSolver &
- & -massDistributionDisk%massEnclosedBySphere(radiusStructureSolver)
+ & +massDistributionStellar_%rotationCurve (radiusStructureSolver)**2 &
+ & * radiusStructureSolver &
+ & -massDistributionStellar_%massEnclosedBySphere(radiusStructureSolver)
end if
end if
return
@@ -379,9 +364,9 @@ subroutine Node_Component_Disk_Standard_Thread_Uninitialize()
!!{
Uninitializes the standard disk component module for each thread.
!!}
- use :: Events_Hooks , only : postEvolveEvent , satelliteMergerEvent , mergerTreeExtraOutputEvent
+ use :: Events_Hooks , only : postEvolveEvent , satelliteMergerEvent, mergerTreeExtraOutputEvent
use :: Galacticus_Nodes , only : defaultDiskComponent
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk, massDistributionDisk_
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
if (defaultDiskComponent%standardIsActive()) then
@@ -393,9 +378,9 @@ subroutine Node_Component_Disk_Standard_Thread_Uninitialize()
-
-
-
+
+
+
!!]
end if
return
@@ -1247,9 +1232,9 @@ subroutine Node_Component_Disk_Standard_State_Store(stateFile,gslStateFile,state
!!{
Write the tabulation state to file.
!!}
- use :: Display , only : displayMessage , verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
+ use :: Display , only : displayMessage , verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -1257,7 +1242,7 @@ subroutine Node_Component_Disk_Standard_State_Store(stateFile,gslStateFile,state
call displayMessage('Storing state for: componentDisk -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
write (stateFile) diskStructureSolverSpecificAngularMomentum,diskRadiusSolverFlatVsSphericalFactor
return
@@ -1272,9 +1257,9 @@ subroutine Node_Component_Disk_Standard_State_Retrieve(stateFile,gslStateFile,st
!!{
Retrieve the tabulation state from the file.
!!}
- use :: Display , only : displayMessage , verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
+ use :: Display , only : displayMessage , verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -1282,152 +1267,10 @@ subroutine Node_Component_Disk_Standard_State_Retrieve(stateFile,gslStateFile,st
call displayMessage('Retrieving state for: componentDisk -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
read (stateFile) diskStructureSolverSpecificAngularMomentum,diskRadiusSolverFlatVsSphericalFactor
return
end subroutine Node_Component_Disk_Standard_State_Retrieve
- function Node_Component_Disk_Standard_Chandrasekhar_Integral(self,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
- !!{
- Computes the gravitational acceleration at a given position for a standard disk.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentDiskStandard , treeNode
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , weightByMass , &
- & weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
- use :: Coordinates , only : assignment(=) , coordinateSpherical , coordinateCartesian , coordinateCylindrical
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Mass_Distributions , only : massDistributionGaussianEllipsoid
- use :: Linear_Algebra , only : vector , matrix , assignment(=)
- implicit none
- double precision , dimension(3) :: Node_Component_Disk_Standard_Chandrasekhar_Integral
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (treeNode ), intent(inout) :: nodeSatellite
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , parameter :: toomreQRadiusHalfMass =1.50d0 ! The Toomre Q-parameter at the disk half-mass radius (Benson et al.,
- ! 2004 , https://ui.adsabs.harvard.edu/abs/2004MNRAS.351.1215B, Appendix A).
- double precision , parameter :: toomreQFactor =3.36d0 ! The factor appearing in the definition of the Toomre Q-parameter for
- ! a stellar disk (Binney & Tremaine, eqn. 6.71).
- double precision , dimension(3) :: velocityDisk , velocityRelative , &
- & positionSpherical , positionSphericalMidplane , &
- & positionCartesianMidplane , positionCylindricalMidplane , &
- & positionCylindricalHalfMass
- type (massDistributionGaussianEllipsoid), save :: velocityDistribution
- logical , save :: velocityDistributionInitialized =.false.
- !$omp threadprivate(velocityDistribution,velocityDistributionInitialized)
- type (coordinateSpherical ) :: coordinatesSpherical
- type (coordinateCartesian ) :: coordinatesCartesian
- type (coordinateCylindrical ) :: coordinatesCylindrical
- double precision :: velocityDispersionRadial , velocityDispersionAzimuthal , &
- & velocityDispersionVertical , velocityCircular , &
- & velocityCircularHalfMassRadius , velocityCircularSquaredGradient , &
- & velocityCircularSquaredGradientHalfMassRadius , density , &
- & densityMidPlane , densitySurface , &
- & heightScale , radiusMidplane , &
- & frequencyCircular , frequencyEpicyclic , &
- & frequencyCircularHalfMassRadius , frequencyEpicyclicHalfMassRadius, &
- & densitySurfaceRadiusHalfMass , velocityDispersionRadialHalfMass, &
- & velocityDispersionMaximum , velocityRelativeMagnitude , &
- & factorSuppressionExtendedMass , radiusHalfMass
- type (matrix ) :: rotation
-
- ! Return if the disk component is not selected.
- Node_Component_Disk_Standard_Chandrasekhar_Integral=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk) .or. self%radius() <= 0.0d0) return
- ! Construct the velocity vector of the disk rotation.
- positionCartesianMidplane =[positionCartesian(1),positionCartesian(2),0.0d0]
- coordinatesCartesian = positionCartesian
- coordinatesSpherical = coordinatesCartesian
- positionSpherical = coordinatesSpherical
- coordinatesCartesian = positionCartesianMidplane
- coordinatesSpherical = coordinatesCartesian
- coordinatesCylindrical = coordinatesCartesian
- positionSphericalMidplane = coordinatesSpherical
- positionCylindricalMidplane = coordinatesCylindrical
- positionCylindricalHalfMass =[self%halfMassRadius(),0.0d0,0.0d0]
- radiusMidplane = coordinatesCylindrical%r()
- velocityCircular =self%rotationCurve ( radiusMidplane ,componentType,massType)
- velocityCircularSquaredGradient =self%rotationCurveGradient( radiusMidplane ,componentType,massType)
- velocityCircularHalfMassRadius =self%rotationCurve (self%halfMassRadius(),componentType,massType)
- velocityCircularSquaredGradientHalfMassRadius=self%rotationCurveGradient(self%halfMassRadius(),componentType,massType)
- velocityDisk =+[positionCartesianMidplane(2),-positionCartesianMidplane(1),0.0d0] &
- & /radiusMidplane &
- & *velocityCircular
- ! Compute epicyclic frequency.
- frequencyCircular =velocityCircular / radiusMidplane
- frequencyCircularHalfMassRadius =velocityCircularHalfMassRadius/self%halfMassRadius()
- frequencyEpicyclic =sqrt(velocityCircularSquaredGradient / radiusMidplane +2.0d0*frequencyCircular **2)
- frequencyEpicyclicHalfMassRadius=sqrt(velocityCircularSquaredGradientHalfMassRadius/self%halfMassRadius()+2.0d0*frequencyCircularHalfMassRadius**2)
- ! Get disk structural properties.
- density =+self%density (positionSpherical ,componentTypeDisk,massTypeAll,weightByMass,weightIndexNull)
- densityMidPlane =+self%density (positionSphericalMidplane ,componentTypeDisk,massTypeAll,weightByMass,weightIndexNull)
- densitySurface =+self%surfaceDensity(positionCylindricalMidplane,componentTypeDisk,massTypeAll,weightByMass,weightIndexNull)
- densitySurfaceRadiusHalfMass=+self%surfaceDensity(positionCylindricalHalfMass,componentTypeDisk,massTypeAll,weightByMass,weightIndexNull)
- if (density <= 0.0d0) return
- heightScale =+0.5d0 &
- & *densitySurface &
- & /densityMidPlane
- ! Compute normalization of the radial velocity dispersion.
- velocityDispersionRadialHalfMass=+toomreQFactor &
- & *gravitationalConstantGalacticus &
- & *densitySurfaceRadiusHalfMass &
- & *toomreQRadiusHalfMass &
- & /frequencyEpicyclicHalfMassRadius
- ! Find the velocity dispersion components of the disk.
- velocityDispersionRadial =+velocityDispersionRadialHalfMass &
- & *exp(- radiusMidPlane /self%radius()/2.0d0) &
- & /exp(-self%halfMassRadius()/self%radius()/2.0d0)
- velocityDispersionAzimuthal=+velocityDispersionRadial*frequencyEpicyclic/2.0d0/frequencyCircular
- velocityDispersionVertical =+sqrt(Pi*gravitationalConstantGalacticus*densitySurface*heightScale)
- velocityDispersionMaximum =+maxval([velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical])
- velocityDispersionRadial =+velocityDispersionRadial /velocityDispersionMaximum
- velocityDispersionAzimuthal=+velocityDispersionAzimuthal/velocityDispersionMaximum
- velocityDispersionVertical =+velocityDispersionVertical /velocityDispersionMaximum
- if (any([velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical] <= 0.0d0)) return
- ! Find the relative velocity of the perturber and the disk.
- velocityRelative=(velocityCartesian-velocityDisk)/velocityDispersionMaximum
- ! Handle limiting case of large relative velocity.
- velocityRelativeMagnitude=sqrt(sum(velocityRelative**2))
- ! Initialize the velocity distribution.
- rotation=reshape( &
- & [ &
- & +positionCartesianMidplane(1),-positionCartesianMidplane(2),+0.0d0 , &
- & +positionCartesianMidplane(2),+positionCartesianMidplane(1),+0.0d0 , &
- & +0.0d0 ,+0.0d0 ,+radiusMidplane &
- & ] &
- & /radiusMidplane , &
- & [3,3] &
- & )
- coordinatesCartesian=velocityRelative
- if (.not.velocityDistributionInitialized) then
- velocityDistribution =massDistributionGaussianEllipsoid(scaleLength=[1.0d0,1.0d0,1.0d0],rotation=rotation,mass=1.0d0,dimensionless=.true.)
- velocityDistributionInitialized=.true.
- end if
- call velocityDistribution%initialize(scaleLength=[velocityDispersionRadial,velocityDispersionAzimuthal,velocityDispersionVertical],rotation=rotation)
- ! Compute suppression factor due to satellite being an extended mass distribution. This is largely untested - it is meant to
- ! simply avoid extremely large accelerations for subhalo close to the disk plane when that subhalo is much more extended than
- ! the disk.
- radiusHalfMass=galacticStructure_%radiusEnclosingMass( &
- & nodeSatellite , &
- & massFractional=0.5d0 , &
- & componentType =componentTypeAll, &
- & massType =massTypeAll &
- & )
- if (radiusHalfMass > heightScale) then
- factorSuppressionExtendedMass=+heightScale &
- & /radiusHalfMass
- else
- factorSuppressionExtendedMass=+1.0d0
- end if
- ! Evaluate the integral.
- Node_Component_Disk_Standard_Chandrasekhar_Integral=+density &
- & *velocityDistribution %acceleration(coordinatesCartesian) &
- & /velocityDispersionMaximum **2 &
- & *factorSuppressionExtendedMass
- return
- end function Node_Component_Disk_Standard_Chandrasekhar_Integral
-
end module Node_Component_Disk_Standard
diff --git a/source/objects.nodes.components.disk.standard.bound_functions.Inc b/source/objects.nodes.components.disk.standard.bound_functions.Inc
index ee3f7cb983..eaf87c8382 100644
--- a/source/objects.nodes.components.disk.standard.bound_functions.Inc
+++ b/source/objects.nodes.components.disk.standard.bound_functions.Inc
@@ -32,476 +32,145 @@ subroutine Node_Component_Disk_Standard_Attach_Pipes(self)
!$GLC attributes unused :: self
if (hotHalo%hotHaloCoolingMassRateIsAttached ()) &
- call Error_Report('expected to find unclaimed hot halo mass cooling pipe'//{introspection:location})
+ call Error_Report('expected to find unclaimed hot halo mass cooling pipe' //{introspection:location})
if (hotHalo%hotHaloCoolingAngularMomentumRateIsAttached()) &
call Error_Report('expected to find unclaimed hot halo angular momentum cooling pipe'//{introspection:location})
if (hotHalo%hotHaloCoolingAbundancesRateIsAttached ()) &
- call Error_Report('expected to find unclaimed hot halo abundances cooling pipe'//{introspection:location})
+ call Error_Report('expected to find unclaimed hot halo abundances cooling pipe' //{introspection:location})
call hotHalo%hotHaloCoolingMassRateFunction (DiskStandardMassGasRateGeneric )
call hotHalo%hotHaloCoolingAngularMomentumRateFunction(DiskStandardAngularMomentumRateGeneric)
call hotHalo%hotHaloCoolingAbundancesRateFunction (DiskStandardAbundancesGasRateGeneric )
return
end subroutine Node_Component_Disk_Standard_Attach_Pipes
-double precision function Node_Component_Disk_Standard_Half_Mass_Radius(self)
+function Node_Component_Disk_Standard_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the half-mass radius of the standard disk.
- !!}
- use :: Error , only : Error_Report
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- implicit none
- class(nodeComponentDiskStandard), intent(inout) :: self
-
- Node_Component_Disk_Standard_Half_Mass_Radius=self%radius()*massDistributionDisk%radiusHalfMass()
- return
-end function Node_Component_Disk_Standard_Half_Mass_Radius
-
-double precision function Node_Component_Disk_Standard_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for an standard disk.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk, massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , radiusLarge , &
- & weightByLuminosity , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Math_Arithmetic , only : divideSafe
- implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- double precision :: radiusDisk , fractionalRadius
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
-
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Standard_Enclosed_Mass=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Get the total mass.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Disk_Standard_Enclosed_Mass=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Disk_Standard_Enclosed_Mass=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Disk_Standard_Enclosed_Mass= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Standard_Enclosed_Mass =luminositiesDisk%luminosity(weightIndex)
- end select
- end select
- ! Return if no mass.
- if (Node_Component_Disk_Standard_Enclosed_Mass <= 0.0d0) return
- ! Return if the total mass was requested.
- if (radius >= radiusLarge) return
- ! Compute the actual mass.
- radiusDisk=self%radius()
- if (radiusDisk > 0.0d0) then
- fractionalRadius=divideSafe(radius,radiusDisk)
- Node_Component_Disk_Standard_Enclosed_Mass= &
- & +Node_Component_Disk_Standard_Enclosed_Mass &
- & *massDistributionDisk%massEnclosedBySphere(fractionalRadius)
- end if
- return
-end function Node_Component_Disk_Standard_Enclosed_Mass
-
-function Node_Component_Disk_Standard_Acceleration(self,positionCartesian,componentType,massType)
- !!{
- Computes the gravitational acceleration at a given position for a standard disk.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateCartesian
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , enumerationComponentTypeType, &
- & enumerationMassTypeType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Numerical_Constants_Astronomical , only : gigaYear , megaParsec , gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: Node_Component_Disk_Standard_Acceleration
- class (nodeComponentDiskStandard ), intent(inout) :: self
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision :: radius , massTotal
- type (coordinateCartesian ) :: positionScaleFree
-
- ! Return if the disk component is not selected.
- Node_Component_Disk_Standard_Acceleration=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Determine total mass.
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- massTotal=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- massTotal=self%massGas()
- case (massTypeStellar%ID)
- massTotal= self%massStellar()
- case default
- massTotal=0.0d0
- end select
- ! Return if no mass.
- if (massTotal <= 0.0d0 .or. self%radius() <= 0.0d0) return
- ! Compute the acceleration.
- radius =sqrt(sum(positionCartesian**2))
- positionScaleFree=positionCartesian/self%radius()
- Node_Component_Disk_Standard_Acceleration=+kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *massTotal &
- & /self%radius() **2 &
- & *massDistributionDisk%acceleration(positionScaleFree)
- return
-end function Node_Component_Disk_Standard_Acceleration
-
-function Node_Component_Disk_Standard_Tidal_Tensor(self,positionCartesian,componentType,massType)
- !!{
- Computes the gravitational acceleration at a given position for a standard disk.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateCartesian
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar, enumerationComponentTypeType, &
- & enumerationMassTypeType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorNullR2D3Sym , operator(*)
- implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Disk_Standard_Tidal_Tensor
- class (nodeComponentDiskStandard ), intent(inout) :: self
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision :: radius , massTotal
- type (coordinateCartesian ) :: positionScaleFree
-
- ! Return if the disk component is not selected.q
- Node_Component_Disk_Standard_Tidal_Tensor=tensorNullR2D3Sym
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Determine total mass.
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- massTotal=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- massTotal=self%massGas()
- case (massTypeStellar%ID)
- massTotal= self%massStellar()
- case default
- massTotal=0.0d0
- end select
- ! Return if no mass.
- if (massTotal <= 0.0d0 .or. self%radius() <= 0.0d0) return
- ! Compute the acceleration.
- radius =sqrt(sum(positionCartesian**2))
- positionScaleFree=positionCartesian/self%radius()
- Node_Component_Disk_Standard_Tidal_Tensor=+gravitationalConstantGalacticus &
- & *massTotal &
- & /self%radius() **3 &
- & *massDistributionDisk%tidalTensor(positionScaleFree)
- return
-end function Node_Component_Disk_Standard_Tidal_Tensor
-
-double precision function Node_Component_Disk_Standard_Density(self,positionSpherical,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for an standard disk.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionSpherical(3)
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
- type (coordinateSpherical ) :: position
-
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Standard_Density=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Determine mass/luminosity type.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Disk_Standard_Density=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Disk_Standard_Density=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Disk_Standard_Density= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Standard_Density=luminositiesDisk%luminosity(weightIndex)
- end select
- end select
- ! Skip further calculation if mass or radius is zero.
- if (Node_Component_Disk_Standard_Density > 0.0d0 .and. self%radius() > 0.0d0) then
- ! Compute the actual density.
- position=[positionSpherical(1)/self%radius(),positionSpherical(2),positionSpherical(3)]
- Node_Component_Disk_Standard_Density=+Node_Component_Disk_Standard_Density &
- & /self %radius ( )**3 &
- & *massDistributionDisk%density(position)
- end if
- return
-end function Node_Component_Disk_Standard_Density
-
-double precision function Node_Component_Disk_Standard_Density_Spherical_Average(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for an standard disk.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Math_Arithmetic , only : divideSafe
- implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
-
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Standard_Density_Spherical_Average=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Determine mass/luminosity type.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Disk_Standard_Density_Spherical_Average=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Disk_Standard_Density_Spherical_Average=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Disk_Standard_Density_Spherical_Average= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Standard_Density_Spherical_Average=luminositiesDisk%luminosity(weightIndex)
- end select
- end select
- ! Skip further calculation if mass or radius is zero.
- if (Node_Component_Disk_Standard_Density_Spherical_Average > 0.0d0 .and. self%radius() > 0.0d0) then
- ! Compute the actual density.
- Node_Component_Disk_Standard_Density_Spherical_Average=+Node_Component_Disk_Standard_Density_Spherical_Average &
- & /self %radius ( )**3 &
- & *massDistributionDisk%densitySphericalAverage(divideSafe(radius,self%radius()))
- end if
- return
- end function Node_Component_Disk_Standard_Density_Spherical_Average
-
-double precision function Node_Component_Disk_Standard_Potential(self,radius,componentType,massType,status)
- !!{
- Compute the gravitational potential due to an standard disk.
+ Return the mass distribution for the standard disk component.
!!}
- use :: Coordinates , only : assignment(=) , coordinateCylindrical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , radiusLarge , weightByMass , &
- & weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType, enumerationStructureErrorCodeType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
- use :: Math_Arithmetic , only : divideSafe
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionCylindricalScaler, massDistributionComposite , massDistributionList , &
+ & massDistributionCylindrical, massDistributionMatches_
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_ , massDistributionGas_ , kinematicDistribution_
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeStellar , massTypeGaseous , enumerationWeightByType, &
+ & weightByMass , weightByLuminosity , enumerationComponentTypeType, enumerationMassTypeType
implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- double precision :: componentMass
- type (coordinateCylindrical ) :: position
- !$GLC attributes unused :: status
-
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Standard_Potential=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Avoid an arithmetic exception at radius zero.
- if (radius <= 0.0d0) return
- ! Get the relevant mass of the disk.
- componentMass=self%enclosedMass(radiusLarge,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass <= 0.0d0) return
- ! Check for zero-sized disk.
- if (self%radius() <= 0.0d0) then
- ! Treat as a point mass.
- Node_Component_Disk_Standard_Potential=-gravitationalConstantGalacticus &
- & *componentMass &
- & /radius
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponentDiskStandard ), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ type (massDistributionCylindricalScaler), pointer :: massDistributionStellar , massDistributionGas
+ type (massDistributionComposite ), pointer :: massDistributionTotal
+ type (massDistributionList ), pointer :: massDistributionComponents
+ type (stellarLuminosities ), save :: luminosities
+ !$omp threadprivate(luminosities)
+ double precision :: massStellar , massGas , &
+ & radiusScale
+ logical :: includeGas , includeStars
+ !![
+
+ !!]
+
+ ! Determine which components of the disk to include.
+ includeGas =massDistributionMatches_(componentTypeDisk,massTypeGaseous,componentType,massType) .and. weightBy_ == weightByMass
+ includeStars=massDistributionMatches_(componentTypeDisk,massTypeStellar,componentType,massType) .and. (weightBy_ == weightByMass .or. weightBy_ == weightByLuminosity)
+ ! Get properties of the mass distribution and ensure they are physical.
+ if (weightBy_ == weightByMass ) then
+ massStellar = max (0.0d0,self %massStellar ( ))
+ massGas = max (0.0d0,self %massGas ( ))
+ else if (weightBy_ == weightByLuminosity) then
+ luminosities = self %luminositiesStellar( )
+ massStellar = max (0.0d0,luminosities%luminosity (weightIndex))
+ massGas = 0.0d0
else
- ! Compute the potential.
- position=[divideSafe(radius,self%radius()),0.0d0,0.0d0]
- Node_Component_Disk_Standard_Potential=+gravitationalConstantGalacticus &
- & *componentMass &
- & /self%radius() &
- & *massDistributionDisk%potential(position)
+ massDistribution_ => null()
+ return
end if
- return
-end function Node_Component_Disk_Standard_Potential
-
-double precision function Node_Component_Disk_Standard_Rotation_Curve(self,radius,componentType,massType)
- !!{
- Computes the rotation curve at a given radius for an standard disk.
- !!}
- use :: Galactic_Structure_Options , only : radiusLarge , weightByMass, weightIndexNull, enumerationComponentTypeType, &
- & enumerationMassTypeType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
- use :: Math_Arithmetic , only : divideSafe
- implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentMass, radiusDisk, fractionalRadius
-
- ! Set to zero by default.
- Node_Component_Disk_Standard_Rotation_Curve=0.0d0
- ! Get the mass of the disk.
- componentMass=self%enclosedMass(radiusLarge,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass <= 0.0d0) return
- ! Compute the actual velocity.
- radiusDisk=self%radius()
- if (radiusDisk > 0.0d0) then
- fractionalRadius=divideSafe(radius,radiusDisk)
- Node_Component_Disk_Standard_Rotation_Curve=+sqrt( &
- & +gravitationalConstantGalacticus &
- & *componentMass &
- & /radiusDisk &
- & ) &
- & *massDistributionDisk%rotationCurve(fractionalRadius)
+ ! Determine which components to build.
+ radiusScale=self%radius()
+ if (radiusScale <= 0.0d0 .or. .not.(includeGas .or. includeStars)) then
+ ! Disk has non-positive size, or no components matched. Return a null distribution.
+ massDistribution_ => null()
+ else
+ ! Build the individual distributions.
+ massDistributionStellar => null()
+ massDistributionGas => null()
+ if (includeStars) then
+ allocate(massDistributionStellar)
+ select type (massDistributionStellar_)
+ class is (massDistributionCylindrical)
+ !![
+
+ !!]
+ end select
+ call massDistributionStellar%setKinematicsDistribution(kinematicDistribution_)
+ end if
+ if (includeGas ) then
+ allocate(massDistributionGas )
+ select type (massDistributionGas_ )
+ class is (massDistributionCylindrical)
+ !![
+
+ !!]
+ end select
+ call massDistributionGas %setKinematicsDistribution(kinematicDistribution_)
+ end if
+ ! Combine the distributions as necessary.
+ if (includeStars .and. includeGas) then
+ ! Wrap the dimensionless mass distribution inside scaler classes to allow us to re-scale it to any disk system, and then composite those.
+ allocate(massDistributionTotal )
+ allocate(massDistributionComponents )
+ allocate(massDistributionComponents%next)
+ massDistributionComponents %massDistribution_ => massDistributionStellar
+ massDistributionComponents%next%massDistribution_ => massDistributionGas
+ !![
+
+
+
+ !!]
+ nullify(massDistributionComponents)
+ ! Return a pointer to the disk mass distribution.
+ massDistribution_ => massDistributionTotal
+ else if (includeStars ) then
+ ! Return just the stellar component.
+ massDistribution_ => massDistributionStellar
+ else if ( includeGas) then
+ ! Return just the gas component.
+ massDistribution_ => massDistributionGas
+ end if
end if
return
-end function Node_Component_Disk_Standard_Rotation_Curve
+end function Node_Component_Disk_Standard_Mass_Distribution
-double precision function Node_Component_Disk_Standard_Rotation_Curve_Gradient(self,radius,componentType,massType)
+double precision function Node_Component_Disk_Standard_Mass_Baryonic(self) result(massBaryonic)
!!{
- Computes the rotation curve gradient for an standard disk.
+ Return the baryonic mass for the standard disk component.
!!}
- use :: Galactic_Structure_Options , only : radiusLarge , weightByMass, weightIndexNull, enumerationComponentTypeType, &
- & enumerationMassTypeType
- use :: Node_Component_Disk_Standard_Data, only : massDistributionDisk
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
- use :: Math_Arithmetic , only : divideSafe
implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: radiusDisk , fractionalRadius, &
- & componentMass
+ class(nodeComponentDiskStandard), intent(inout) :: self
- ! Set to zero by default.
- Node_Component_Disk_Standard_Rotation_Curve_Gradient=0.0d0
- ! Return if radius is zero.
- if (radius <= 0.0d0) return
- ! Get the mass of the disk.
- componentMass=self%enclosedMass(radiusLarge,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass <= 0.0d0) return
- ! Compute the rotation curve gradient.
- radiusDisk=self%radius()
- if (radiusDisk > 0.0d0) then
- fractionalRadius=divideSafe(radius,radiusDisk)
- Node_Component_Disk_Standard_Rotation_Curve_Gradient=+gravitationalConstantGalacticus &
- & *componentMass &
- & /radiusDisk**2 &
- & *massDistributionDisk%rotationCurveGradient(fractionalRadius)
- end if
+ massBaryonic=+max(0.0d0,self%massStellar()) &
+ & +max(0.0d0,self%massGas ())
return
-end function Node_Component_Disk_Standard_Rotation_Curve_Gradient
+end function Node_Component_Disk_Standard_Mass_Baryonic
-double precision function Node_Component_Disk_Standard_Surface_Density(self,positionCylindrical,componentType,massType,weightBy,weightIndex)
+double precision function Node_Component_Disk_Standard_Half_Mass_Radius(self) result(radiusHalfMass)
!!{
- Computes the surface density at a given position for an standard disk.
+ Return the half-mass radius of the standard disk.
!!}
- use :: Coordinates , only : coordinateCylindrical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType , enumerationWeightByType
- use :: Node_Component_Disk_Standard_Data, only : Node_Component_Disk_Standard_Reset , massDistributionDisk , lastUniqueID , radiusScaleDisk , &
- & radiusScaleDiskComputed , surfaceDensityCentralGas , surfaceDensityCentralGasComputed , surfaceDensityCentralStellar, &
- & surfaceDensityCentralStellarComputed, surfaceDensityCentralTotal , surfaceDensityCentralTotalComputed
- use :: Numerical_Constants_Math , only : Pi
+ use :: Error , only : Error_Report
+ use :: Mass_Distributions , only : massDistributionCylindrical
+ use :: Node_Component_Disk_Standard_Data, only : massDistributionStellar_
implicit none
- class (nodeComponentDiskStandard ), intent(inout) :: self
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionCylindrical(3)
- type (treeNode ), pointer :: selfNode
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
- type (coordinateCylindrical ) :: position
+ class(nodeComponentDiskStandard), intent(inout) :: self
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Standard_Surface_Density=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Check whether this is a new node.
- selfNode => self%host()
- if (selfNode%uniqueID() /= lastUniqueID) call Node_Component_Disk_Standard_Reset(selfNode%uniqueID())
- ! Determine disk radius.
- if (.not.radiusScaleDiskComputed) then
- radiusScaleDisk =self%radius()
- radiusScaleDiskComputed=.true.
- end if
- ! Return zero if the disk has unphysical size.
- if (radiusScaleDisk <= 0.0d0) then
- Node_Component_Disk_Standard_Surface_Density=0.0d0
- return
- end if
- ! Determine mass type.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- if (.not.surfaceDensityCentralTotalComputed ) then
- surfaceDensityCentralTotal =(self%massGas()+self%massStellar())/radiusScaleDisk**2
- surfaceDensityCentralTotalComputed =.true.
- end if
- Node_Component_Disk_Standard_Surface_Density=surfaceDensityCentralTotal
- case (massTypeGaseous%ID)
- if (.not.surfaceDensityCentralGasComputed ) then
- surfaceDensityCentralGas = self%massGas() /radiusScaleDisk**2
- surfaceDensityCentralGasComputed =.true.
- end if
- Node_Component_Disk_Standard_Surface_Density=surfaceDensityCentralGas
- case (massTypeStellar%ID)
- if (.not.surfaceDensityCentralStellarComputed) then
- surfaceDensityCentralStellar = self%massStellar() /radiusScaleDisk**2
- surfaceDensityCentralStellarComputed=.true.
- end if
- Node_Component_Disk_Standard_Surface_Density=surfaceDensityCentralStellar
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Standard_Surface_Density=luminositiesDisk%luminosity(weightIndex)/2.0d0/Pi/radiusScaleDisk**2
- end select
+ select type (massDistributionStellar_)
+ class is (massDistributionCylindrical)
+ radiusHalfMass=+massDistributionStellar_%radiusHalfMass() &
+ & *self %radius ()
+ class default
+ radiusHalfMass=0.0d0
+ call Error_Report('disk mass distribution is not cylindrically-symmetric'//{introspection:location})
end select
- ! Return if no surface density.
- if (Node_Component_Disk_Standard_Surface_Density <= 0.0d0) return
- ! Compute the surface density.
- call position%rSet(positionCylindrical(1)/radiusScaleDisk)
- Node_Component_Disk_Standard_Surface_Density= &
- & +Node_Component_Disk_Standard_Surface_Density &
- & *massDistributionDisk%surfaceDensity(position)
return
-end function Node_Component_Disk_Standard_Surface_Density
+end function Node_Component_Disk_Standard_Half_Mass_Radius
diff --git a/source/objects.nodes.components.disk.standard.data.F90 b/source/objects.nodes.components.disk.standard.data.F90
index 6b4fde528b..a7133ab98c 100644
--- a/source/objects.nodes.components.disk.standard.data.F90
+++ b/source/objects.nodes.components.disk.standard.data.F90
@@ -26,29 +26,29 @@ module Node_Component_Disk_Standard_Data
Stores data for the standard disk node component.
!!}
use :: Kind_Numbers , only : kind_int8
- use :: Mass_Distributions, only : massDistributionClass, massDistributionCylindrical
+ use :: Mass_Distributions, only : massDistributionClass, kinematicsDistributionLocal
implicit none
public
! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID =-1
+ integer (kind=kind_int8 ) :: lastUniqueID =-1
!$omp threadprivate(lastUniqueID)
! Records of previously computed and stored quantities.
- logical :: surfaceDensityCentralGasComputed , surfaceDensityCentralStellarComputed, &
- & surfaceDensityCentralTotalComputed
+ logical :: surfaceDensityCentralGasComputed , surfaceDensityCentralStellarComputed, &
+ & surfaceDensityCentralTotalComputed
!$omp threadprivate(surfaceDensityCentralGasComputed,surfaceDensityCentralStellarComputed,surfaceDensityCentralTotalComputed)
- double precision :: surfaceDensityCentralGas , surfaceDensityCentralStellar , &
- & surfaceDensityCentralTotal
+ double precision :: surfaceDensityCentralGas , surfaceDensityCentralStellar , &
+ & surfaceDensityCentralTotal
!$omp threadprivate(surfaceDensityCentralGas,surfaceDensityCentralStellar,surfaceDensityCentralTotal)
- logical :: radiusScaleDiskComputed
+ logical :: radiusScaleDiskComputed
!$omp threadprivate(radiusScaleDiskComputed)
- double precision :: radiusScaleDisk
+ double precision :: radiusScaleDisk
!$omp threadprivate(radiusScaleDisk)
- ! The mass distribution object.
- class (massDistributionClass ), pointer :: massDistributionDisk_
- class (massDistributionCylindrical), pointer :: massDistributionDisk
- !$omp threadprivate(massDistributionDisk_,massDistributionDisk)
+ ! The mass distribution objects.
+ class (massDistributionClass ), pointer :: massDistributionStellar_ , massDistributionGas_
+ type (kinematicsDistributionLocal), pointer :: kinematicDistribution_
+ !$omp threadprivate(massDistributionStellar_,massDistributionGas_,kinematicDistribution_)
contains
diff --git a/source/objects.nodes.components.disk.very_simple.F90 b/source/objects.nodes.components.disk.very_simple.F90
index 58fad8547b..0d2e92dde6 100644
--- a/source/objects.nodes.components.disk.very_simple.F90
+++ b/source/objects.nodes.components.disk.very_simple.F90
@@ -27,7 +27,6 @@ module Node_Component_Disk_Very_Simple
!!}
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Galacticus_Nodes , only : treeNode
use :: Math_Exponentiation , only : fastExponentiator
use :: Satellite_Merging_Mass_Movements, only : mergerMassMovementsClass
@@ -97,8 +96,8 @@ module Node_Component_Disk_Very_Simple
-
-
+
+
objects.nodes.components.disk.very_simple.bound_functions.inc
@@ -110,9 +109,8 @@ module Node_Component_Disk_Very_Simple
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(stellarFeedbackOutflowsClass ), pointer :: stellarFeedbackOutflows_
class(starFormationRateDisksClass ), pointer :: starFormationRateDisks_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(mergerMassMovementsClass ), pointer :: mergerMassMovements_
- !$omp threadprivate(cosmologyFunctions_,stellarPopulationProperties_,darkMatterHaloScale_,stellarFeedbackOutflows_,starFormationRateDisks_,darkMatterProfileDMO_,mergerMassMovements_)
+ !$omp threadprivate(cosmologyFunctions_,stellarPopulationProperties_,darkMatterHaloScale_,stellarFeedbackOutflows_,starFormationRateDisks_,mergerMassMovements_)
! Record of whether to use the simple disk analytic solver.
logical :: useAnalyticSolver
@@ -222,7 +220,6 @@ subroutine Node_Component_Disk_Very_Simple_Thread_Initialize(parameters)
-
@@ -253,7 +250,6 @@ subroutine Node_Component_Disk_Very_Simple_Thread_Uninitialize()
-
@@ -904,7 +900,7 @@ subroutine Node_Component_Disk_Very_Simple_State_Store(stateFile,gslStateFile,st
call displayMessage('Storing state for: componentDisk -> verySimple',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Disk_Very_Simple_State_Store
@@ -927,7 +923,7 @@ subroutine Node_Component_Disk_Very_Simple_State_Restore(stateFile,gslStateFile,
call displayMessage('Retrieving state for: componentDisk -> verySimple',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Disk_Very_Simple_State_Restore
diff --git a/source/objects.nodes.components.disk.very_simple.bound_functions.Inc b/source/objects.nodes.components.disk.very_simple.bound_functions.Inc
index 126be1ab96..fd9e91b525 100644
--- a/source/objects.nodes.components.disk.very_simple.bound_functions.Inc
+++ b/source/objects.nodes.components.disk.very_simple.bound_functions.Inc
@@ -45,6 +45,18 @@ subroutine Node_Component_Disk_Very_Simple_Attach_Pipe(self)
return
end subroutine Node_Component_Disk_Very_Simple_Attach_Pipe
+double precision function Node_Component_Disk_Very_Simple_Mass_Baryonic(self) result(massBaryonic)
+ !!{
+ Return the baryonic mass for the very simple disk component.
+ !!}
+ implicit none
+ class(nodeComponentDiskVerySimple), intent(inout) :: self
+
+ massBaryonic=+max(0.0d0,self%massStellar()) &
+ & +max(0.0d0,self%massGas ())
+ return
+end function Node_Component_Disk_Very_Simple_Mass_Baryonic
+
double precision function Node_Component_Disk_Very_Simple_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
!!{
Computes the mass within a given radius for an very simple disk.
diff --git a/source/objects.nodes.components.disk.very_simple.size.F90 b/source/objects.nodes.components.disk.very_simple.size.F90
index 831d0afa7c..4c07996ea4 100644
--- a/source/objects.nodes.components.disk.very_simple.size.F90
+++ b/source/objects.nodes.components.disk.very_simple.size.F90
@@ -65,8 +65,7 @@ module Node_Component_Disk_Very_Simple_Size
-
-
+
objects.nodes.components.disk.very_simple.size.bound_functions.inc
@@ -121,7 +120,8 @@ subroutine Node_Component_Disk_Very_Simple_Size_Thread_Initialize(parameters)
use :: Galacticus_Nodes , only : defaultDiskComponent
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Mass_Distributions , only : massDistributionCylindrical
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : diskMassDistribution
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_ , massDistributionGas_
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeStellar , massTypeGaseous
implicit none
type(inputParameters), intent(inout) :: parameters
type(inputParameters) :: subParameters
@@ -130,15 +130,33 @@ subroutine Node_Component_Disk_Very_Simple_Size_Thread_Initialize(parameters)
! Find our parameters.
subParameters=parameters%subParameters('componentDisk')
!![
-
+
-
+
-
+
!!]
- if (.not.diskMassDistribution%isDimensionless()) call Error_Report('disk mass distribution must be dimensionless'//{introspection:location})
+ ! Validate the disk mass distribution.
+ select type (massDistributionStellar_)
+ class is (massDistributionCylindrical)
+ ! The disk mass distribution must have cylindrical symmetry. So, this is acceptable.
+ class default
+ call Error_Report('only cylindrically symmetric mass distributions are allowed'//{introspection:location})
+ end select
+ if (.not.massDistributionStellar_%isDimensionless()) call Error_Report('disk mass distribution must be dimensionless'//{introspection:location})
+ ! Duplicate the dimensionless mass distribution to use for the gas component, and set component and mass types in both.
+ !$omp critical(diskVerySimpleSizeDeepCopy)
+ allocate(massDistributionGas_,mold=massDistributionStellar_)
+ !![
+
+
+
+ !!]
+ !$omp end critical(diskVerySimpleSizeDeepCopy)
+ call massDistributionStellar_%setTypes(componentTypeDisk,massTypeStellar)
+ call massDistributionGas_ %setTypes(componentTypeDisk,massTypeGaseous)
end if
return
end subroutine Node_Component_Disk_Very_Simple_Size_Thread_Initialize
@@ -153,12 +171,13 @@ subroutine Node_Component_Disk_Very_Simple_Size_Thread_Uninitialize()
Uninitializes the tree node standard merging statistics module.
!!}
use :: Galacticus_Nodes , only : defaultDiskComponent
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : diskMassDistribution
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_, massDistributionGas_
implicit none
if (defaultDiskComponent%verySimpleSizeIsActive()) then
!![
-
+
+
!!]
end if
return
@@ -322,9 +341,9 @@ subroutine Node_Component_Disk_Very_Simple_Size_State_Store(stateFile,gslStateFi
!!{
Write the tabulation state to file.
!!}
- use :: Display , only : displayMessage , verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : diskMassDistribution
+ use :: Display , only : displayMessage , verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_, massDistributionGas_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -332,7 +351,7 @@ subroutine Node_Component_Disk_Very_Simple_Size_State_Store(stateFile,gslStateFi
call displayMessage('Storing state for: componentDisk -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Disk_Very_Simple_Size_State_Store
@@ -346,9 +365,9 @@ subroutine Node_Component_Disk_Very_Simple_Size_State_Retrieve(stateFile,gslStat
!!{
Retrieve the tabulation state from the file.
!!}
- use :: Display , only : displayMessage , verbosityLevelInfo
- use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : diskMassDistribution
+ use :: Display , only : displayMessage , verbosityLevelInfo
+ use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_, massDistributionGas_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -356,7 +375,7 @@ subroutine Node_Component_Disk_Very_Simple_Size_State_Retrieve(stateFile,gslStat
call displayMessage('Retrieving state for: componentDisk -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Disk_Very_Simple_Size_State_Retrieve
diff --git a/source/objects.nodes.components.disk.very_simple.size.bound_functions.Inc b/source/objects.nodes.components.disk.very_simple.size.bound_functions.Inc
index a3a79cabff..91d642e94d 100644
--- a/source/objects.nodes.components.disk.very_simple.size.bound_functions.Inc
+++ b/source/objects.nodes.components.disk.very_simple.size.bound_functions.Inc
@@ -21,154 +21,120 @@
Contains custom functions for the very simple size disk component.
!!}
-double precision function Node_Component_Disk_Very_Simple_Size_Half_Mass_Radius(self)
+function Node_Component_Disk_Very_Simple_Size_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the half-mass radius of the very simple size disk.
+ Return the mass distribution for the very simple size disk component.
!!}
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionCylindricalScaler, massDistributionComposite , massDistributionList , &
+ & massDistributionCylindrical, massDistributionMatches_
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_ , massDistributionGas_
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeStellar , massTypeGaseous , enumerationWeightByType, &
+ & weightByMass , weightByLuminosity , enumerationComponentTypeType, enumerationMassTypeType
implicit none
- class (nodeComponentDiskVerySimpleSize), intent(inout) :: self
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponentDiskVerySimpleSize ), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ type (massDistributionCylindricalScaler), pointer :: massDistributionStellar , massDistributionGas
+ type (massDistributionComposite ), pointer :: massDistributionTotal
+ type (massDistributionList ), pointer :: massDistributionComponents
+ type (stellarLuminosities ), save :: luminosities
+ !$omp threadprivate(luminosities)
+ double precision :: massStellar , massGas , &
+ & radiusScale
+ logical :: includeGas , includeStars
+ !![
+
+ !!]
- Node_Component_Disk_Very_Simple_Size_Half_Mass_Radius=self%radius()
- return
-end function Node_Component_Disk_Very_Simple_Size_Half_Mass_Radius
-
-double precision function Node_Component_Disk_Very_Simple_Size_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for an standard disk.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk, massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , radiusLarge , &
- & weightByLuminosity , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : diskMassDistribution
- implicit none
- class (nodeComponentDiskVerySimpleSize), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- double precision :: radiusDisk , fractionalRadius
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
-
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Get the total mass.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass =luminositiesDisk%luminosity(weightIndex)
- end select
- end select
- ! Return if no mass.
- if (Node_Component_Disk_Very_Simple_Size_Enclosed_Mass <= 0.0d0) return
- ! Return if the total mass was requested.
- if (radius >= radiusLarge) return
- ! Compute the actual mass.
- radiusDisk=self%radius()
- if (radiusDisk > 0.0d0) then
- fractionalRadius=radius/radiusDisk
- Node_Component_Disk_Very_Simple_Size_Enclosed_Mass= &
- & +Node_Component_Disk_Very_Simple_Size_Enclosed_Mass &
- & *diskMassDistribution%massEnclosedBySphere(fractionalRadius)
+ ! Determine which components of the disk to include.
+ includeGas =massDistributionMatches_(componentTypeDisk,massTypeGaseous,componentType,massType) .and. weightBy_ == weightByMass
+ includeStars=massDistributionMatches_(componentTypeDisk,massTypeStellar,componentType,massType) .and. (weightBy_ == weightByMass .or. weightBy_ == weightByLuminosity)
+ ! Get properties of the mass distribution and ensure they are physical.
+ if (weightBy_ == weightByMass ) then
+ massStellar = max (0.0d0,self %massStellar ( ))
+ massGas = max (0.0d0,self %massGas ( ))
+ else if (weightBy_ == weightByLuminosity) then
+ luminosities = self %luminositiesStellar( )
+ massStellar = max (0.0d0,luminosities%luminosity (weightIndex))
+ massGas = 0.0d0
+ else
+ massDistribution_ => null()
+ return
+ end if
+ ! Determine which components to build.
+ radiusScale=self%radius()
+ if (radiusScale <= 0.0d0 .or. .not.(includeGas .or. includeStars)) then
+ ! Disk has non-positive size, or no components matched. Return a null distribution.
+ massDistribution_ => null()
+ else
+ ! Build the individual distributions.
+ massDistributionStellar => null()
+ massDistributionGas => null()
+ if (includeStars) then
+ allocate(massDistributionStellar)
+ select type (massDistributionStellar_)
+ class is (massDistributionCylindrical)
+ !![
+
+ !!]
+ end select
+ end if
+ if (includeGas ) then
+ allocate(massDistributionGas )
+ select type (massDistributionGas_ )
+ class is (massDistributionCylindrical)
+ !![
+
+ !!]
+ end select
+ end if
+ ! Combine the distributions as necessary.
+ if (includeStars .and. includeGas) then
+ ! Wrap the dimensionless mass distribution inside scaler classes to allow us to re-scale it to any disk system, and then composite those.
+ allocate(massDistributionTotal )
+ allocate(massDistributionComponents )
+ allocate(massDistributionComponents%next)
+ massDistributionComponents %massDistribution_ => massDistributionStellar
+ massDistributionComponents%next%massDistribution_ => massDistributionGas
+ !![
+
+
+
+ !!]
+ nullify(massDistributionComponents)
+ ! Return a pointer to the disk mass distribution.
+ massDistribution_ => massDistributionTotal
+ else if (includeStars ) then
+ ! Return just the stellar component.
+ massDistribution_ => massDistributionStellar_
+ else if ( includeGas) then
+ ! Return just the gas component.
+ massDistribution_ => massDistributionGas_
+ end if
end if
return
-end function Node_Component_Disk_Very_Simple_Size_Enclosed_Mass
+end function Node_Component_Disk_Very_Simple_Size_Mass_Distribution
-double precision function Node_Component_Disk_Very_Simple_Size_Surface_Density(self,positionCylindrical,componentType,massType,weightBy,weightIndex)
+double precision function Node_Component_Disk_Very_Simple_Size_Half_Mass_Radius(self) result(radiusHalfMass)
!!{
- Computes the surface density at a given position for an standard disk.
+ Return the half-mass radius of the very simple size disk.
!!}
- use :: Coordinates , only : coordinateCylindrical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeDisk , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType , enumerationWeightByType
+ use :: Error , only : Error_Report
use :: Mass_Distributions , only : massDistributionCylindrical
- use :: Node_Component_Disk_Very_Simple_Size_Data, only : Node_Component_Disk_Very_Simple_Size_Reset, diskMassDistribution , lastUniqueID , radiusScaleDisk , &
- & radiusScaleDiskComputed , surfaceDensityCentralGas , surfaceDensityCentralGasComputed , surfaceDensityCentralStellar, &
- & surfaceDensityCentralStellarComputed , surfaceDensityCentralTotal , surfaceDensityCentralTotalComputed
- use :: Numerical_Constants_Math , only : Pi
+ use :: Node_Component_Disk_Very_Simple_Size_Data, only : massDistributionStellar_
implicit none
- class (nodeComponentDiskVerySimpleSize), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionCylindrical(3)
- type (treeNode ), pointer :: selfNode
- type (stellarLuminosities ), save :: luminositiesDisk
- !$omp threadprivate(luminositiesDisk)
- type (coordinateCylindrical ) :: position
+ class(nodeComponentDiskVerySimpleSize), intent(inout) :: self
- ! Return immediately if disk component is not requested.
- Node_Component_Disk_Very_Simple_Size_Surface_Density=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeDisk)) return
- ! Check whether this is a new node.
- selfNode => self%host()
- if (selfNode%uniqueID() /= lastUniqueID) call Node_Component_Disk_Very_Simple_Size_Reset(selfNode%uniqueID())
- ! Determine disk radius.
- if (.not.radiusScaleDiskComputed) then
- radiusScaleDisk =self%radius()
- radiusScaleDiskComputed=.true.
- end if
- ! Return zero if the disk has unphysical size.
- if (radiusScaleDisk <= 0.0d0) then
- Node_Component_Disk_Very_Simple_Size_Surface_Density=0.0d0
- return
- end if
- ! Determine mass type.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- if (.not.surfaceDensityCentralTotalComputed ) then
- surfaceDensityCentralTotal =(self%massGas()+self%massStellar())/radiusScaleDisk**2
- surfaceDensityCentralTotalComputed =.true.
- end if
- Node_Component_Disk_Very_Simple_Size_Surface_Density=surfaceDensityCentralTotal
- case (massTypeGaseous%ID)
- if (.not.surfaceDensityCentralGasComputed ) then
- surfaceDensityCentralGas = self%massGas() /radiusScaleDisk**2
- surfaceDensityCentralGasComputed =.true.
- end if
- Node_Component_Disk_Very_Simple_Size_Surface_Density=surfaceDensityCentralGas
- case (massTypeStellar%ID)
- if (.not.surfaceDensityCentralStellarComputed) then
- surfaceDensityCentralStellar = self%massStellar() /radiusScaleDisk**2
- surfaceDensityCentralStellarComputed=.true.
- end if
- Node_Component_Disk_Very_Simple_Size_Surface_Density=surfaceDensityCentralStellar
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesDisk=self%luminositiesStellar()
- Node_Component_Disk_Very_Simple_Size_Surface_Density=luminositiesDisk%luminosity(weightIndex)/2.0d0/Pi/radiusScaleDisk**2
- end select
- end select
- ! Return if no density.
- if (Node_Component_Disk_Very_Simple_Size_Surface_Density <= 0.0d0) return
- ! Check that the mass distribution is cylindrical.
- select type (diskMassDistribution)
+ select type (massDistributionStellar_)
class is (massDistributionCylindrical)
- ! Compute the surface density.
- call position%rSet(positionCylindrical(1)/self%radius())
- Node_Component_Disk_Very_Simple_Size_Surface_Density= &
- & +Node_Component_Disk_Very_Simple_Size_Surface_Density &
- & *diskMassDistribution%surfaceDensity(position)
+ radiusHalfMass=+massDistributionStellar_%radiusHalfMass() &
+ & *self %radius ()
+ class default
+ radiusHalfMass=0.0d0
+ call Error_Report('disk mass distribution is not cylindrically-symmetric'//{introspection:location})
end select
return
-end function Node_Component_Disk_Very_Simple_Size_Surface_Density
+end function Node_Component_Disk_Very_Simple_Size_Half_Mass_Radius
diff --git a/source/objects.nodes.components.disk.very_simple.size.data.F90 b/source/objects.nodes.components.disk.very_simple.size.data.F90
index ec1669b9f9..842eef04f4 100644
--- a/source/objects.nodes.components.disk.very_simple.size.data.F90
+++ b/source/objects.nodes.components.disk.very_simple.size.data.F90
@@ -31,7 +31,7 @@ module Node_Component_Disk_Very_Simple_Size_Data
public
! Record of unique ID of node which we last computed results for.
- integer (kind=kind_int8 ) :: lastUniqueID =-1
+ integer (kind=kind_int8 ) :: lastUniqueID =-1
!$omp threadprivate(lastUniqueID)
! Records of previously computed and stored quantities.
logical :: surfaceDensityCentralGasComputed =.false., surfaceDensityCentralStellarComputed=.false., &
@@ -45,9 +45,9 @@ module Node_Component_Disk_Very_Simple_Size_Data
double precision :: radiusScaleDisk
!$omp threadprivate(radiusScaleDisk)
- ! The mass distribution object.
- class (massDistributionClass), pointer :: diskMassDistribution
- !$omp threadprivate(diskMassDistribution)
+ ! The mass distribution objects.
+ class (massDistributionClass), pointer :: massDistributionStellar_ , massDistributionGas_
+ !$omp threadprivate(massDistributionStellar_,massDistributionGas_)
contains
diff --git a/source/objects.nodes.components.hot_halo.cold_mode.F90 b/source/objects.nodes.components.hot_halo.cold_mode.F90
index 747eb81aee..e0015513f3 100644
--- a/source/objects.nodes.components.hot_halo.cold_mode.F90
+++ b/source/objects.nodes.components.hot_halo.cold_mode.F90
@@ -27,11 +27,12 @@ module Node_Component_Hot_Halo_Cold_Mode
Implements an extension to the standard hot halo node component which supports a cold mode
reservoir.
!!}
- use :: Accretion_Halos , only : accretionHaloClass
- use :: Cooling_Cold_Mode_Infall_Rates , only : coldModeInfallRateClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Hot_Halo_Outflows_Reincorporations, only : hotHaloOutflowReincorporationClass
+ use :: Accretion_Halos , only : accretionHaloClass
+ use :: Cooling_Cold_Mode_Infall_Rates , only : coldModeInfallRateClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Hot_Halo_Outflows_Reincorporations , only : hotHaloOutflowReincorporationClass
+ use :: Hot_Halo_Cold_Mode_Mass_Distributions, only : hotHaloColdModeMassDistributionClass
implicit none
private
public :: Node_Component_Hot_Halo_Cold_Mode_Initialize , Node_Component_Hot_Halo_Cold_Mode_Rate_Compute , &
@@ -79,17 +80,34 @@ module Node_Component_Hot_Halo_Cold_Mode
Node_Component_Hot_Halo_Cold_Mode_Mass_Total
+
+
+
+ class(massDistributionClass), pointer
+ 0
+ Galactic_Structure_Options, only : enumerationWeightByType, enumerationComponentTypeType, enumerationMassTypeType
+ Mass_Distributions , only : massDistributionClass
+
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+
+
+
objects.nodes.components.hot_halo.cold_mode.bound_functions.inc
!!]
! Objects used by this component.
- class(accretionHaloClass ), pointer :: accretionHalo_
- class(coldModeInfallRateClass ), pointer :: coldModeInfallRate_
- class(cosmologyParametersClass ), pointer :: cosmologyParameters_
- class(galacticStructureClass ), pointer :: galacticStructure_
- class(hotHaloOutflowReincorporationClass), pointer :: hotHaloOutflowReincorporation_
- !$omp threadprivate(accretionHalo_,coldModeInfallRate_,cosmologyParameters_,galacticStructure_,hotHaloOutflowReincorporation_)
+ class(accretionHaloClass ), pointer :: accretionHalo_
+ class(coldModeInfallRateClass ), pointer :: coldModeInfallRate_
+ class(cosmologyParametersClass ), pointer :: cosmologyParameters_
+ class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class(hotHaloOutflowReincorporationClass ), pointer :: hotHaloOutflowReincorporation_
+ class(hotHaloColdModeMassDistributionClass), pointer :: hotHaloColdModeMassDistribution_
+ !$omp threadprivate(accretionHalo_,coldModeInfallRate_,cosmologyParameters_,darkMatterHaloScale_,hotHaloOutflowReincorporation_,hotHaloColdModeMassDistribution_)
! Options controlling the behavior of the cold mode gas.
logical :: outflowToColdMode
@@ -101,6 +119,9 @@ module Node_Component_Hot_Halo_Cold_Mode
integer :: thread
!$omp threadprivate(thread)
+ ! Procedure pointer to mass distribution function.
+ procedure(Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution), pointer :: Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution_
+
contains
!![
@@ -139,6 +160,9 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Initialize(parameters)
! Bind the outflow return function if outflow returns to the cold mode. (If it does not, do
! not bind any function and let the parent class handle this behavior.)
if (outflowToColdMode) call hotHalo%outflowReturnFunction(Node_Component_Hot_Halo_Cold_Mode_Outflow_Return)
+ ! Bind the mass distribution function.
+ Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution_ => Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution
+ call hotHalo%massDistributionFunction(Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution_)
end if
!$omp end critical (Node_Component_Hot_Halo_Cold_Mode_Initialize)
return
@@ -153,12 +177,11 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Thread_Initialize(parameters)
!!{
Initializes the tree node hot halo cold mode methods module.
!!}
- use :: Events_Hooks , only : nodePromotionEvent , satelliteMergerEvent , openMPThreadBindingAtLevel, dependencyRegEx, &
- & dependencyDirectionAfter , haloFormationEvent
- use :: Galacticus_Nodes , only : defaultHotHaloComponent
- use :: Hot_Halo_Cold_Mode_Density_Core_Radii , only : hotHaloColdModeCoreRadii
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks, only : darkMatterHaloScale_ , hotHaloColdModeCoreRadii_
+ use :: Events_Hooks , only : nodePromotionEvent , satelliteMergerEvent, openMPThreadBindingAtLevel, dependencyRegEx, &
+ & dependencyDirectionAfter , haloFormationEvent
+ use :: Galacticus_Nodes , only : defaultHotHaloComponent
+ use :: Hot_Halo_Cold_Mode_Density_Core_Radii, only : hotHaloColdModeCoreRadii
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type(inputParameters), intent(inout) :: parameters
type(dependencyRegEx), dimension(1) :: dependencies
@@ -168,13 +191,12 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Thread_Initialize(parameters)
! Find our parameters.
subParameters=parameters%subParameters('componentHotHalo')
!![
-
-
-
-
-
-
-
+
+
+
+
+
+
!!]
dependencies(1)=dependencyRegEx(dependencyDirectionAfter,'^remnantStructure:')
call nodePromotionEvent %attach(thread,nodePromotion ,openMPThreadBindingAtLevel,label='nodeComponentHotHaloColdMode' )
@@ -193,20 +215,18 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Thread_Uninitialize()
!!{
Uninitializes the tree node hot halo cold mode methods module.
!!}
- use :: Events_Hooks , only : nodePromotionEvent , satelliteMergerEvent , haloFormationEvent
- use :: Galacticus_Nodes , only : defaultHotHaloComponent
- use :: Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks, only : darkMatterHaloScale_ , hotHaloColdModeCoreRadii_
+ use :: Events_Hooks , only : nodePromotionEvent , satelliteMergerEvent , haloFormationEvent
+ use :: Galacticus_Nodes, only : defaultHotHaloComponent
implicit none
if (defaultHotHaloComponent%coldModeIsActive()) then
!![
-
-
-
-
-
-
-
+
+
+
+
+
+
!!]
if (nodePromotionEvent %isAttached(thread,nodePromotion )) call nodePromotionEvent %detach(thread,nodePromotion )
if (satelliteMergerEvent%isAttached(thread,satelliteMerger)) call satelliteMergerEvent%detach(thread,satelliteMerger)
@@ -284,27 +304,30 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Rate_Compute(node,interrupt,interru
!!{
Compute the hot halo node mass rate of change.
!!}
- use :: Abundances_Structure , only : abs
- use :: Accretion_Halos , only : accretionModeCold
- use :: Galactic_Structure_Options , only : componentTypeColdHalo , coordinateSystemSpherical , massTypeGaseous
- use :: Galacticus_Nodes , only : defaultHotHaloComponent , interruptTask , nodeComponentBasic, nodeComponentHotHalo, &
- & nodeComponentHotHaloColdMode , propertyInactive , treeNode , nodeComponentSpin
- use :: Node_Component_Hot_Halo_Standard_Data , only : angularMomentumAlwaysGrows, outerRadiusOverVirialRadiusMinimum
- use :: Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks, only : darkMatterHaloScale_
- use :: Numerical_Constants_Math , only : Pi
+ use :: Abundances_Structure , only : abs
+ use :: Accretion_Halos , only : accretionModeCold
+ use :: Galactic_Structure_Options , only : componentTypeColdHalo , massTypeGaseous
+ use :: Galacticus_Nodes , only : defaultHotHaloComponent , interruptTask , nodeComponentBasic, nodeComponentHotHalo, &
+ & nodeComponentHotHaloColdMode, propertyInactive , treeNode , nodeComponentSpin
+ use :: Node_Component_Hot_Halo_Standard_Data, only : angularMomentumAlwaysGrows , outerRadiusOverVirialRadiusMinimum
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- type (treeNode ), intent(inout) :: node
- logical , intent(inout) :: interrupt
- procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
- integer , intent(in ) :: propertyType
- class (nodeComponentSpin ) , pointer :: spin
- class (nodeComponentHotHalo) , pointer :: hotHalo
- class (nodeComponentBasic ) , pointer :: basic
- double precision :: angularMomentumAccretionRate, densityAtOuterRadius , &
- & massAccretionRate , massLossRate , &
- & outerRadius , outerRadiusGrowthRate, &
- & gasMass , infallRate
-
+ type (treeNode ), intent(inout) :: node
+ logical , intent(inout) :: interrupt
+ procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
+ integer , intent(in ) :: propertyType
+ class (nodeComponentSpin ) , pointer :: spin
+ class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass) , pointer :: massDistribution_
+ double precision :: angularMomentumAccretionRate, densityAtOuterRadius , &
+ & massAccretionRate , massLossRate , &
+ & outerRadius , outerRadiusGrowthRate, &
+ & gasMass , infallRate
+ type (coordinateSpherical ) :: coordinates
+
! Return immediately if inactive variables are requested.
if (propertyInactive(propertyType)) return
! Return immediately if this class is not in use.
@@ -351,7 +374,12 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Rate_Compute(node,interrupt,interru
& outerRadius > outerRadiusOverVirialRadiusMinimum*darkMatterHaloScale_%radiusVirial(node) &
& ) then
! The ram pressure stripping radius is within the outer radius. Remove mass from the cold mode halo at the appropriate rate.
- densityAtOuterRadius=galacticStructure_%density(node,[outerRadius,0.0d0,0.0d0],coordinateSystemSpherical,componentTypeColdHalo,massTypeGaseous)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentType=componentTypeColdHalo,massType=massTypeGaseous)
+ densityAtOuterRadius = massDistribution_%density ( coordinates )
+ !![
+
+ !!]
! Compute the mass loss rate.
massLossRate=4.0d0*Pi*densityAtOuterRadius*outerRadius**2*outerRadiusGrowthRate
! Adjust the rates.
@@ -375,28 +403,31 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Outflow_Return(self,interrupt,inter
!!{
Return outflowed gas to the cold mode reservoir.
!!}
- use :: Abundances_Structure , only : abundances , max , operator(*)
- use :: Galactic_Structure_Options , only : componentTypeColdHalo, coordinateSystemSpherical, massTypeGaseous
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : interruptTask , nodeComponentBasic , nodeComponentHotHaloColdMode, nodeComponentHotHaloStandard, &
- & treeNode
- use :: Node_Component_Hot_Halo_Standard_Data , only : starveSatellites
- use :: Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks, only : darkMatterHaloScale_
- use :: Numerical_Constants_Astronomical , only : gigaYear , megaParsec
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Abundances_Structure , only : abundances , max , operator(*)
+ use :: Galactic_Structure_Options , only : componentTypeColdHalo, massTypeGaseous
+ use :: Error , only : Error_Report
+ use :: Galacticus_Nodes , only : interruptTask , nodeComponentBasic, nodeComponentHotHaloColdMode, nodeComponentHotHaloStandard, &
+ & treeNode
+ use :: Node_Component_Hot_Halo_Standard_Data, only : starveSatellites
+ use :: Numerical_Constants_Astronomical , only : gigaYear , megaParsec
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (nodeComponentHotHaloStandard), intent(inout) :: self
logical , intent(inout) :: interrupt
procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
type (treeNode ), pointer :: node
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: outflowedMass , massReturnRate, &
& angularMomentumReturnRate, radiusVirial , &
& densityAtOuterRadius , densityMinimum, &
& outerRadius
type (abundances ), save :: abundancesReturnRate
!$omp threadprivate(abundancesReturnRate)
+ type (coordinateSpherical ) :: coordinates
select type (self)
class is (nodeComponentHotHaloColdMode)
@@ -422,7 +453,12 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Outflow_Return(self,interrupt,inter
outerRadius =self%outerRadius()
radiusVirial=darkMatterHaloScale_%radiusVirial(node)
if (outerRadius < radiusVirial) then
- densityAtOuterRadius=galacticStructure_%density(node,[outerRadius,0.0d0,0.0d0],coordinateSystemSpherical,componentTypeColdHalo,massTypeGaseous)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentType=componentTypeColdHalo,massType=massTypeGaseous)
+ densityAtOuterRadius = massDistribution_%density ( coordinates )
+ !![
+
+ !!]
! If the outer radius and density are non-zero we can expand the outer radius at a rate determined by the current
! density profile.
if (outerRadius > 0.0d0 .and. densityAtOuterRadius > 0.0d0) then
@@ -462,10 +498,9 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Scale_Set(node)
!!{
Set scales for properties of {\normalfont \ttfamily node}.
!!}
- use :: Abundances_Structure , only : unitAbundances
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, nodeComponentHotHaloColdMode, treeNode, &
- & defaultHotHaloComponent
- use :: Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks, only : darkMatterHaloScale_
+ use :: Abundances_Structure, only : unitAbundances
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo, nodeComponentHotHaloColdMode, treeNode, &
+ & defaultHotHaloComponent
implicit none
type (treeNode ), intent(inout), pointer :: node
class (nodeComponentHotHalo) , pointer :: hotHalo
@@ -564,25 +599,27 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Node_Merger(node)
!!{
Starve {\normalfont \ttfamily node} by transferring its hot halo to its parent.
!!}
- use :: Abundances_Structure , only : abundances , operator(*) , zeroAbundances
- use :: Accretion_Halos , only : accretionModeCold , accretionModeTotal
- use :: Galactic_Structure_Options , only : componentTypeAll , massTypeBaryonic , radiusLarge
- use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , nodeComponentHotHaloColdMode, nodeComponentSpin, &
- & treeNode , defaultHotHaloComponent
+ use :: Abundances_Structure , only : abundances , operator(*) , zeroAbundances
+ use :: Accretion_Halos , only : accretionModeCold , accretionModeTotal
+ use :: Galactic_Structure_Options , only : componentTypeAll , massTypeBaryonic
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , nodeComponentHotHaloColdMode, nodeComponentSpin, &
+ & treeNode , defaultHotHaloComponent
use :: Node_Component_Hot_Halo_Standard_Data, only : fractionBaryonLimitInNodeMerger, starveSatellites
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- type (treeNode ), intent(inout) :: node
- type (treeNode ), pointer :: nodeParent
- class (nodeComponentHotHalo), pointer :: hotHaloParent , hotHalo
- class (nodeComponentSpin ), pointer :: spinParent
- class (nodeComponentBasic ), pointer :: basicParent , basic
- double precision :: baryonicMassCurrent , baryonicMassMaximum , &
- & fractionRemove , massAccretedCold , &
- & massAccreted , massUnaccreted , &
- & angularMomentumAccreted, massReaccreted , &
- & fractionAccreted
- type (abundances ), save :: massMetalsAccreted , fractionMetalsAccreted, &
- & massMetalsReaccreted
+ type (treeNode ), intent(inout) :: node
+ type (treeNode ), pointer :: nodeParent
+ class (nodeComponentHotHalo ), pointer :: hotHaloParent , hotHalo
+ class (nodeComponentSpin ), pointer :: spinParent
+ class (nodeComponentBasic ), pointer :: basicParent , basic
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: baryonicMassCurrent , baryonicMassMaximum , &
+ & fractionRemove , massAccretedCold , &
+ & massAccreted , massUnaccreted , &
+ & angularMomentumAccreted, massReaccreted , &
+ & fractionAccreted
+ type (abundances ), save :: massMetalsAccreted , fractionMetalsAccreted, &
+ & massMetalsReaccreted
!$omp threadprivate(massMetalsAccreted,fractionMetalsAccreted,massMetalsReaccreted)
! Return immediately if this class is not in use.
@@ -669,15 +706,14 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_Node_Merger(node)
! Check if the baryon fraction in the parent hot halo exceeds the universal value. If it does, mitigate this by moving
! some of the mass to the failed accretion reservoir.
if (fractionBaryonLimitInNodeMerger) then
- baryonicMassMaximum=+basicParent %mass () &
- & *cosmologyParameters_%omegaBaryon () &
- & /cosmologyParameters_%omegaMatter ()
- baryonicMassCurrent= galacticStructure_ %massEnclosed( &
- & nodeParent , &
- & radiusLarge , &
- & massType =massTypeBaryonic, &
- & componentType=componentTypeAll &
- & )
+ massDistribution_ => nodeParent %massDistribution(massType=massTypeBaryonic)
+ baryonicMassMaximum = +basicParent %mass ( ) &
+ & *cosmologyParameters_%omegaBaryon ( ) &
+ & /cosmologyParameters_%omegaMatter ( )
+ baryonicMassCurrent = +massDistribution_ %massTotal ( )
+ !![
+
+ !!]
if (baryonicMassCurrent > baryonicMassMaximum .and. hotHaloParent%mass()+hotHaloParent%massCold() > 0.0d0) then
fractionRemove=min((baryonicMassCurrent-baryonicMassMaximum)/hotHaloParent%massTotal(),1.0d0)
call hotHaloParent% unaccretedMassSet( &
@@ -852,7 +888,7 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_State_Store(stateFile,gslStateFile,
call displayMessage('Storing state for: componentHotHalo -> coldMode',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Hot_Halo_Cold_Mode_State_Store
@@ -875,9 +911,77 @@ subroutine Node_Component_Hot_Halo_Cold_Mode_State_Restore(stateFile,gslStateFil
call displayMessage('Retrieving state for: componentHotHalo -> coldMode',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Hot_Halo_Cold_Mode_State_Restore
+ function Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the mass distribution associated with the hot halo.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentHotHaloStandard, nodeComponentHotHaloColdMode
+ use :: Galactic_Structure_Options, only : enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType , componentTypeColdHalo, &
+ & massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionLocal , massDistributionComposite, massDistributionList , &
+ & massDistributionMatches_
+ implicit none
+ class (massDistributionClass ), pointer :: massDistributionHotMode , massDistributionColdMode, &
+ & massDistribution_
+ type (kinematicsDistributionLocal ), pointer :: kinematicsDistribution_
+ type (massDistributionComposite ), pointer :: massDistributionTotal
+ type (massDistributionList ), pointer :: massDistributionComponents
+ class (nodeComponentHotHaloStandard), intent(inout) :: self
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+ select type (self)
+ class is (nodeComponentHotHaloColdMode)
+ if (massDistributionMatches_(componentTypeColdHalo,massTypeGaseous,componentType,massType)) then
+ massDistributionColdMode => hotHaloColdModeMassDistribution_ %get (self%hostNode ,weightBy,weightIndex)
+ else
+ massDistributionColdMode => null()
+ end if
+ massDistributionHotMode => self %nodeComponentHotHaloStandard%massDistribution(componentType,massType,weightBy,weightIndex)
+ if (associated(massDistributionColdMode)) then
+ allocate(kinematicsDistribution_)
+ !![
+
+ !!]
+ call massDistributionColdMode%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ end if
+ if (.not.associated(massDistributionColdMode)) then
+ if (.not.associated(massDistributionHotMode)) then
+ massDistribution_ => null()
+ else
+ massDistribution_ => massDistributionHotMode
+ end if
+ else
+ if (.not.associated(massDistributionHotMode)) then
+ massDistribution_ => massDistributionColdMode
+ else
+ allocate(massDistributionTotal )
+ allocate(massDistributionComponents )
+ allocate(massDistributionComponents%next)
+ massDistributionComponents %massDistribution_ => massDistributionHotMode
+ massDistributionComponents%next%massDistribution_ => massDistributionColdMode
+ !![
+
+
+
+ !!]
+ nullify(massDistributionComponents)
+ end if
+ end if
+ class default
+ call Error_Report('unexpected class'//{introspection:location})
+ end select
+ return
+ end function Node_Component_Hot_Halo_Cold_Mode_Mass_Distribution
+
end module Node_Component_Hot_Halo_Cold_Mode
diff --git a/source/objects.nodes.components.hot_halo.cold_mode.bound_functions.Inc b/source/objects.nodes.components.hot_halo.cold_mode.bound_functions.Inc
index 75242299f7..5fd0c3d596 100644
--- a/source/objects.nodes.components.hot_halo.cold_mode.bound_functions.Inc
+++ b/source/objects.nodes.components.hot_halo.cold_mode.bound_functions.Inc
@@ -32,3 +32,16 @@ double precision function Node_Component_Hot_Halo_Cold_Mode_Mass_Total(self)
return
end function Node_Component_Hot_Halo_Cold_Mode_Mass_Total
+double precision function Node_Component_Hot_Halo_Cole_Mode_Mass_Baryonic(self) result(massBaryonic)
+ !!{
+ Return the baryonic mass for the very simple disk component.
+ !!}
+ implicit none
+ class(nodeComponentHotHaloColdMode), intent(inout) :: self
+
+ massBaryonic=+max(0.0d0,self%mass ()) &
+ & +max(0.0d0,self%massCold ()) &
+ & +max(0.0d0,self%outflowedMass())
+ return
+end function Node_Component_Hot_Halo_Cole_Mode_Mass_Baryonic
+
diff --git a/source/objects.nodes.components.hot_halo.cold_mode.structure_tasks.F90 b/source/objects.nodes.components.hot_halo.cold_mode.structure_tasks.F90
deleted file mode 100644
index 4aef8cedd2..0000000000
--- a/source/objects.nodes.components.hot_halo.cold_mode.structure_tasks.F90
+++ /dev/null
@@ -1,376 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 structure tasks for the cold mode hot halo component.
-!!}
-
-module Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks
- !!{
- Implements structure tasks for the cold mode hot halo component.
- !!}
- use :: Hot_Halo_Cold_Mode_Density_Core_Radii, only : hotHaloColdModeCoreRadiiClass
- use :: Mass_Distributions , only : massDistributionBetaProfile
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- implicit none
- private
- public :: Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task , Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task , &
- & Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task, Node_Component_Hot_Halo_Cold_Mode_Density_Task , &
- & Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task , Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task , &
- & Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral , Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task
-
- type (massDistributionBetaProfile ), public :: coldModeMassDistribution
- class(hotHaloColdModeCoreRadiiClass), public, pointer :: hotHaloColdModeCoreRadii_
- class(darkMatterHaloScaleClass ), public, pointer :: darkMatterHaloScale_
- !$omp threadprivate(coldModeMassDistribution,hotHaloColdModeCoreRadii_,darkMatterHaloScale_)
-
-contains
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task
-
- !!]
- double precision function Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for the cold mode hot halo component.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeColdHalo , massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , radiusLarge , weightByMass, enumerationComponentTypeType, &
- & enumerationMassTypeType, enumerationWeightByType
- use :: Galacticus_Nodes , only : defaultHotHaloComponent, nodeComponentHotHalo , treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- double precision :: radiusOuter , radiusCore
- !$GLC attributes unused :: weightIndex
-
- ! Return zero mass if the requested mass type or component is not matched.
- Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task=0.0d0
- if (.not.defaultHotHaloComponent%coldModeIsActive() ) return
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeColdHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
- ! Get the hot halo component.
- hotHalo => node %hotHalo ()
- ! Check for total mass request.
- if (radius >= radiusLarge) then
- Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task=hotHalo%massCold()
- return
- end if
- ! Get the outer radius.
- radiusOuter = hotHalo%outerRadius()
- if (radiusOuter <= 0.0d0) return
- ! Compute the enclosed mass.
- ! Find the scale length of the cold mode halo.
- radiusCore=hotHaloColdModeCoreRadii_%radius(node)
- ! Initialize the mass profile
- coldModeMassDistribution=massDistributionBetaProfile(beta=2.0d0/3.0d0,coreRadius=radiusCore,mass=hotHalo%massCold(),outerRadius=hotHalo%outerRadius())
- ! Compute the enclosed mass.
- Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task=coldModeMassDistribution%massEnclosedBySphere(radius)
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task
-
- !!]
- function Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task(node,positionCartesian,componentType,massType)
- !!{
- Computes the acceleration due to a cold-mode profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision :: radius
-
- radius =+sqrt(sum(positionCartesian**2))
- Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task(node,radius,componentType,massType,weightByMass,weightIndexNull) &
- & *positionCartesian &
- & /radius**3
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Acceleration_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task
-
- !!]
- function Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task(node,positionCartesian,componentType,massType)
- !!{
- Computes the tidalTensor due to the cold mode halo.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorIdentityR2D3Sym, assignment(=) , operator(*)
- use :: Vectors , only : Vector_Outer_Product
- implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian
- double precision , dimension(3) :: positionSpherical
- double precision :: radius , massEnclosed, &
- & density
- type (tensorRank2Dimension3Symmetric) :: positionTensor
-
- radius =sqrt(sum(positionCartesian**2))
- positionSpherical =[radius,0.0d0,0.0d0]
- massEnclosed =Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task(node,radius ,componentType,massType,weightByMass,weightIndexNull)
- density =Node_Component_Hot_Halo_Cold_Mode_Density_Task (node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- positionTensor =Vector_Outer_Product ( positionCartesian,symmetrize=.true. )
- Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task=+gravitationalConstantGalacticus &
- & *( &
- & -(massEnclosed /radius**3)*tensorIdentityR2D3Sym &
- & +(massEnclosed*3.0d0 /radius**5)*positionTensor &
- & -(density *4.0d0*Pi/radius**2)*positionTensor &
- & )
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Tidal_Tensor_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task
-
- !!]
- double precision function Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task(node,radius,componentType,massType)
- !!{
- Computes the rotation curve at a given radius for the hot halo density profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task=0.0d0
- ! Compute if a spheroid is present.
- if (radius > 0.0d0) then
- componentMass=Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task(node,radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task=sqrt(gravitationalConstantGalacticus*componentMass)/sqrt(radius)
- end if
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task
-
- !!]
- double precision function Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task(node,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient at a given radius for the hot halo density profile.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentDensity, componentMass
-
- ! Set to zero by default.
- Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task=0.0d0
- ! Compute if a spheroid is present.
- if (radius > 0.0d0) then
- componentMass=Node_Component_Hot_Halo_Cold_Mode_Enclosed_Mass_Task(node,radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) then
- componentDensity=Node_Component_Hot_Halo_Cold_Mode_Density_Task(node,[radius,0.0d0,0.0d0],componentType,massType,weightByMass,weightIndexNull)
- Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task=gravitationalConstantGalacticus*(-componentMass/radius**2+4.0d0*Pi*radius&
- &*componentDensity)
- end if
- end if
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Rotation_Curve_Gradient_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Density_Task
-
- !!]
- double precision function Node_Component_Hot_Halo_Cold_Mode_Density_Task(node,positionSpherical,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for a cold-mode profile.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeColdHalo, massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Galacticus_Nodes , only : defaultHotHaloComponent, nodeComponentHotHalo , treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionSpherical(3)
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- type (coordinateSpherical ) :: position
- double precision :: radiusOuter , radiusCore
- !$GLC attributes unused :: weightIndex
-
- Node_Component_Hot_Halo_Cold_Mode_Density_Task=0.0d0
- if (.not.defaultHotHaloComponent%coldModeIsActive() ) return
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeColdHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
- ! Get the hot halo component.
- hotHalo => node%hotHalo()
- ! Get the outer radius.
- radiusOuter = hotHalo%outerRadius()
- if (radiusOuter <= 0.0d0) return
- ! Compute the enclosed mass.
- ! Find the scale length of the cold mode halo.
- radiusCore=hotHaloColdModeCoreRadii_%radius(node)
- ! Initialize the mass profile
- coldModeMassDistribution=massDistributionBetaProfile(beta=2.0d0/3.0d0,coreRadius=radiusCore,mass=hotHalo%massCold(),outerRadius=hotHalo%outerRadius())
- ! Compute the density.
- position=[positionSpherical(1)/radiusCore,0.0d0,0.0d0]
- Node_Component_Hot_Halo_Cold_Mode_Density_Task=coldModeMassDistribution%density(position)
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Density_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task
-
- !!]
- double precision function Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task(node,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the spherically-averaged density at a given position for a cold-mode profile.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeColdHalo, massTypeAll , massTypeBaryonic , &
- & massTypeGaseous , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Galacticus_Nodes , only : defaultHotHaloComponent, nodeComponentHotHalo , treeNode
- implicit none
- type (treeNode ), intent(inout) :: node
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- class (nodeComponentHotHalo ) , pointer :: hotHalo
- type (coordinateSpherical ) :: position
- double precision :: radiusOuter , radiusCore
- !$GLC attributes unused :: weightIndex
-
- Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task=0.0d0
- if (.not.defaultHotHaloComponent%coldModeIsActive() ) return
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeColdHalo )) return
- if (.not.(massType == massTypeAll .or. massType == massTypeBaryonic .or. massType == massTypeGaseous)) return
- if (.not.(weightBy == weightByMass )) return
- ! Get the hot halo component.
- hotHalo => node%hotHalo()
- ! Get the outer radius.
- radiusOuter = hotHalo%outerRadius()
- if (radiusOuter <= 0.0d0) return
- ! Compute the enclosed mass.
- ! Find the scale length of the cold mode halo.
- radiusCore=hotHaloColdModeCoreRadii_%radius(node)
- ! Initialize the mass profile
- coldModeMassDistribution=massDistributionBetaProfile(beta=2.0d0/3.0d0,coreRadius=radiusCore,mass=hotHalo%massCold(),outerRadius=hotHalo%outerRadius())
- ! Compute the density.
- position=[radius/radiusCore,0.0d0,0.0d0]
- Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task=coldModeMassDistribution%density(position)
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Density_Sphrcl_Avrg_Task
-
- !![
-
- Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral
-
- !!]
- function Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral(node,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
- !!{
- Computes the Chandrasekhar integral due to a cold-mode profile.
- !!}
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale, darkMatterHaloScaleClass
- use :: Galactic_Structure_Options, only : weightByMass , weightIndexNull , enumerationComponentTypeType, enumerationMassTypeType
- use :: Galacticus_Nodes , only : treeNode , defaultHotHaloComponent
- use :: Numerical_Constants_Math , only : Pi
- implicit none
- double precision , dimension(3) :: Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral
- type (treeNode ), intent(inout) :: node , nodeSatellite
- type (enumerationComponentTypeType), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- double precision , dimension(3) :: positionSpherical
- double precision , parameter :: XvMaximum =10.0d0
- double precision :: radius , velocity , &
- & density , xV
- !$GLC attributes unused :: nodeSatellite
-
- Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral=0.0d0
- if (.not.defaultHotHaloComponent%coldModeIsActive() ) return
- radius = sqrt(sum(positionCartesian**2))
- velocity = sqrt(sum(velocityCartesian**2))
- if (velocity <= 0.0d0) return
- positionSpherical = [radius,0.0d0,0.0d0]
- density = Node_Component_Hot_Halo_Cold_Mode_Density_Task(node,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (density <= 0.0d0) return
- xV = + velocity &
- & /darkMatterHaloScale_%velocityVirial(node) &
- & /sqrt(2.0d0)
- Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral = -density &
- & *velocityCartesian &
- & /velocity **3
- if (Xv <= XvMaximum) &
- & Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral=+Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral &
- & *( &
- & +erf ( xV ) &
- & -2.0d0 &
- & * xV &
- & *exp (-xV**2) &
- & /sqrt( Pi ) &
- & )
- return
- end function Node_Component_Hot_Halo_Cold_Mode_Chandrasekhar_Integral
-
-end module Node_Component_Hot_Halo_Cold_Mode_Structure_Tasks
diff --git a/source/objects.nodes.components.hot_halo.standard.F90 b/source/objects.nodes.components.hot_halo.standard.F90
index 605317ec5a..76469c885c 100644
--- a/source/objects.nodes.components.hot_halo.standard.F90
+++ b/source/objects.nodes.components.hot_halo.standard.F90
@@ -34,8 +34,8 @@ module Node_Component_Hot_Halo_Standard
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
+ use :: Hot_Halo_Temperature_Profiles , only : hotHaloTemperatureProfileClass
use :: Hot_Halo_Outflows_Reincorporations , only : hotHaloOutflowReincorporationClass
use :: Hot_Halo_Ram_Pressure_Stripping , only : hotHaloRamPressureStrippingClass
use :: Hot_Halo_Ram_Pressure_Stripping_Timescales, only : hotHaloRamPressureTimescaleClass
@@ -220,7 +220,7 @@ module Node_Component_Hot_Halo_Standard
void
- logical, intent(inout) :: interrupt
+ logical , intent(inout) :: interrupt
procedure(interruptTask), intent(inout), pointer :: interruptProcedure
@@ -231,6 +231,20 @@ module Node_Component_Hot_Halo_Standard
+
+
+ class(massDistributionClass), pointer
+ 0
+ Galactic_Structure_Options, only : enumerationWeightByType, enumerationComponentTypeType, enumerationMassTypeType
+ Mass_Distributions , only : massDistributionClass
+
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+
+
+
objects.nodes.components.hot_halo.standard.bound_functions.inc
@@ -242,6 +256,7 @@ module Node_Component_Hot_Halo_Standard
class(coolingSpecificAngularMomentumClass), pointer :: coolingSpecificAngularMomentum_
class(coolingInfallRadiusClass ), pointer :: coolingInfallRadius_
class(hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
+ class(hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class(accretionHaloClass ), pointer :: accretionHalo_
class(hotHaloRamPressureStrippingClass ), pointer :: hotHaloRamPressureStripping_
class(hotHaloRamPressureTimescaleClass ), pointer :: hotHaloRamPressureTimescale_
@@ -249,36 +264,38 @@ module Node_Component_Hot_Halo_Standard
class(chemicalStateClass ), pointer :: chemicalState_
class(coolingRateClass ), pointer :: coolingRate_
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
- class(galacticStructureClass ), pointer :: galacticStructure_
- !$omp threadprivate(cosmologyFunctions_,darkMatterHaloScale_,coolingSpecificAngularMomentum_,coolingInfallRadius_,hotHaloMassDistribution_,accretionHalo_,chemicalState_,hotHaloRamPressureStripping_,hotHaloRamPressureTimescale_,coolingRate_,cosmologyParameters_,hotHaloOutflowReincorporation_,galacticStructure_)
+ !$omp threadprivate(cosmologyFunctions_,darkMatterHaloScale_,coolingSpecificAngularMomentum_,coolingInfallRadius_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,accretionHalo_,chemicalState_,hotHaloRamPressureStripping_,hotHaloRamPressureTimescale_,coolingRate_,cosmologyParameters_,hotHaloOutflowReincorporation_)
! Internal count of abundances and chemicals.
- integer :: abundancesCount , chemicalsCount
+ integer :: abundancesCount , chemicalsCount
! Configuration variables.
- logical :: hotHaloExcessHeatDrivesOutflow
- double precision :: rateMaximumExpulsion , efficiencyStrippingOutflow
+ logical :: hotHaloExcessHeatDrivesOutflow
+ double precision :: rateMaximumExpulsion , efficiencyStrippingOutflow
! Quantities stored to avoid repeated computation.
- integer (kind=kind_int8 ) :: uniqueIDPrevious
- logical :: gotAngularMomentumCoolingRate =.false., gotCoolingRate =.false., &
- & gotOuterRadiusGrowthRate =.false.
- double precision :: angularMomentumHeatingRateRemaining , rateCooling , &
- & massHeatingRateRemaining , outerRadiusGrowthRateStored
+ integer (kind=kind_int8 ) :: uniqueIDPrevious
+ logical :: gotAngularMomentumCoolingRate =.false., gotCoolingRate =.false., &
+ & gotOuterRadiusGrowthRate =.false.
+ double precision :: angularMomentumHeatingRateRemaining , rateCooling , &
+ & massHeatingRateRemaining , outerRadiusGrowthRateStored
!$omp threadprivate(gotCoolingRate,gotAngularMomentumCoolingRate,gotOuterRadiusGrowthRate,rateCooling,massHeatingRateRemaining,angularMomentumHeatingRateRemaining,outerRadiusGrowthRateStored,uniqueIDPrevious)
! Radiation structure.
- class (radiationFieldClass ), pointer :: radiation
+ class (radiationFieldClass ), pointer :: radiation
!$omp threadprivate(radiation)
! Tracked properties control.
- logical :: trackStrippedGas
+ logical :: trackStrippedGas
! Parameters controlling absolute tolerance scales.
- double precision , parameter :: scaleMassRelative =1.0d-3
- double precision , parameter :: scaleRadiusRelative =1.0d-1
+ double precision , parameter :: scaleMassRelative =1.0d-3
+ double precision , parameter :: scaleRadiusRelative =1.0d-1
+ ! Procedure pointer to mass distribution function.
+ procedure (Node_Component_Hot_Halo_Standard_Mass_Distribution), pointer :: Node_Component_Hot_Halo_Standard_Mass_Distribution_
+
! A threadprivate object used to track to which thread events are attached.
- integer :: thread
+ integer :: thread
!$omp threadprivate(thread)
contains
@@ -449,6 +466,9 @@ subroutine Node_Component_Hot_Halo_Standard_Initialize(parameters)
call hotHalo% outflowingAbundancesRateFunction(Node_Component_Hot_Halo_Standard_Outflowing_Abundances_Rate)
! Bind a creation function.
call hotHalo% createFunctionSet(Node_Component_Hot_Halo_Standard_Initializor )
+ ! Bind the mass distribution function.
+ Node_Component_Hot_Halo_Standard_Mass_Distribution_ => Node_Component_Hot_Halo_Standard_Mass_Distribution
+ call hotHalo% massDistributionFunction(Node_Component_Hot_Halo_Standard_Mass_Distribution_ )
! Bind the mass sink function.
call hotHalo% massSinkRateFunction(Node_Component_Hot_Halo_Standard_Mass_Sink )
! Bind the outflow return function.
@@ -491,13 +511,13 @@ subroutine Node_Component_Hot_Halo_Standard_Thread_Initialize(parameters)
+
-
!!]
dependencies(1)=dependencyRegEx(dependencyDirectionAfter,'^remnantStructure:')
call nodePromotionEvent %attach(thread,nodePromotion ,openMPThreadBindingAtLevel,label='nodeComponentHotHaloStandard' )
@@ -542,6 +562,7 @@ subroutine Node_Component_Hot_Halo_Standard_Thread_Uninitialize()
+
@@ -549,7 +570,6 @@ subroutine Node_Component_Hot_Halo_Standard_Thread_Uninitialize()
-
!!]
if (nodePromotionEvent %isAttached(thread,nodePromotion )) call nodePromotionEvent %detach(thread,nodePromotion )
if (satelliteMergerEvent%isAttached(thread,satelliteMerger)) call satelliteMergerEvent%detach(thread,satelliteMerger)
@@ -1025,17 +1045,24 @@ double precision function Node_Component_Hot_Halo_Standard_Outflow_Stripped_Frac
Compute the fraction of material outflowing into the hot halo of {\normalfont \ttfamily node} which is susceptible to being stripped
away.
!!}
- use :: Galacticus_Nodes, only : nodeComponentHotHaloStandard, treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options, only : componentTypeHotHalo , massTypeGaseous
+ use :: Galacticus_Nodes , only : nodeComponentHotHaloStandard, treeNode
implicit none
type (treeNode ), intent(inout), pointer :: node
+ class (massDistributionClass ) , pointer :: massDistribution_
class (nodeComponentHotHaloStandard) :: hotHalo
- double precision :: massOuter , massVirial , &
- & radiusOuter, radiusVirial
-
- radiusOuter =hotHalo %outerRadius ( )
- radiusVirial=darkMatterHaloScale_ %radiusVirial(node )
- massOuter =hotHaloMassDistribution_%enclosedMass(node,radiusOuter )
- massVirial =hotHaloMassDistribution_%enclosedMass(node,radiusVirial)
+ double precision :: massOuter , massVirial , &
+ & radiusOuter , radiusVirial
+
+ massDistribution_ => node %massDistribution (componentTypeHotHalo,massTypeGaseous)
+ radiusOuter = hotHalo %outerRadius ( )
+ radiusVirial = darkMatterHaloScale_%radiusVirial (node )
+ massOuter = massDistribution_ %massEnclosedBySphere(radiusOuter )
+ massVirial = massDistribution_ %massEnclosedBySphere(radiusVirial )
+ !![
+
+ !!]
if (massVirial > 0.0d0) then
Node_Component_Hot_Halo_Standard_Outflow_Stripped_Fraction=efficiencyStrippingOutflow*(1.0d0-massOuter/massVirial)
else
@@ -1206,24 +1233,29 @@ subroutine Node_Component_Hot_Halo_Standard_Rate_Compute(node,interrupt,interrup
!!{
Compute the hot halo node mass rate of change.
!!}
- use :: Abundances_Structure , only : abundances , abs
- use :: Accretion_Halos , only : accretionModeHot , accretionModeTotal
- use :: Galacticus_Nodes , only : defaultHotHaloComponent , interruptTask , nodeComponentBasic, nodeComponentHotHalo, &
- & nodeComponentHotHaloStandard , propertyInactive , treeNode , nodeComponentSpin
- use :: Node_Component_Hot_Halo_Standard_Data, only : outerRadiusOverVirialRadiusMinimum , angularMomentumAlwaysGrows
+ use :: Abundances_Structure , only : abundances , abs
+ use :: Accretion_Halos , only : accretionModeHot , accretionModeTotal
+ use :: Galacticus_Nodes , only : defaultHotHaloComponent , interruptTask , nodeComponentBasic, nodeComponentHotHalo, &
+ & nodeComponentHotHaloStandard , propertyInactive , treeNode , nodeComponentSpin
+ use :: Node_Component_Hot_Halo_Standard_Data, only : outerRadiusOverVirialRadiusMinimum, angularMomentumAlwaysGrows
use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
implicit none
- type (treeNode ), intent(inout) :: node
- logical , intent(inout) :: interrupt
- procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
- integer , intent(in ) :: propertyType
- class (nodeComponentSpin ) , pointer :: spin
- class (nodeComponentHotHalo) , pointer :: hotHalo
- class (nodeComponentBasic ) , pointer :: basic
- double precision :: angularMomentumAccretionRate, outerRadiusGrowthRate , &
- & densityAtOuterRadius , rateAccretionMassFailed, &
- & massLossRate , rateAccretionMass , &
- & outerRadius
+ type (treeNode ), intent(inout) :: node
+ logical , intent(inout) :: interrupt
+ procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
+ integer , intent(in ) :: propertyType
+ class (nodeComponentSpin ) , pointer :: spin
+ class (nodeComponentHotHalo ) , pointer :: hotHalo
+ class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass) , pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
+ double precision :: angularMomentumAccretionRate, outerRadiusGrowthRate , &
+ & densityAtOuterRadius , rateAccretionMassFailed, &
+ & massLossRate , rateAccretionMass , &
+ & outerRadius
! Return immediately if inactive variables are requested.
if (propertyInactive(propertyType)) return
@@ -1287,7 +1319,12 @@ subroutine Node_Component_Hot_Halo_Standard_Rate_Compute(node,interrupt,interrup
& .and. &
& outerRadius > outerRadiusOverVirialRadiusMinimum*darkMatterHaloScale_%radiusVirial(node) &
& ) then
- densityAtOuterRadius=hotHaloMassDistribution_%density(node,outerRadius)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ densityAtOuterRadius = massDistribution_%density (coordinates )
+ !![
+
+ !!]
massLossRate =4.0d0*Pi*densityAtOuterRadius*outerRadius**2*outerRadiusGrowthRate
call hotHalo%outerRadiusRate(+outerRadiusGrowthRate,interrupt,interruptProcedure)
call hotHalo% massSinkRate(+ massLossRate,interrupt,interruptProcedure)
@@ -1342,17 +1379,22 @@ subroutine Node_Component_Hot_Halo_Standard_Outflow_Return(self,interrupt,interr
!!{
Return outflowed gas to the hot halo.
!!}
- use :: Abundances_Structure , only : abundances , max
+ use :: Abundances_Structure , only : abundances , max
use :: Chemical_Abundances_Structure , only : chemicalAbundances
- use :: Galacticus_Nodes , only : interruptTask , nodeComponentBasic , nodeComponentHotHaloStandard, treeNode
- use :: Node_Component_Hot_Halo_Standard_Data, only : starveSatellites , starveSatellitesOutflowed
+ use :: Galacticus_Nodes , only : interruptTask , nodeComponentBasic , nodeComponentHotHaloStandard, treeNode
+ use :: Node_Component_Hot_Halo_Standard_Data, only : starveSatellites , starveSatellitesOutflowed
use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeHotHalo , massTypeGaseous
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
class (nodeComponentHotHaloStandard), intent(inout) :: self
logical , intent(inout) :: interrupt
procedure (interruptTask ), intent(inout), pointer :: interruptProcedure
type (treeNode ) , pointer :: node
class (nodeComponentBasic ) , pointer :: basic
+ class (massDistributionClass ) , pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
double precision :: outflowedMass , massReturnRate , &
& angularMomentumReturnRate, densityAtOuterRadius, &
& radiusVirial , outerRadius , &
@@ -1385,8 +1427,13 @@ subroutine Node_Component_Hot_Halo_Standard_Outflow_Return(self,interrupt,interr
outerRadius =self %outerRadius ( )
radiusVirial=darkMatterHaloScale_%radiusVirial(node)
if (outerRadius < radiusVirial) then
- basic => node %basic ( )
- densityAtOuterRadius = hotHaloMassDistribution_%density(node,outerRadius)
+ coordinates = [outerRadius,0.0d0,0.0d0]
+ basic => node %basic ( )
+ massDistribution_ => node %massDistribution(componentTypeHotHalo,massTypeGaseous)
+ densityAtOuterRadius = massDistribution_%density (coordinates )
+ !![
+
+ !!]
! If the outer radius and density are non-zero we can expand the outer radius at a rate determined by the current
! density profile.
if (outerRadius > 0.0d0 .and. densityAtOuterRadius > 0.0d0) then
@@ -1604,24 +1651,26 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
use :: Abundances_Structure , only : abundances , operator(*) , zeroAbundances , operator(>)
use :: Accretion_Halos , only : accretionModeHot , accretionModeTotal
use :: Chemical_Abundances_Structure , only : chemicalAbundances , operator(*) , zeroChemicalAbundances , operator(>)
- use :: Galactic_Structure_Options , only : componentTypeAll , massTypeBaryonic , radiusLarge
+ use :: Galactic_Structure_Options , only : componentTypeAll , massTypeBaryonic
use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentHotHalo , nodeComponentHotHaloStandard, nodeComponentSpin, &
& treeNode , defaultHotHaloComponent
+ use :: Mass_Distributions , only : massDistributionClass
use :: Error , only : Error_Report
use :: Node_Component_Hot_Halo_Standard_Data, only : fractionBaryonLimitInNodeMerger, starveSatellites , starveSatellitesOutflowed
implicit none
- type (treeNode ), intent(inout) :: node
- type (treeNode ), pointer :: nodeParent
- class (nodeComponentHotHalo), pointer :: hotHaloParent , hotHalo
- class (nodeComponentSpin ), pointer :: spinParent
- class (nodeComponentBasic ), pointer :: parentBasic , basic
- double precision :: baryonicMassCurrent , baryonicMassMaximum , &
- & fractionRemove , massAccreted , &
- & massUnaccreted , massReaccreted , &
- & fractionAccreted , angularMomentumAccreted , &
- & massAccretedHot
- logical :: massTotalNonZero
- type (abundances ), save :: massMetalsReaccreted
+ type (treeNode ), intent(inout) :: node
+ type (treeNode ), pointer :: nodeParent
+ class (nodeComponentHotHalo ), pointer :: hotHaloParent , hotHalo
+ class (nodeComponentSpin ), pointer :: spinParent
+ class (nodeComponentBasic ), pointer :: basicParent , basic
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: baryonicMassCurrent , baryonicMassMaximum , &
+ & fractionRemove , massAccreted , &
+ & massUnaccreted , massReaccreted , &
+ & fractionAccreted , angularMomentumAccreted , &
+ & massAccretedHot
+ logical :: massTotalNonZero
+ type (abundances ), save :: massMetalsReaccreted
!$omp threadprivate(massMetalsReaccreted)
type (chemicalAbundances ), save :: massChemicalsAccreted , fractionChemicalsAccreted, &
& massChemicalsReaccreted
@@ -1639,7 +1688,7 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
call Node_Component_Hot_Halo_Standard_Create(nodeParent)
hotHaloParent => nodeParent%hotHalo(autoCreate=.true.)
spinParent => nodeParent%spin ( )
- parentBasic => nodeParent%basic ( )
+ basicParent => nodeParent%basic ( )
basic => node %basic ( )
! Any gas that failed to be accreted by this halo is always transferred to the parent.
call hotHaloParent% unaccretedMassSet( &
@@ -1675,7 +1724,7 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
massReaccreted=+hotHaloParent %unaccretedMass() &
& *fractionAccreted &
& *basic % mass() &
- & /parentBasic % mass()
+ & /basicParent % mass()
!! Reaccrete the gas.
call hotHaloParent%unaccretedMassSet(hotHaloParent%unaccretedMass()-massReaccreted)
call hotHaloParent% massSet(hotHaloParent% mass()+massReaccreted)
@@ -1683,13 +1732,13 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
massMetalsReaccreted=+hotHaloParent %unaccretedAbundances() &
& *fractionAccreted &
& *basic % mass() &
- & /parentBasic % mass()
+ & /basicParent % mass()
call hotHaloParent%unaccretedAbundancesSet(hotHaloParent%unaccretedAbundances()-massMetalsReaccreted)
call hotHaloParent% abundancesSet(hotHaloParent% abundances()+massMetalsReaccreted)
! Compute the reaccreted angular momentum.
angularMomentumAccreted=+ massReaccreted &
& *spinParent %angularMomentum() &
- & /parentBasic%mass ()
+ & /basicParent%mass ()
call hotHaloParent%angularMomentumSet(hotHaloParent%angularMomentum()+angularMomentumAccreted)
end if
! Compute the reaccreted chemicals.
@@ -1707,7 +1756,7 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
massChemicalsReaccreted=+hotHaloParent %unaccretedMass() &
& *fractionChemicalsAccreted &
& *basic % mass() &
- & /parentBasic % mass()
+ & /basicParent % mass()
!! Reaccrete the chemicals.
call hotHaloParent%chemicalsSet(hotHaloParent%chemicals()+massChemicalsReaccreted)
end if
@@ -1724,7 +1773,7 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
& hotHaloParent%angularMomentum () &
& +hotHalo %mass () &
& *spinParent %angularMomentum () &
- & /parentBasic %mass () &
+ & /basicParent %mass () &
& )
end if
call hotHaloParent% outflowedMassSet( &
@@ -1735,7 +1784,7 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
& hotHaloParent%outflowedAngularMomentum() &
& +hotHalo %outflowedMass () &
& *spinParent %angularMomentum () &
- & /parentBasic %mass () &
+ & /basicParent %mass () &
& )
if (starveSatellites) then
call hotHalo % massSet( &
@@ -1787,8 +1836,14 @@ subroutine Node_Component_Hot_Halo_Standard_Node_Merger(node)
! some of the mass to the failed accretion reservoir.
if (fractionBaryonLimitInNodeMerger) then
! Get the default cosmology.
- baryonicMassMaximum=parentBasic%mass()*cosmologyParameters_%OmegaBaryon()/cosmologyParameters_%OmegaMatter()
- baryonicMassCurrent=galacticStructure_%massEnclosed(nodeParent,radiusLarge,massType=massTypeBaryonic,componentType =componentTypeAll)
+ massDistribution_ => nodeParent %massDistribution(massType=massTypeBaryonic)
+ baryonicMassMaximum = +basicParent %mass ( ) &
+ & *cosmologyParameters_%omegaBaryon ( ) &
+ & /cosmologyParameters_%omegaMatter ( )
+ baryonicMassCurrent = +massDistribution_ %massTotal ( )
+ !![
+
+ !!]
if (baryonicMassCurrent > baryonicMassMaximum .and. hotHaloParent%mass() > 0.0d0) then
fractionRemove=min((baryonicMassCurrent-baryonicMassMaximum)/hotHaloParent%massTotal(),1.0d0)
call hotHaloParent% unaccretedMassSet( &
@@ -2179,7 +2234,7 @@ subroutine Node_Component_Hot_Halo_Standard_State_Store(stateFile,gslStateFile,s
call displayMessage('Storing state for: componentHotHalo -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Hot_Halo_Standard_State_Store
@@ -2202,9 +2257,42 @@ subroutine Node_Component_Hot_Halo_Standard_State_Restore(stateFile,gslStateFile
call displayMessage('Retrieving state for: componentHotHalo -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Hot_Halo_Standard_State_Restore
+ function Node_Component_Hot_Halo_Standard_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
+ !!{
+ Return the mass distribution associated with the hot halo.
+ !!}
+ use :: Galacticus_Nodes , only : nodeComponentHotHaloStandard
+ use :: Galactic_Structure_Options, only : enumerationWeightByType , enumerationComponentTypeType, enumerationMassTypeType , massTypeGaseous, &
+ & componentTypeHotHalo
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass , massDistributionMatches_
+ implicit none
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ class (nodeComponentHotHaloStandard), intent(inout) :: self
+ type (enumerationComponentTypeType), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ !$GLC attributes unused :: weightIndex
+
+ if (massDistributionMatches_(componentTypeHotHalo,massTypeGaseous,componentType,massType)) then
+ massDistribution_ => hotHaloMassDistribution_%get(self%hostNode,weightBy,weightIndex)
+ if (associated(massDistribution_)) then
+ kinematicsDistribution_ => hotHaloTemperatureProfile_%get(self%hostNode)
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ !![
+
+ !!]
+ end if
+ else
+ massDistribution_ => null()
+ end if
+ return
+ end function Node_Component_Hot_Halo_Standard_Mass_Distribution
+
end module Node_Component_Hot_Halo_Standard
diff --git a/source/objects.nodes.components.hot_halo.standard.bound_functions.Inc b/source/objects.nodes.components.hot_halo.standard.bound_functions.Inc
index 5edc7ca016..a1a8c07a40 100644
--- a/source/objects.nodes.components.hot_halo.standard.bound_functions.Inc
+++ b/source/objects.nodes.components.hot_halo.standard.bound_functions.Inc
@@ -45,3 +45,14 @@ double precision function Node_Component_Hot_Halo_Standard_Mass_Total(self)
return
end function Node_Component_Hot_Halo_Standard_Mass_Total
+double precision function Node_Component_Hot_Halo_Standard_Mass_Baryonic(self) result(massBaryonic)
+ !!{
+ Return the baryonic mass for the hot halo standard component.
+ !!}
+ implicit none
+ class(nodeComponentHotHaloStandard), intent(inout) :: self
+
+ massBaryonic=+max(0.0d0,self%mass ()) &
+ & +max(0.0d0,self%outflowedMass())
+ return
+end function Node_Component_Hot_Halo_Standard_Mass_Baryonic
diff --git a/source/objects.nodes.components.hot_halo.very_simple.F90 b/source/objects.nodes.components.hot_halo.very_simple.F90
index 29d09a6992..04c9be7019 100644
--- a/source/objects.nodes.components.hot_halo.very_simple.F90
+++ b/source/objects.nodes.components.hot_halo.very_simple.F90
@@ -94,6 +94,10 @@ module Node_Component_Hot_Halo_Very_Simple
+
+
+
+ objects.nodes.components.hot_halo.very_simple.bound_functions.inc
!!]
diff --git a/source/objects.nodes.components.hot_halo.very_simple.bound_functions.Inc b/source/objects.nodes.components.hot_halo.very_simple.bound_functions.Inc
new file mode 100644
index 0000000000..462bf0a7d0
--- /dev/null
+++ b/source/objects.nodes.components.hot_halo.very_simple.bound_functions.Inc
@@ -0,0 +1,33 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 custom functions for the very simple hot halo component.
+!!}
+
+double precision function Node_Component_Hot_Halo_Very_Simple_Mass_Baryonic(self) result(massBaryonic)
+ !!{
+ Return the baryonic mass for the very simple hot halo component.
+ !!}
+ implicit none
+ class(nodeComponentHotHaloVerySimple), intent(inout) :: self
+
+ massBaryonic=+max(0.0d0,self%mass())
+ return
+end function Node_Component_Hot_Halo_Very_Simple_Mass_Baryonic
diff --git a/source/objects.nodes.components.hot_halo.very_simple_delayed.F90 b/source/objects.nodes.components.hot_halo.very_simple_delayed.F90
index 20c6ac1eb4..e8e27dc9e4 100644
--- a/source/objects.nodes.components.hot_halo.very_simple_delayed.F90
+++ b/source/objects.nodes.components.hot_halo.very_simple_delayed.F90
@@ -61,6 +61,10 @@ module Node_Component_Hot_Halo_VS_Delayed
+
+
+
+ objects.nodes.components.hot_halo.very_simple_delayed.bound_functions.inc
!!]
diff --git a/source/objects.nodes.components.hot_halo.very_simple_delayed.bound_functions.Inc b/source/objects.nodes.components.hot_halo.very_simple_delayed.bound_functions.Inc
new file mode 100644
index 0000000000..e71d3765f0
--- /dev/null
+++ b/source/objects.nodes.components.hot_halo.very_simple_delayed.bound_functions.Inc
@@ -0,0 +1,34 @@
+!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+!! 2019, 2020, 2021, 2022, 2023, 2024
+!! 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 custom functions for the very simple delayed hot halo component.
+!!}
+
+double precision function Node_Component_Hot_Halo_Very_Simple_Delayed_Mass_Baryonic(self) result(massBaryonic)
+ !!{
+ Return the baryonic mass for the very simple delayed hot halo component.
+ !!}
+ implicit none
+ class(nodeComponentHotHaloVerySimpleDelayed), intent(inout) :: self
+
+ massBaryonic=+max(0.0d0,self%mass ()) &
+ & +max(0.0d0,self%outflowedMass())
+ return
+end function Node_Component_Hot_Halo_Very_Simple_Delayed_Mass_Baryonic
diff --git a/source/objects.nodes.components.satellite.orbiting.F90 b/source/objects.nodes.components.satellite.orbiting.F90
index ce65604903..c19b33fb31 100644
--- a/source/objects.nodes.components.satellite.orbiting.F90
+++ b/source/objects.nodes.components.satellite.orbiting.F90
@@ -26,15 +26,13 @@ module Node_Component_Satellite_Orbiting
!!{
Implements the orbiting satellite component.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Kepler_Orbits , only : keplerOrbit
- use :: Tensors , only : tensorRank2Dimension3Symmetric
- use :: Virial_Orbits , only : virialOrbit , virialOrbitClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Kepler_Orbits , only : keplerOrbit
+ use :: Tensors , only : tensorRank2Dimension3Symmetric
+ use :: Virial_Orbits , only : virialOrbit , virialOrbitClass
implicit none
private
public :: Node_Component_Satellite_Orbiting_Scale_Set , Node_Component_Satellite_Orbiting_Create , &
@@ -122,14 +120,12 @@ module Node_Component_Satellite_Orbiting
!!]
! Objects used by this module.
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(virialDensityContrastClass), pointer :: virialDensityContrast_
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(virialOrbitClass ), pointer :: virialOrbit_
- class(galacticStructureClass ), pointer :: galacticStructure_
- !$omp threadprivate(darkMatterHaloScale_,virialOrbit_,darkMatterProfileDMO_,virialDensityContrast_,cosmologyParameters_,cosmologyFunctions_,galacticStructure_)
+ !$omp threadprivate(darkMatterHaloScale_,virialOrbit_,virialDensityContrast_,cosmologyParameters_,cosmologyFunctions_)
! Option controlling whether or not unbound virial orbits are acceptable.
logical , parameter :: acceptUnboundOrbits =.false.
@@ -239,11 +235,9 @@ subroutine Node_Component_Satellite_Orbiting_Thread_Initialize(parameters)
!![
-
-
!!]
dependenciesSubhaloPromotion(1)=dependencyExact(dependencyDirectionBefore,'mergerTreeNodeEvolver')
call subhaloPromotionEvent%attach(thread,subhaloPromotion ,openMPThreadBindingAtLevel,label='nodeComponentSatelliteOrbiting',dependencies=dependenciesSubhaloPromotion)
@@ -272,11 +266,9 @@ subroutine Node_Component_Satellite_Orbiting_Thread_Uninitialize()
!![
-
-
!!]
if ( subhaloPromotionEvent%isAttached(thread,subhaloPromotion )) call subhaloPromotionEvent%detach(thread,subhaloPromotion )
if (satellitePreHostChangeEvent%isAttached(thread,satellitePreHostChange)) call satellitePreHostChangeEvent%detach(thread,satellitePreHostChange)
@@ -584,14 +576,15 @@ subroutine Node_Component_Satellite_Orbiting_Bound_Mass_Initialize(satellite,nod
!!{
Set the initial bound mass of the satellite.
!!}
+ use :: Mass_Distributions , only : massDistributionClass
use :: Dark_Matter_Profile_Mass_Definitions, only : Dark_Matter_Profile_Mass_Definition
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentSatellite , nodeComponentSatelliteOrbiting, treeNode
implicit none
class (nodeComponentSatellite), intent(inout) :: satellite
type (treeNode ), intent(inout) :: node
- double precision :: virialRadius , maximumRadius, &
- & massSatellite
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision :: massSatellite , maximumRadius
select type (satellite)
class is (nodeComponentSatelliteOrbiting)
@@ -600,9 +593,10 @@ subroutine Node_Component_Satellite_Orbiting_Bound_Mass_Initialize(satellite,nod
! Do nothing. The bound mass of this satellite is set to the node mass by default.
case (initializationTypeMassBoundMaximumRadius %ID)
! Set the initial bound mass of this satellite by integrating the density profile up to a maximum radius.
- virialRadius =darkMatterHaloScale_%radiusVirial(node )
- maximumRadius=radiusMaximumOverRadiusVirial*virialRadius
- massSatellite=galacticStructure_ %massEnclosed(node,maximumRadius)
+ maximumRadius = + radiusMaximumOverRadiusVirial &
+ & *darkMatterHaloScale_%radiusVirial (node )
+ massDistribution_ => node %massDistribution ( )
+ massSatellite = massDistribution_ %massEnclosedBySphere (maximumRadius)
call satellite%boundMassSet(massSatellite)
case (initializationTypeMassBoundDensityContrast%ID)
! Set the initial bound mass of this satellite by assuming a specified density contrast.
@@ -611,7 +605,6 @@ subroutine Node_Component_Satellite_Orbiting_Bound_Mass_Initialize(satellite,nod
& densityContrastMassBound, &
& cosmologyParameters_ =cosmologyParameters_ , &
& cosmologyFunctions_ =cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMO_ , &
& virialDensityContrast_=virialDensityContrast_ &
& )
call satellite%boundMassSet(massSatellite)
@@ -640,7 +633,7 @@ subroutine Node_Component_Satellite_Orbiting_State_Store(stateFile,gslStateFile,
call displayMessage('Storing state for: componentSatellite -> orbiting',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Satellite_Orbiting_State_Store
@@ -663,7 +656,7 @@ subroutine Node_Component_Satellite_Orbiting_State_Restore(stateFile,gslStateFil
call displayMessage('Retrieving state for: componentSatellite -> orbiting',verbosity=verbosityLevelInfo)
!![
-
+
!!]
return
end subroutine Node_Component_Satellite_Orbiting_State_Restore
diff --git a/source/objects.nodes.components.spheroid.standard.F90 b/source/objects.nodes.components.spheroid.standard.F90
index 0d11068beb..0291bb4b27 100644
--- a/source/objects.nodes.components.spheroid.standard.F90
+++ b/source/objects.nodes.components.spheroid.standard.F90
@@ -148,15 +148,8 @@ module Node_Component_Spheroid_Standard
-
-
-
-
-
-
-
-
-
+
+
objects.nodes.components.spheroid.standard.bound_functions.inc
@@ -267,29 +260,30 @@ subroutine Node_Component_Spheroid_Standard_Thread_Initialize(parameters)
!!{
Initializes the standard spheroid module for each thread.
!!}
- use :: Events_Hooks , only : dependencyDirectionAfter , dependencyRegEx , openMPThreadBindingAtLevel, postEvolveEvent, &
- & satelliteMergerEvent , mergerTreeExtraOutputEvent
+ use :: Events_Hooks , only : dependencyDirectionAfter , dependencyRegEx , openMPThreadBindingAtLevel, postEvolveEvent, &
+ & satelliteMergerEvent , mergerTreeExtraOutputEvent
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : defaultSpheroidComponent
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Mass_Distributions , only : massDistributionSymmetrySpherical
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Mass_Distributions , only : massDistributionSpherical, kinematicsDistributionLocal
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_ , massDistributionGas_ , kinematicDistribution_
+ use :: Galactic_Structure_Options , only : componentTypeSpheroid , massTypeStellar , massTypeGaseous
implicit none
type (inputParameters), intent(inout) :: parameters
- logical :: densityMoment2IsInfinite , densityMoment3IsInfinite
- double precision :: massDistributionSpheroidDensityMomentum2 , massDistributionSpheroidDensityMomentum3, &
- & ratioAngularMomentumScaleRadiusDefault
type (dependencyRegEx), dimension(3) :: dependencies
+ logical :: densityMoment2IsInfinite , densityMoment3IsInfinite
+ double precision :: massDistributionSpheroidDensityMomentum2, massDistributionSpheroidDensityMomentum3, &
+ & ratioAngularMomentumScaleRadiusDefault
type (inputParameters) :: subParameters
! Check if this implementation is selected. If so, initialize the mass distribution.
if (defaultSpheroidComponent%standardIsActive()) then
- call postEvolveEvent %attach(thread,postEvolve ,openMPThreadBindingAtLevel,label='nodeComponentSpheroidStandard' )
dependencies(1)=dependencyRegEx(dependencyDirectionAfter,'^remnantStructure:')
dependencies(2)=dependencyRegEx(dependencyDirectionAfter,'^preAnalysis:' )
dependencies(3)=dependencyRegEx(dependencyDirectionAfter,'^nodeComponentDisk')
call satelliteMergerEvent %attach(thread,satelliteMerger ,openMPThreadBindingAtLevel,label='nodeComponentSpheroidStandard',dependencies=dependencies)
call mergerTreeExtraOutputEvent%attach(thread,mergerTreeExtraOutput,openMPThreadBindingAtLevel,label='nodeComponentSpheroidStandard' )
+ call postEvolveEvent %attach(thread,postEvolve ,openMPThreadBindingAtLevel,label='nodeComponentSpheroidStandard' )
! Find our parameters.
subParameters=parameters%subParameters('componentSpheroid')
!![
@@ -298,7 +292,7 @@ subroutine Node_Component_Spheroid_Standard_Thread_Initialize(parameters)
-
+
@@ -306,15 +300,35 @@ subroutine Node_Component_Spheroid_Standard_Thread_Initialize(parameters)
!!]
- if (.not.massDistributionSpheroid%isDimensionless() ) &
- & call Error_Report('spheroid mass distribution must be dimensionless' //{introspection:location})
- if (.not.massDistributionSpheroid%symmetry () == massDistributionSymmetrySpherical) &
- & call Error_Report('spheroid mass distribution must be spherically symmetric'//{introspection:location})
+ ! Validate the disk mass distribution.
+ select type (massDistributionStellar_)
+ class is (massDistributionSpherical)
+ ! The spheroid mass distribution must have spherical symmetry. So, this is acceptable.
+ class default
+ call Error_Report('only spehrically symmetric mass distributions are allowed'//{introspection:location})
+ end select
+ if (.not.massDistributionStellar_%isDimensionless()) call Error_Report('spheroid mass distribution must be dimensionless'//{introspection:location})
+ ! Duplicate the dimensionless mass distribution to use for the gas component, and set component and mass types in both.
+ !$omp critical(spheroidStandardDeepCopy)
+ allocate(massDistributionGas_,mold=massDistributionStellar_)
+ !![
+
+
+
+ !!]
+ !$omp end critical(spheroidStandardDeepCopy)
+ call massDistributionStellar_%setTypes(componentTypeSpheroid,massTypeStellar)
+ call massDistributionGas_ %setTypes(componentTypeSpheroid,massTypeGaseous)
+ ! Construct the kinematic distribution.
+ allocate(kinematicDistribution_)
+ !![
+
+ !!]
! Determine the specific angular momentum at the scale radius in units of the mean specific angular
! momentum of the spheroid. This is equal to the ratio of the 2nd to 3rd radial moments of the density
! distribution (assuming a flat rotation curve).
- massDistributionSpheroidDensityMomentum2=massDistributionSpheroid%densityRadialMoment(2.0d0,isInfinite=densityMoment2IsInfinite)
- massDistributionSpheroidDensityMomentum3=massDistributionSpheroid%densityRadialMoment(3.0d0,isInfinite=densityMoment3IsInfinite)
+ massDistributionSpheroidDensityMomentum2=massDistributionStellar_%densityRadialMoment(2.0d0,isInfinite=densityMoment2IsInfinite)
+ massDistributionSpheroidDensityMomentum3=massDistributionStellar_%densityRadialMoment(3.0d0,isInfinite=densityMoment3IsInfinite)
if (densityMoment2IsInfinite.or.densityMoment3IsInfinite) then
! One of the moments is infinite, so we can not compute the appropriate ratio. Simply assume a value
! of 0.5 as a default.
@@ -350,7 +364,7 @@ subroutine Node_Component_Spheroid_Standard_Thread_Uninitialize()
!!}
use :: Events_Hooks , only : postEvolveEvent , satelliteMergerEvent, mergerTreeExtraOutputEvent
use :: Galacticus_Nodes , only : defaultSpheroidComponent
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
if (defaultSpheroidComponent%standardIsActive()) then
@@ -363,7 +377,9 @@ subroutine Node_Component_Spheroid_Standard_Thread_Uninitialize()
-
+
+
+
!!]
end if
return
@@ -1536,7 +1552,7 @@ subroutine Node_Component_Spheroid_Standard_State_Store(stateFile,gslStateFile,s
!!}
use :: Display , only : displayMessage , verbosityLevelInfo
use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -1544,7 +1560,7 @@ subroutine Node_Component_Spheroid_Standard_State_Store(stateFile,gslStateFile,s
call displayMessage('Storing state for: componentSpheroid -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
write (stateFile) ratioAngularMomentumScaleRadius
return
@@ -1561,7 +1577,7 @@ subroutine Node_Component_Spheroid_Standard_State_Retrieve(stateFile,gslStateFil
!!}
use :: Display , only : displayMessage , verbosityLevelInfo
use, intrinsic :: ISO_C_Binding , only : c_ptr , c_size_t
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_, massDistributionGas_, kinematicDistribution_
implicit none
integer , intent(in ) :: stateFile
integer(c_size_t), intent(in ) :: stateOperationID
@@ -1569,7 +1585,7 @@ subroutine Node_Component_Spheroid_Standard_State_Retrieve(stateFile,gslStateFil
call displayMessage('Retrieving state for: componentSpheroid -> standard',verbosity=verbosityLevelInfo)
!![
-
+
!!]
read (stateFile) ratioAngularMomentumScaleRadius
return
diff --git a/source/objects.nodes.components.spheroid.standard.bound_functions.Inc b/source/objects.nodes.components.spheroid.standard.bound_functions.Inc
index 6e1bf80938..6be7147c13 100644
--- a/source/objects.nodes.components.spheroid.standard.bound_functions.Inc
+++ b/source/objects.nodes.components.spheroid.standard.bound_functions.Inc
@@ -21,417 +21,135 @@
Contains custom functions for the standard spheroid component.
!!}
-double precision function Node_Component_Spheroid_Standard_Half_Mass_Radius(self)
+function Node_Component_Spheroid_Standard_Mass_Distribution(self,componentType,massType,weightBy,weightIndex) result(massDistribution_)
!!{
- Return the half-mass radius of the standard spheroid.
- !!}
- use :: Error , only : Error_Report
- use :: Mass_Distributions , only : massDistributionSpherical
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
- implicit none
- class(nodeComponentSpheroidStandard), intent(inout) :: self
-
- select type (massDistributionSpheroid)
- class is (massDistributionSpherical)
- Node_Component_Spheroid_Standard_Half_Mass_Radius=self%radius()*massDistributionSpheroid%radiusHalfMass()
- class default
- Node_Component_Spheroid_Standard_Half_Mass_Radius=0.0d0
- call Error_Report('mass distribution should be spherical'//{introspection:location})
- end select
- return
-end function Node_Component_Spheroid_Standard_Half_Mass_Radius
-
-double precision function Node_Component_Spheroid_Standard_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the mass within a given radius for an standard spheroid.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeSpheroid, massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , radiusLarge , &
- & weightByLuminosity , weightByMass , enumerationComponentTypeType, enumerationMassTypeType, &
- & enumerationWeightByType
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
- implicit none
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- double precision :: fractionalRadius , radiusSpheroid
- type (stellarLuminosities ), save :: luminositiesSpheroid
- !$omp threadprivate(luminositiesSpheroid)
-
- ! Return if the spheroid component is not selected.
- Node_Component_Spheroid_Standard_Enclosed_Mass=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
- ! Get the total mass.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Spheroid_Standard_Enclosed_Mass=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Spheroid_Standard_Enclosed_Mass=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Spheroid_Standard_Enclosed_Mass= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesSpheroid=self%luminositiesStellar()
- Node_Component_Spheroid_Standard_Enclosed_Mass =luminositiesSpheroid%luminosity(weightIndex)
- end select
- end select
- ! Return if total mass was requested.
- if (radius >= radiusLarge) return
- ! Return if mass is zero.
- if (Node_Component_Spheroid_Standard_Enclosed_Mass <= 0.0d0) return
- ! Compute actual mass.
- radiusSpheroid=self%radius()
- if (radiusSpheroid > 0.0d0) then
- fractionalRadius=radius/radiusSpheroid
- Node_Component_Spheroid_Standard_Enclosed_Mass =Node_Component_Spheroid_Standard_Enclosed_Mass*massDistributionSpheroid%massEnclosedBySphere(fractionalRadius)
- end if
- return
-end function Node_Component_Spheroid_Standard_Enclosed_Mass
-
-function Node_Component_Spheroid_Standard_Acceleration(self,positionCartesian,componentType,massType)
- !!{
- Computes the gravitational acceleration at a given position for a standard spheroid.
- !!}
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeSpheroid , weightByMass , weightIndexNull, &
- & enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec , gravitationalConstantGalacticus
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision , dimension(3) :: Node_Component_Spheroid_Standard_Acceleration
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision :: radius , massEnclosed
-
- ! Return if the spheroid component is not selected.
- Node_Component_Spheroid_Standard_Acceleration=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
- ! Compute the acceleration.
- radius =sqrt(sum(positionCartesian**2))
- massEnclosed =Node_Component_Spheroid_Standard_Enclosed_Mass(self,radius,componentType,massType,weightByMass,weightIndexNull)
- Node_Component_Spheroid_Standard_Acceleration=-kilo &
- & *gigaYear &
- & /megaParsec &
- & *gravitationalConstantGalacticus &
- & *massEnclosed &
- & *positionCartesian &
- & /radius**3
- return
-end function Node_Component_Spheroid_Standard_Acceleration
-
-function Node_Component_Spheroid_Standard_Tidal_Tensor(self,positionCartesian,componentType,massType)
- !!{
- Computes the gravitational tidal tensor at a given position for a standard spheroid.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateCartesian
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeSpheroid , weightByMass , weightIndexNull, &
- & radiusLarge , enumerationComponentTypeType, enumerationMassTypeType
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
- use :: Tensors , only : tensorRank2Dimension3Symmetric , tensorNullR2D3Sym , operator(*)
- implicit none
- type (tensorRank2Dimension3Symmetric) :: Node_Component_Spheroid_Standard_Tidal_Tensor
- class (nodeComponentSpheroidStandard ), intent(inout) :: self
- double precision , intent(in ), dimension(3) :: positionCartesian
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision :: massTotal
- type (coordinateCartesian ) :: positionScaleFree
-
- ! Return if the spheroid component is not selected.
- Node_Component_Spheroid_Standard_Tidal_Tensor=tensorNullR2D3Sym
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
- ! Find the total mass.
- massTotal=Node_Component_Spheroid_Standard_Enclosed_Mass(self,radiusLarge,componentType,massType,weightByMass,weightIndexNull)
- if (massTotal <= 0.0d0 .or. self%radius() <= 0.0d0) return
- ! Compute the tidal tensor.
- positionScaleFree=positionCartesian/self%radius()
- Node_Component_Spheroid_Standard_Tidal_Tensor=+gravitationalConstantGalacticus &
- & *massTotal &
- & *massDistributionSpheroid%tidalTensor(positionScaleFree) &
- & /self%radius()**3
- return
-end function Node_Component_Spheroid_Standard_Tidal_Tensor
-
-function Node_Component_Spheroid_Standard_Chandrasekhar_Integral(self,nodeSatellite,positionCartesian,velocityCartesian,componentType,massType)
- !!{
- Computes the gravitational acceleration at a given position for a standard spheroid.
+ Return the mass distribution for the standard spheroid component.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll , componentTypeSpheroid , weightByMass, weightIndexNull, &
- & enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionSphericalScaler, massDistributionComposite , massDistributionList , &
+ & massDistributionSpherical, massDistributionMatches_
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_ , massDistributionGas_ , kinematicDistribution_
+ use :: Galactic_Structure_Options , only : componentTypeSpheroid , massTypeStellar , massTypeGaseous , enumerationWeightByType, &
+ & weightByMass , weightByLuminosity , enumerationComponentTypeType, enumerationMassTypeType
implicit none
- double precision , dimension(3) :: Node_Component_Spheroid_Standard_Chandrasekhar_Integral
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (treeNode ), intent(inout) :: nodeSatellite
- double precision , intent(in ), dimension(3) :: positionCartesian , velocityCartesian
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , dimension(3) :: positionSpherical
- double precision , parameter :: XvMaximum =10.0d0
- double precision :: radius , velocity , &
- & density , xV , &
- & velocityRotation , velocityDispersion
- !$GLC attributes unused :: nodeSatellite
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (nodeComponentSpheroidStandard ), intent(inout) :: self
+ type (enumerationComponentTypeType ), intent(in ), optional :: componentType
+ type (enumerationMassTypeType ), intent(in ), optional :: massType
+ type (enumerationWeightByType ), intent(in ), optional :: weightBy
+ integer , intent(in ), optional :: weightIndex
+ type (massDistributionSphericalScaler), pointer :: massDistributionStellar , massDistributionGas
+ type (massDistributionComposite ), pointer :: massDistributionTotal
+ type (massDistributionList ), pointer :: massDistributionComponents
+ type (stellarLuminosities ), save :: luminosities
+ !$omp threadprivate(luminosities)
+ double precision :: massStellar , massGas , &
+ & radiusScale
+ logical :: includeGas , includeStars
+ !![
+
+ !!]
- ! Return if the spheroid component is not selected.
- Node_Component_Spheroid_Standard_Chandrasekhar_Integral=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
- ! Compute the integral.
- radius = sqrt(sum(positionCartesian**2))
- velocity = sqrt(sum(velocityCartesian**2))
- if (velocity <= 0.0d0) return
- positionSpherical = [radius,0.0d0,0.0d0]
- velocityRotation = Node_Component_Spheroid_Standard_Rotation_Curve(self,radius ,componentType,massType )
- density = Node_Component_Spheroid_Standard_Density (self,positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (density <= 0.0d0) return
- velocityDispersion = +velocityRotation &
- & /sqrt(2.0d0)
- xV = +velocity &
- & /velocityDispersion &
- & /sqrt(2.0d0)
- Node_Component_Spheroid_Standard_Chandrasekhar_Integral = -density &
- & *velocityCartesian &
- & /velocity **3
- if (Xv <= XvMaximum) &
- & Node_Component_Spheroid_Standard_Chandrasekhar_Integral=+Node_Component_Spheroid_Standard_Chandrasekhar_Integral &
- & *( &
- & +erf ( xV ) &
- & -2.0d0 &
- & * xV &
- & *exp (-xV**2) &
- & /sqrt( Pi ) &
- & )
- return
-end function Node_Component_Spheroid_Standard_Chandrasekhar_Integral
-
-double precision function Node_Component_Spheroid_Standard_Rotation_Curve(self,radius,componentType,massType)
- !!{
- Computes the rotation curve at a given radius for a standard spheroid.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentMass
-
- ! Set to zero by default.
- Node_Component_Spheroid_Standard_Rotation_Curve=0.0d0
-
- ! Return immediately for non-positive radius.
- if (radius <= 0.0d0) return
-
- ! Compute if a spheroid is present.
- componentMass=self%enclosedMass(radius,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass > 0.0d0) Node_Component_Spheroid_Standard_Rotation_Curve=sqrt(gravitationalConstantGalacticus&
- &*componentMass)/sqrt(radius)
- return
-end function Node_Component_Spheroid_Standard_Rotation_Curve
-
-double precision function Node_Component_Spheroid_Standard_Rotation_Curve_Gradient(self,radius,componentType,massType)
- !!{
- Computes the rotation curve gradient for the standard spheroid.
- !!}
- use :: Galactic_Structure_Options , only : weightByMass , weightIndexNull, enumerationComponentTypeType, enumerationMassTypeType
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
- implicit none
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- double precision :: componentDensity, componentMass, positionSpherical(3)
-
- ! Set to zero by default.
- Node_Component_Spheroid_Standard_Rotation_Curve_Gradient=0.0d0
-
- ! Return immediately for non-positive radius.
- if (radius <= 0.0d0) return
-
- ! Compute if a spheroid is present.
- positionSpherical= [radius,0.0d0,0.0d0]
- componentMass =self%enclosedMass(radius ,componentType,massType,weightByMass,weightIndexNull)
- componentDensity =self%density (positionSpherical,componentType,massType,weightByMass,weightIndexNull)
- if (componentMass == 0.0d0 .or. componentDensity == 0.0d0) return
- Node_Component_Spheroid_Standard_Rotation_Curve_Gradient= &
- & gravitationalConstantGalacticus &
- & *( &
- & -componentMass/radius**2 &
- & +4.0d0*Pi*radius*componentDensity &
- & )
- return
-end function Node_Component_Spheroid_Standard_Rotation_Curve_Gradient
-
-double precision function Node_Component_Spheroid_Standard_Density(self,positionSpherical,componentType,massType,weightBy,weightIndex)
- !!{
- Computes the density at a given position for an standard spheroid.
- !!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeSpheroid , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
- implicit none
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: positionSpherical(3)
- double precision , parameter :: radiusHuge =1.0d+100
- type (coordinateSpherical ) :: position
- type (stellarLuminosities ), save :: luminositiesSpheroid
- !$omp threadprivate(luminositiesSpheroid)
-
- Node_Component_Spheroid_Standard_Density=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
-
- ! Get the spheroid component and check that it is of the standard class.
- select type (self)
- class is (nodeComponentSpheroidStandard)
-
- if (self%radius() <= 0.0d0 .or. self%radius() > radiusHuge) return
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Spheroid_Standard_Density=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Spheroid_Standard_Density=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Spheroid_Standard_Density= self%massStellar()
+ ! Determine which components of the spheroid to include.
+ includeGas =massDistributionMatches_(componentTypeSpheroid,massTypeGaseous,componentType,massType) .and. weightBy_ == weightByMass
+ includeStars=massDistributionMatches_(componentTypeSpheroid,massTypeStellar,componentType,massType) .and. (weightBy_ == weightByMass .or. weightBy_ == weightByLuminosity)
+ ! Get properties of the mass distribution and ensure they are physical.
+ if (weightBy_ == weightByMass ) then
+ massStellar = max (0.0d0,self %massStellar ( ))
+ massGas = max (0.0d0,self %massGas ( ))
+ else if (weightBy_ == weightByLuminosity) then
+ luminosities = self %luminositiesStellar( )
+ massStellar = max (0.0d0,luminosities%luminosity (weightIndex))
+ massGas = 0.0d0
+ else
+ massDistribution_ => null()
+ return
+ end if
+ ! Determine which components to build.
+ radiusScale=self%radius()
+ if (radiusScale <= 0.0d0 .or. .not.(includeGas .or. includeStars)) then
+ ! Disk has non-positive size, or no components matched. Return a null distribution.
+ massDistribution_ => null()
+ else
+ ! Build the individual distributions.
+ massDistributionStellar => null()
+ massDistributionGas => null()
+ if (includeStars) then
+ allocate(massDistributionStellar)
+ select type (massDistributionStellar_)
+ class is (massDistributionSpherical)
+ !![
+
+ !!]
end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesSpheroid=self%luminositiesStellar()
- Node_Component_Spheroid_Standard_Density=luminositiesSpheroid%luminosity(weightIndex)
+ call massDistributionStellar%setKinematicsDistribution(kinematicDistribution_)
+ end if
+ if (includeGas ) then
+ allocate(massDistributionGas )
+ select type (massDistributionGas_ )
+ class is (massDistributionSpherical)
+ !![
+
+ !!]
end select
- end select
- ! Return if density is zero.
- if (Node_Component_Spheroid_Standard_Density <= 0.0d0) then
- Node_Component_Spheroid_Standard_Density=0.0d0
- return
+ call massDistributionGas %setKinematicsDistribution(kinematicDistribution_)
end if
- ! Compute actual density.
- position=[positionSpherical(1)/self%radius(),0.0d0,0.0d0]
- Node_Component_Spheroid_Standard_Density =+Node_Component_Spheroid_Standard_Density &
- & *massDistributionSpheroid %density(position)
- if (Node_Component_Spheroid_Standard_Density > 0.0d0) &
- & Node_Component_Spheroid_Standard_Density=+Node_Component_Spheroid_Standard_Density &
- & /self %radius ( )**3
- end select
+ ! Combine the distributions as necessary.
+ if (includeStars .and. includeGas) then
+ ! Wrap the dimensionless mass distribution inside scaler classes to allow us to re-scale it to any spheroid system, and then composite those.
+ allocate(massDistributionTotal )
+ allocate(massDistributionComponents )
+ allocate(massDistributionComponents%next)
+ massDistributionComponents %massDistribution_ => massDistributionStellar
+ massDistributionComponents%next%massDistribution_ => massDistributionGas
+ !![
+
+
+
+ !!]
+ nullify(massDistributionComponents)
+ ! Return a pointer to the spheroid mass distribution.
+ massDistribution_ => massDistributionTotal
+ else if (includeStars ) then
+ ! Return just the stellar component.
+ massDistribution_ => massDistributionStellar
+ else if ( includeGas) then
+ ! Return just the gas component.
+ massDistribution_ => massDistributionGas
+ end if
+ end if
return
-end function Node_Component_Spheroid_Standard_Density
+end function Node_Component_Spheroid_Standard_Mass_Distribution
-double precision function Node_Component_Spheroid_Standard_Density_Spherical_Average(self,radius,componentType,massType,weightBy,weightIndex)
+double precision function Node_Component_Spheroid_Standard_Mass_Baryonic(self) result(massBaryonic)
!!{
- Computes the spherically-averaged density at a given radius for an standard spheroid.
+ Return the baryonic mass for the standard spheroid component.
!!}
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options , only : componentTypeAll , componentTypeSpheroid , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic , massTypeGaseous , massTypeStellar , weightByLuminosity , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
implicit none
- class (nodeComponentSpheroidStandard), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- double precision , parameter :: radiusHuge =1.0d+100
- type (coordinateSpherical ) :: position
- type (stellarLuminosities ), save :: luminositiesSpheroid
- !$omp threadprivate(luminositiesSpheroid)
-
- Node_Component_Spheroid_Standard_Density_Spherical_Average=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
-
- ! Get the spheroid component and check that it is of the standard class.
- select type (self)
- class is (nodeComponentSpheroidStandard)
+ class(nodeComponentSpheroidStandard), intent(inout) :: self
- if (self%radius() <= 0.0d0 .or. self%radius() > radiusHuge) return
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Spheroid_Standard_Density_Spherical_Average=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Spheroid_Standard_Density_Spherical_Average=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Spheroid_Standard_Density_Spherical_Average= self%massStellar()
- end select
- case (weightByLuminosity%ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID,massTypeStellar%ID)
- luminositiesSpheroid=self%luminositiesStellar()
- Node_Component_Spheroid_Standard_Density_Spherical_Average=luminositiesSpheroid%luminosity(weightIndex)
- end select
- end select
- ! Return if density is zero.
- if (Node_Component_Spheroid_Standard_Density_Spherical_Average <= 0.0d0) then
- Node_Component_Spheroid_Standard_Density_Spherical_Average=0.0d0
- return
- end if
- ! Compute actual density.
- position=[radius/self%radius(),0.0d0,0.0d0]
- Node_Component_Spheroid_Standard_Density_Spherical_Average =+Node_Component_Spheroid_Standard_Density_Spherical_Average &
- & *massDistributionSpheroid %density(position)
- if (Node_Component_Spheroid_Standard_Density_Spherical_Average > 0.0d0) &
- & Node_Component_Spheroid_Standard_Density_Spherical_Average=+Node_Component_Spheroid_Standard_Density_Spherical_Average &
- & /self %radius ( )**3
- end select
+ massBaryonic=+max(0.0d0,self%massStellar()) &
+ & +max(0.0d0,self%massGas ())
return
-end function Node_Component_Spheroid_Standard_Density_Spherical_Average
+end function Node_Component_Spheroid_Standard_Mass_Baryonic
-double precision function Node_Component_Spheroid_Standard_Potential(self,radius,componentType,massType,status)
- use :: Coordinates , only : assignment(=) , coordinateSpherical
- use :: Galactic_Structure_Options , only : radiusLarge , weightByMass , weightIndexNull, enumerationComponentTypeType, &
- & enumerationMassTypeType , enumerationStructureErrorCodeType
- use :: Node_Component_Spheroid_Standard_Data, only : massDistributionSpheroid
- use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
+double precision function Node_Component_Spheroid_Standard_Half_Mass_Radius(self) result(radiusHalfMass)
!!{
- Return the potential due to the standard spheroid.
+ Return the half-mass radius of the standard spheroid.
!!}
- class (nodeComponentSpheroidStandard ), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- double precision , intent(in ) :: radius
- type (enumerationStructureErrorCodeType), intent(inout), optional :: status
- double precision :: componentMass
- type (coordinateSpherical ) :: position
- !$GLC attributes unused :: status
-
- ! Set to zero by default.
- Node_Component_Spheroid_Standard_Potential=0.0d0
+ use :: Error , only : Error_Report
+ use :: Mass_Distributions , only : massDistributionSpherical
+ use :: Node_Component_Spheroid_Standard_Data, only : massDistributionStellar_
+ implicit none
+ class(nodeComponentSpheroidStandard), intent(inout) :: self
- ! Return immediately for non-positive radius.
- if (radius <= 0.0d0) return
- ! Get the spheroid component and check that it is of the standard class.
- select type (self)
- class is (nodeComponentSpheroidStandard)
- ! Compute if a spheroid is present.
- componentMass=self%enclosedMass(radiusLarge,componentType,massType,weightByMass&
- &,weightIndexNull)
- if (componentMass > 0.0d0 .and. self%radius() > 0.0d0) then
- position=[radius/self%radius(),0.0d0,0.0d0]
- Node_Component_Spheroid_Standard_Potential=(gravitationalConstantGalacticus*componentMass&
- &/self%radius())*massDistributionSpheroid%potential(position)
- end if
+ select type (massDistributionStellar_)
+ class is (massDistributionSpherical)
+ radiusHalfMass=+massDistributionStellar_%radiusHalfMass() &
+ & *self %radius ()
+ class default
+ radiusHalfMass=0.0d0
+ call Error_Report('spheroid mass distribution is not spherically-symmetric'//{introspection:location})
end select
return
-end function Node_Component_Spheroid_Standard_Potential
+end function Node_Component_Spheroid_Standard_Half_Mass_Radius
diff --git a/source/objects.nodes.components.spheroid.standard.data.F90 b/source/objects.nodes.components.spheroid.standard.data.F90
index bff0f11699..b22221ce34 100644
--- a/source/objects.nodes.components.spheroid.standard.data.F90
+++ b/source/objects.nodes.components.spheroid.standard.data.F90
@@ -25,12 +25,13 @@ module Node_Component_Spheroid_Standard_Data
!!{
Contains data for standard spheroid components.
!!}
- use :: Mass_Distributions, only : massDistributionClass
+ use :: Mass_Distributions, only : massDistributionClass, kinematicsDistributionLocal
implicit none
public
- ! The mass distribution object.
- class(massDistributionClass), pointer :: massDistributionSpheroid
- !$omp threadprivate(massDistributionSpheroid)
+ ! The mass distribution objects.
+ class(massDistributionClass ), pointer :: massDistributionStellar_, massDistributionGas_
+ type (kinematicsDistributionLocal), pointer :: kinematicDistribution_
+ !$omp threadprivate(massDistributionStellar_,massDistributionGas_,kinematicDistribution_)
end module Node_Component_Spheroid_Standard_Data
diff --git a/source/objects.nodes.components.spheroid.very_simple.F90 b/source/objects.nodes.components.spheroid.very_simple.F90
index 41b5e62fb6..98c0ccc883 100644
--- a/source/objects.nodes.components.spheroid.very_simple.F90
+++ b/source/objects.nodes.components.spheroid.very_simple.F90
@@ -111,7 +111,7 @@ module Node_Component_Spheroid_Very_Simple
-
+
objects.nodes.components.spheroid.very_simple.bound_functions.inc
diff --git a/source/objects.nodes.components.spheroid.very_simple.bound_functions.Inc b/source/objects.nodes.components.spheroid.very_simple.bound_functions.Inc
index 6eedc1bc96..45159976e1 100644
--- a/source/objects.nodes.components.spheroid.very_simple.bound_functions.Inc
+++ b/source/objects.nodes.components.spheroid.very_simple.bound_functions.Inc
@@ -21,56 +21,25 @@
Contains custom functions for the very simple spheroid component.
!!}
-double precision function Node_Component_Spheroid_Very_Simple_Enclosed_Mass(self,radius,componentType,massType,weightBy,weightIndex)
+double precision function Node_Component_Spheroid_Very_Simple_Half_Mass_Radius(self)
!!{
- Computes the mass within a given radius for an very simple spheroid.
+ Return the half-mass radius of the very simple spheroid.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll, componentTypeSpheroid , massTypeAll , massTypeBaryonic , &
- & massTypeGalactic, massTypeGaseous , massTypeStellar , radiusLarge , &
- & weightByMass , enumerationComponentTypeType, enumerationMassTypeType, enumerationWeightByType
- use :: Error , only : Error_Report
implicit none
- class (nodeComponentSpheroidVerySimple), intent(inout) :: self
- type (enumerationComponentTypeType ), intent(in ) :: componentType
- type (enumerationMassTypeType ), intent(in ) :: massType
- type (enumerationWeightByType ), intent(in ) :: weightBy
- integer , intent(in ) :: weightIndex
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: weightIndex
+ class(nodeComponentSpheroidVerySimple), intent(inout) :: self
- ! Return zero mass if the mass and component types do not match.
- Node_Component_Spheroid_Very_Simple_Enclosed_Mass=0.0d0
- if (.not.(componentType == componentTypeAll .or. componentType == componentTypeSpheroid)) return
- ! Determine mass contributed.
- select case (weightBy%ID)
- case (weightByMass %ID)
- select case (massType%ID)
- case (massTypeAll%ID,massTypeBaryonic%ID,massTypeGalactic%ID)
- Node_Component_Spheroid_Very_Simple_Enclosed_Mass=self%massGas()+self%massStellar()
- case (massTypeGaseous%ID)
- Node_Component_Spheroid_Very_Simple_Enclosed_Mass=self%massGas()
- case (massTypeStellar%ID)
- Node_Component_Spheroid_Very_Simple_Enclosed_Mass= self%massStellar()
- end select
- case default
- call Error_Report('this component does not track luminosity'//{introspection:location})
- end select
- ! Return if no mass.
- if (Node_Component_Spheroid_Very_Simple_Enclosed_Mass <= 0.0d0) return
- ! Return if the total mass was requested.
- if (radius >= radiusLarge) return
- ! Otherwise we have an error.
- call Error_Report('this component does not specify a mass profile'//{introspection:location})
+ Node_Component_Spheroid_Very_Simple_Half_Mass_Radius=self%radius()
return
-end function Node_Component_Spheroid_Very_Simple_Enclosed_Mass
+end function Node_Component_Spheroid_Very_Simple_Half_Mass_Radius
-double precision function Node_Component_Spheroid_Very_Simple_Half_Mass_Radius(self)
+double precision function Node_Component_Spheroid_Very_Simple_Mass_Baryonic(self) result(massBaryonic)
!!{
- Return the half-mass radius of the very simple spheroid.
+ Return the baryonic mass for the very simple spheroid component.
!!}
implicit none
class(nodeComponentSpheroidVerySimple), intent(inout) :: self
- Node_Component_Spheroid_Very_Simple_Half_Mass_Radius=self%radius()
+ massBaryonic=+max(0.0d0,self%massStellar()) &
+ & +max(0.0d0,self%massGas ())
return
-end function Node_Component_Spheroid_Very_Simple_Half_Mass_Radius
+end function Node_Component_Spheroid_Very_Simple_Mass_Baryonic
diff --git a/source/objects.tensors.F90 b/source/objects.tensors.F90
index 6bd318a3e5..829435c2b0 100644
--- a/source/objects.tensors.F90
+++ b/source/objects.tensors.F90
@@ -64,6 +64,7 @@ module Tensors
contains
!![
+
@@ -101,6 +102,7 @@ module Tensors
generic :: operator(/) => Tensor_R2_D3_Sym_Scalar_Divide
generic :: operator(==) => Tensor_R2_D3_Sym_Matrix_Equality
procedure :: nonStaticSizeOf => Tensor_R2_D3_Sym_Non_Static_Size_Of
+ procedure :: element => Tensor_R2_D3_Sym_Element
procedure :: isZero => Tensor_R2_D3_Sym_Is_Zero
procedure :: destroy => Tensor_R2_D3_Sym_Destroy
procedure :: setToIdentity => Tensor_R2_D3_Sym_Set_To_Identity
@@ -201,6 +203,13 @@ module subroutine Tensor_R2_D3_Sym_Set_To_Identity(self)
!!}
class(tensorRank2Dimension3Symmetric), intent(inout) :: self
end subroutine Tensor_R2_D3_Sym_Set_To_Identity
+ module double precision function Tensor_R2_D3_Sym_Element(self,i,j)
+ !!{
+ Return the enumeration element of a {\normalfont \ttfamily tensorRank2Dimension3Symmetric} object.
+ !!}
+ class (tensorRank2Dimension3Symmetric), intent(in) :: self
+ integer , intent(in) :: i , j
+ end function Tensor_R2_D3_Sym_Element
module logical function Tensor_R2_D3_Sym_Is_Zero(self)
!!{
Test whether a {\normalfont \ttfamily tensorRank2Dimension3Symmetric} object is zero.
diff --git a/source/objects.tensors.rank2.dimension3.symmetric.F90 b/source/objects.tensors.rank2.dimension3.symmetric.F90
index aa6973daf8..c116069e83 100644
--- a/source/objects.tensors.rank2.dimension3.symmetric.F90
+++ b/source/objects.tensors.rank2.dimension3.symmetric.F90
@@ -259,6 +259,56 @@ end function tensorRank2Dimension3SymmetricInternal
return
end procedure Tensor_R2_D3_Sym_Is_Zero
+ module procedure Tensor_R2_D3_Sym_Element
+ !!{
+ Return the enumeration element of a {\normalfont \ttfamily tensorRank2Dimension3Symmetric} object.
+ !!}
+ use :: Error, only : Error_Report
+ implicit none
+
+ select case (i)
+ case (0)
+ select case (j)
+ case (0)
+ Tensor_R2_D3_Sym_Element=self%x00
+ return
+ case (1)
+ Tensor_R2_D3_Sym_Element=self%x01
+ return
+ case (2)
+ Tensor_R2_D3_Sym_Element=self%x02
+ return
+ end select
+ case (1)
+ select case (j)
+ case (0)
+ Tensor_R2_D3_Sym_Element=self%x01
+ return
+ case (1)
+ Tensor_R2_D3_Sym_Element=self%x11
+ return
+ case (2)
+ Tensor_R2_D3_Sym_Element=self%x12
+ return
+ end select
+ case (2)
+ select case (j)
+ case (0)
+ Tensor_R2_D3_Sym_Element=self%x02
+ return
+ case (1)
+ Tensor_R2_D3_Sym_Element=self%x12
+ return
+ case (2)
+ Tensor_R2_D3_Sym_Element=self%x22
+ return
+ end select
+ end select
+ Tensor_R2_D3_Sym_Element=0.0d0
+ call Error_Report('invalid indices'//{introspection:location})
+ return
+ end procedure Tensor_R2_D3_Sym_Element
+
module procedure Tensor_R2_D3_Sym_Add
!!{
Add two {\normalfont \ttfamily tensorRank2Dimension3Symmetric} objects.
diff --git a/source/output.analyses.HI_vs_halo_mass_relation.ALFALFA_Padmanabhan_2017.F90 b/source/output.analyses.HI_vs_halo_mass_relation.ALFALFA_Padmanabhan_2017.F90
index b7480dfbb4..c3095bf293 100644
--- a/source/output.analyses.HI_vs_halo_mass_relation.ALFALFA_Padmanabhan_2017.F90
+++ b/source/output.analyses.HI_vs_halo_mass_relation.ALFALFA_Padmanabhan_2017.F90
@@ -37,8 +37,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (outputAnalysisMolecularRatioClass), pointer :: outputAnalysisMolecularRatio_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient
integer (c_size_t ) :: likelihoodBin
contains
@@ -61,7 +59,6 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorParameters(parameters) re
!!}
use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Functions_Global , only : Virial_Density_Contrast_Percolation_Objects_Constructor_
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Output_Analysis_Molecular_Ratios, only : outputAnalysisMolecularRatio , outputAnalysisMolecularRatioClass
@@ -73,8 +70,6 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorParameters(parameters) re
class (outputTimesClass ), pointer :: outputTimes_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (outputAnalysisMolecularRatioClass ), pointer :: outputAnalysisMolecularRatio_
class (* ), pointer :: percolationObjects_
integer (c_size_t ) :: likelihoodBin
@@ -104,25 +99,21 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorParameters(parameters) re
-
-
!!]
percolationObjects_ => Virial_Density_Contrast_Percolation_Objects_Constructor_(parameters)
- self = outputAnalysisHIVsHaloMassRelationPadmanabhan2017(likelihoodBin,systematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,percolationObjects_)
+ self = outputAnalysisHIVsHaloMassRelationPadmanabhan2017(likelihoodBin,systematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputAnalysisMolecularRatio_,outputTimes_,percolationObjects_)
!![
-
-
!!]
return
end function hiVsHaloMassRelationPadmanabhan2017ConstructorParameters
- function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,systematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,percolationObjects_) result (self)
+ function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,systematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputAnalysisMolecularRatio_,outputTimes_,percolationObjects_) result (self)
!!{
Constructor for the ``hiVsHaloMassRelationPadmanabhan2017'' output analysis class for internal use.
!!}
@@ -153,8 +144,6 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,sy
class (cosmologyParametersClass ), intent(inout), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (outputAnalysisMolecularRatioClass ), intent(in ), target :: outputAnalysisMolecularRatio_
class (* ), intent(in ), target :: percolationObjects_
@@ -204,7 +193,7 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,sy
& jacobianVelocity0 , jacobianVelocity1
integer (c_size_t ) :: iBin
!![
-
+
!!]
! Construct survey geometry.
@@ -327,7 +316,7 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,sy
! Create an HI mass weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Create a halo mass weight property extractor. The virial density contrast is chosen to equal that expected for a
! friends-of-friends algorithm with linking length parameter b=0.2 since that is what was used by Sheth, Mo & Tormen (2001) in
@@ -338,7 +327,7 @@ function hiVsHaloMassRelationPadmanabhan2017ConstructorInternal(likelihoodBin,sy
!!]
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a halo scale object from which to compute virial velocities. Padmanabhan & Refrigier use the Bryan & Norman (1998)
! virial density contrast definition. However (Padmanabhan, private communication), they assume it gives the density contrast
@@ -486,9 +475,7 @@ subroutine hiVsHaloMassRelationPadmanabhan2017Destructor(self)
-
-
!!]
return
end subroutine hiVsHaloMassRelationPadmanabhan2017Destructor
diff --git a/source/output.analyses.ICM_Xray_luminosity_temperature.F90 b/source/output.analyses.ICM_Xray_luminosity_temperature.F90
index 6a68f2de04..93cadcd20d 100644
--- a/source/output.analyses.ICM_Xray_luminosity_temperature.F90
+++ b/source/output.analyses.ICM_Xray_luminosity_temperature.F90
@@ -31,13 +31,11 @@
An ICM X-ray luminosity-temperature relation output analysis class.
!!}
private
- double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- double precision :: randomErrorMinimum , randomErrorMaximum
+ double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class (coolingFunctionClass ), pointer :: coolingFunction_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ double precision :: randomErrorMinimum , randomErrorMaximum
contains
final :: icmXrayLuminosityTemperatureDestructor
end type outputAnalysisICMXrayLuminosityTemperature
@@ -56,20 +54,16 @@ function icmXrayLuminosityTemperatureConstructorParameters(parameters) result (s
!!{
Constructor for the ``icmXrayLuminosityTemperature'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Cooling_Functions , only : coolingFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Hot_Halo_Mass_Distributions , only : hotHaloMassDistributionClass
- use :: Hot_Halo_Temperature_Profiles, only : hotHaloTemperatureProfileClass
+ use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Cooling_Functions , only : coolingFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
implicit none
type (outputAnalysisICMXrayLuminosityTemperature) :: self
type (inputParameters ), intent(inout) :: parameters
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient, randomErrorPolynomialCoefficient
class (outputTimesClass ), pointer :: outputTimes_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class (coolingFunctionClass ), pointer :: coolingFunction_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -106,28 +100,24 @@ function icmXrayLuminosityTemperatureConstructorParameters(parameters) result (s
0.05d0
The maximum random error for X-ray temperature.
-
-
-
-
-
-
+
+
+
+
!!]
! Build the object.
- self=outputAnalysisICMXrayLuminosityTemperature(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,outputTimes_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_)
+ self=outputAnalysisICMXrayLuminosityTemperature(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,outputTimes_,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_)
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end function icmXrayLuminosityTemperatureConstructorParameters
- function icmXrayLuminosityTemperatureConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,outputTimes_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,coolingFunction_) result (self)
+ function icmXrayLuminosityTemperatureConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,outputTimes_,cosmologyFunctions_,darkMatterHaloScale_,coolingFunction_) result (self)
!!{
Constructor for the ``icmXrayLuminosityTemperature'' output analysis class for internal use.
!!}
@@ -151,8 +141,6 @@ function icmXrayLuminosityTemperatureConstructorInternal(systematicErrorPolynomi
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class (coolingFunctionClass ), intent(in ), target :: coolingFunction_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.0d08, covarianceBinomialMassHaloMaximum =1.0d16
@@ -178,7 +166,7 @@ function icmXrayLuminosityTemperatureConstructorInternal(systematicErrorPolynomi
& countTemperaturesPerDecade =5.0d0
integer (c_size_t ) :: iOutput , countTemperatures
!![
-
+
!!]
! Construct bins in temperature.
@@ -258,12 +246,12 @@ function icmXrayLuminosityTemperatureConstructorInternal(systematicErrorPolynomi
! Create an X-ray temperature property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create an X-ray luminosity property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build the object.
self%outputAnalysisMeanFunction1D=outputAnalysisMeanFunction1D( &
@@ -328,12 +316,10 @@ subroutine icmXrayLuminosityTemperatureDestructor(self)
type(outputAnalysisICMXrayLuminosityTemperature), intent(inout) :: self
!![
-
-
-
-
-
-
+
+
+
+
!!]
return
end subroutine icmXrayLuminosityTemperatureDestructor
diff --git a/source/output.analyses.Local_Group.mass_metallicity_relation.F90 b/source/output.analyses.Local_Group.mass_metallicity_relation.F90
index 0f5bda5116..51471bf224 100644
--- a/source/output.analyses.Local_Group.mass_metallicity_relation.F90
+++ b/source/output.analyses.Local_Group.mass_metallicity_relation.F90
@@ -34,7 +34,6 @@
private
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable, dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& metallicitySystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -64,7 +63,6 @@ function localGroupMassMetallicityRelationConstructorParameters(parameters) resu
Constructor for the ``localGroupMassMetallicityRelation'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -72,7 +70,6 @@ function localGroupMassMetallicityRelationConstructorParameters(parameters) resu
type (outputAnalysisLocalGroupMassMetallicityRelation) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& metallicitySystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -147,10 +144,9 @@ The maximum halo mass to consider when constructing Local Group ste
var_str('orbital')
The type of position to use in survey geometry filters.
-
-
+
!!]
- self=outputAnalysisLocalGroupMassMetallicityRelation(outputTimes_,galacticStructure_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,metallicitySystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
+ self=outputAnalysisLocalGroupMassMetallicityRelation(outputTimes_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,metallicitySystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
!![
@@ -158,7 +154,7 @@ The maximum halo mass to consider when constructing Local Group ste
return
end function localGroupMassMetallicityRelationConstructorParameters
- function localGroupMassMetallicityRelationConstructorInternal(outputTimes_,galacticStructure_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,metallicitySystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function localGroupMassMetallicityRelationConstructorInternal(outputTimes_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,metallicitySystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``localGroupMassMetallicityRelation'' output analysis class for internal use.
!!}
@@ -189,7 +185,6 @@ function localGroupMassMetallicityRelationConstructorInternal(outputTimes_,galac
& metallicitySystematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (nodePropertyExtractorMassStellar ) , pointer :: nodePropertyExtractor_
type (nodePropertyExtractorMetallicityStellar ) , pointer :: outputAnalysisWeightPropertyExtractor_
type (outputAnalysisPropertyOperatorMetallicitySolarRelative) , pointer :: outputAnalysisWeightPropertyOperatorMetallicity_
@@ -227,7 +222,7 @@ function localGroupMassMetallicityRelationConstructorInternal(outputTimes_,galac
type (localGroupDB ) :: localGroupDB_
double precision :: massesWidthBin
!![
-
+
!!]
! Construct mass bins.
@@ -305,7 +300,7 @@ function localGroupMassMetallicityRelationConstructorInternal(outputTimes_,galac
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a stellar metallicity weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
@@ -506,9 +501,8 @@ subroutine localGroupMassMetallicityRelationDestructor(self)
type(outputAnalysisLocalGroupMassMetallicityRelation), intent(inout) :: self
!![
-
-
-
+
+
!!]
return
end subroutine localGroupMassMetallicityRelationDestructor
diff --git a/source/output.analyses.Local_Group.mass_size_relation.F90 b/source/output.analyses.Local_Group.mass_size_relation.F90
index fc13a436f4..0b6f4d1fb4 100644
--- a/source/output.analyses.Local_Group.mass_size_relation.F90
+++ b/source/output.analyses.Local_Group.mass_size_relation.F90
@@ -34,7 +34,6 @@
private
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& sizeSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -64,7 +63,6 @@ function localGroupMassSizeRelationConstructorParameters(parameters) result(self
Constructor for the ``localGroupMassSizeRelation'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -72,7 +70,6 @@ function localGroupMassSizeRelationConstructorParameters(parameters) result(self
type (outputAnalysisLocalGroupMassSizeRelation) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& sizeSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -148,9 +145,8 @@ function localGroupMassSizeRelationConstructorParameters(parameters) result(self
The type of position to use in survey geometry filters.
-
!!]
- self=outputAnalysisLocalGroupMassSizeRelation(outputTimes_,galacticStructure_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,sizeSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
+ self=outputAnalysisLocalGroupMassSizeRelation(outputTimes_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,sizeSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
!![
@@ -158,7 +154,7 @@ function localGroupMassSizeRelationConstructorParameters(parameters) result(self
return
end function localGroupMassSizeRelationConstructorParameters
- function localGroupMassSizeRelationConstructorInternal(outputTimes_,galacticStructure_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,sizeSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function localGroupMassSizeRelationConstructorInternal(outputTimes_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,sizeSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``localGroupMassSizeRelation'' output analysis class for internal use.
!!}
@@ -187,7 +183,6 @@ function localGroupMassSizeRelationConstructorInternal(outputTimes_,galacticStru
& sizeSystematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (nodePropertyExtractorMassStellar ) , pointer :: nodePropertyExtractor_
type (nodePropertyExtractorRadiusHalfMassStellar ) , pointer :: outputAnalysisWeightPropertyExtractor_
type (outputAnalysisPropertyOperatorSystmtcPolynomial ) , pointer :: outputAnalysisPropertyOperatorSystmtcPolynomial_ , outputAnalysisWeightPropertyOperatorSystmtcPolynomial_
@@ -227,7 +222,7 @@ function localGroupMassSizeRelationConstructorInternal(outputTimes_,galacticStru
type (localGroupDB ) :: localGroupDB_
double precision :: massesWidthBin
!![
-
+
!!]
! Construct mass bins.
@@ -312,12 +307,12 @@ function localGroupMassSizeRelationConstructorInternal(outputTimes_,galacticStru
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a stellar metallicity weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build a size weight property operator.
allocate(outputAnalysisWeightPropertyOperatorSystmtcPolynomial_)
@@ -521,9 +516,8 @@ subroutine localGroupMassSizeRelationDestructor(self)
type(outputAnalysisLocalGroupMassSizeRelation), intent(inout) :: self
!![
-
-
-
+
+
!!]
return
end subroutine localGroupMassSizeRelationDestructor
diff --git a/source/output.analyses.Local_Group.mass_velocity_dispersion_relation.F90 b/source/output.analyses.Local_Group.mass_velocity_dispersion_relation.F90
index dabca5ac6f..a14c27ffaa 100644
--- a/source/output.analyses.Local_Group.mass_velocity_dispersion_relation.F90
+++ b/source/output.analyses.Local_Group.mass_velocity_dispersion_relation.F90
@@ -34,7 +34,6 @@
private
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& velocityDispersionSystematicErrorPolynomialCoefficient
@@ -65,7 +64,6 @@ function localGroupMassVelocityDispersionRelationConstructorParameters(parameter
Constructor for the ``localGroupMassVelocityDispersionRelation'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -73,7 +71,6 @@ function localGroupMassVelocityDispersionRelationConstructorParameters(parameter
type (outputAnalysisLocalGroupMassVelocityDispersionRelation) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& velocityDispersionSystematicErrorPolynomialCoefficient
@@ -150,20 +147,18 @@ function localGroupMassVelocityDispersionRelationConstructorParameters(parameter
The type of position to use in survey geometry filters.
-
!!]
- self=outputAnalysisLocalGroupMassVelocityDispersionRelation(outputTimes_,galacticStructure_,darkMatterHaloScale_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,velocityDispersionSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
+ self=outputAnalysisLocalGroupMassVelocityDispersionRelation(outputTimes_,darkMatterHaloScale_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,velocityDispersionSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
!![
-
!!]
return
end function localGroupMassVelocityDispersionRelationConstructorParameters
- function localGroupMassVelocityDispersionRelationConstructorInternal(outputTimes_,galacticStructure_,darkMatterHaloScale_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,velocityDispersionSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function localGroupMassVelocityDispersionRelationConstructorInternal(outputTimes_,darkMatterHaloScale_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,velocityDispersionSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``localGroupMassVelocityDispersionRelation'' output analysis class for internal use.
!!}
@@ -193,7 +188,6 @@ function localGroupMassVelocityDispersionRelationConstructorInternal(outputTimes
& velocityDispersionSystematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
type (nodePropertyExtractorMassStellar ) , pointer :: nodePropertyExtractor_
type (nodePropertyExtractorScalarizer ) , pointer :: outputAnalysisWeightPropertyScalarizer_
@@ -236,7 +230,7 @@ function localGroupMassVelocityDispersionRelationConstructorInternal(outputTimes
double precision :: massesWidthBin
type (varying_string ) , dimension(1) :: radiusSpecifier
!![
-
+
!!]
! Construct mass bins.
@@ -321,13 +315,13 @@ function localGroupMassVelocityDispersionRelationConstructorInternal(outputTimes
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a velocity dispersion weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
radiusSpecifier(1)=var_str('stellarMassFraction{0.5}:all:galactic:lineOfSight:1.0')
!![
-
+
!!]
allocate(outputAnalysisWeightPropertyScalarizer_ )
!![
@@ -538,7 +532,6 @@ subroutine localGroupMassVelocityDispersionRelationDestructor(self)
!![
-
!!]
return
diff --git a/source/output.analyses.Local_Group.occupation_fraction.F90 b/source/output.analyses.Local_Group.occupation_fraction.F90
index 39be4d1a09..a3c89deec5 100644
--- a/source/output.analyses.Local_Group.occupation_fraction.F90
+++ b/source/output.analyses.Local_Group.occupation_fraction.F90
@@ -34,7 +34,6 @@
private
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& massStellarSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -64,7 +63,6 @@ function localGroupOccupationFractionConstructorParameters(parameters) result(se
Constructor for the ``localGroupOccupationFraction'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -72,7 +70,6 @@ function localGroupOccupationFractionConstructorParameters(parameters) result(se
type (outputAnalysisLocalGroupOccupationFraction) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& massStellarSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -147,10 +144,9 @@ function localGroupOccupationFractionConstructorParameters(parameters) result(se
var_str('orbital')
The type of position to use in survey geometry filters.
-
-
+
!!]
- self=outputAnalysisLocalGroupOccupationFraction(outputTimes_,galacticStructure_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
+ self=outputAnalysisLocalGroupOccupationFraction(outputTimes_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
!![
@@ -158,7 +154,7 @@ function localGroupOccupationFractionConstructorParameters(parameters) result(se
return
end function localGroupOccupationFractionConstructorParameters
- function localGroupOccupationFractionConstructorInternal(outputTimes_,galacticStructure_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function localGroupOccupationFractionConstructorInternal(outputTimes_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``localGroupOccupationFraction'' output analysis class for internal use.
!!}
@@ -188,7 +184,6 @@ function localGroupOccupationFractionConstructorInternal(outputTimes_,galacticSt
& massStellarSystematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (nodePropertyExtractorMassBasic ) , pointer :: nodePropertyExtractor_
type (nodePropertyExtractorMassStellar ) , pointer :: outputAnalysisWeightPropertyExtractor_
type (outputAnalysisPropertyOperatorSystmtcPolynomial ) , pointer :: outputAnalysisPropertyOperatorSystmtcPolynomial_ , outputAnalysisWeightPropertyOperatorSystmtcPolynomial_
@@ -218,7 +213,7 @@ function localGroupOccupationFractionConstructorInternal(outputTimes_,galacticSt
integer (c_size_t ) :: i , bufferCount
type (hdf5Object ) :: fileData
!![
-
+
!!]
! Construct the target distribution.
@@ -243,7 +238,7 @@ function localGroupOccupationFractionConstructorInternal(outputTimes_,galacticSt
! Create a stellar mass weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build a size weight property operator.
allocate(outputAnalysisWeightPropertyOperatorSystmtcPolynomial_)
@@ -446,9 +441,8 @@ subroutine localGroupOccupationFractionDestructor(self)
type(outputAnalysisLocalGroupOccupationFraction), intent(inout) :: self
!![
-
-
-
+
+
!!]
return
end subroutine localGroupOccupationFractionDestructor
diff --git a/source/output.analyses.Local_Group.stellar_mass_function.F90 b/source/output.analyses.Local_Group.stellar_mass_function.F90
index 4c93d2a768..976c9b79e9 100644
--- a/source/output.analyses.Local_Group.stellar_mass_function.F90
+++ b/source/output.analyses.Local_Group.stellar_mass_function.F90
@@ -39,7 +39,6 @@
private
type (outputAnalysisVolumeFunction1D), pointer :: volumeFunctionSatellites => null(), volumeFunctionCentrals => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable, dimension(: ) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
double precision , allocatable, dimension(: ) :: masses , massFunction , &
& massFunctionTarget
@@ -80,7 +79,6 @@ function localGroupStellarMassFunctionConstructorParameters(parameters) result(s
Constructor for the ``localGroupStellarMassFunction'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -88,7 +86,6 @@ function localGroupStellarMassFunctionConstructorParameters(parameters) result(s
type (outputAnalysisLocalGroupStellarMassFunction) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -176,10 +173,9 @@ The maximum halo mass to consider when constructing Local Group ste
logImprobable
The log-likelihood to assign to bins where the model expectation is zero.
-
-
+
!!]
- self=outputAnalysisLocalGroupStellarMassFunction(outputTimes_,galacticStructure_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),negativeBinomialScatterFractional,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,logLikelihoodZero)
+ self=outputAnalysisLocalGroupStellarMassFunction(outputTimes_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),negativeBinomialScatterFractional,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,logLikelihoodZero)
!![
@@ -187,7 +183,7 @@ The maximum halo mass to consider when constructing Local Group ste
return
end function localGroupStellarMassFunctionConstructorParameters
- function localGroupStellarMassFunctionConstructorInternal(outputTimes_,galacticStructure_,positionType,negativeBinomialScatterFractional,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,logLikelihoodZero) result (self)
+ function localGroupStellarMassFunctionConstructorInternal(outputTimes_,positionType,negativeBinomialScatterFractional,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,logLikelihoodZero) result (self)
!!{
Constructor for the ``localGroupStellarMassFunction'' output analysis class for internal use.
!!}
@@ -217,7 +213,6 @@ function localGroupStellarMassFunctionConstructorInternal(outputTimes_,galacticS
double precision , intent(in ), dimension(: ) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (nodePropertyExtractorMassStellar ) , pointer :: nodePropertyExtractor_
type (outputAnalysisPropertyOperatorSystmtcPolynomial ) , pointer :: outputAnalysisPropertyOperatorSystmtcPolynomial_
type (outputAnalysisPropertyOperatorLog10 ) , pointer :: outputAnalysisPropertyOperatorLog10_
@@ -251,7 +246,7 @@ function localGroupStellarMassFunctionConstructorInternal(outputTimes_,galacticS
& bufferCountSatellites
type (localGroupDB ) :: localGroupDB_
!![
-
+
!!]
! Initialize.
@@ -284,7 +279,7 @@ function localGroupStellarMassFunctionConstructorInternal(outputTimes_,galacticS
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create property operators and unoperators to perform conversion to/from logarithmic mass.
allocate(outputAnalysisPropertyOperatorLog10_ )
@@ -523,7 +518,6 @@ subroutine localGroupStellarMassFunctionDestructor(self)
-
!!]
return
end subroutine localGroupStellarMassFunctionDestructor
diff --git a/source/output.analyses.Local_Group.stellar_mass_halo_mass_relation.F90 b/source/output.analyses.Local_Group.stellar_mass_halo_mass_relation.F90
index c0cba16a18..7d6d3db05b 100644
--- a/source/output.analyses.Local_Group.stellar_mass_halo_mass_relation.F90
+++ b/source/output.analyses.Local_Group.stellar_mass_halo_mass_relation.F90
@@ -34,7 +34,6 @@
private
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& massStellarSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -64,7 +63,6 @@ function localGroupStellarMassHaloMassRelationConstructorParameters(parameters)
Constructor for the ``localGroupStellarMassHaloMassRelation'' output analysis class which takes a parameter set as input.
!!}
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Times , only : outputTimes , outputTimesClass
use :: Galactic_Filters , only : enumerationPositionTypeEncode
use :: Models_Likelihoods_Constants, only : logImprobable
@@ -72,7 +70,6 @@ function localGroupStellarMassHaloMassRelationConstructorParameters(parameters)
type (outputAnalysisLocalGroupStellarMassHaloMassRelation) :: self
type (inputParameters ), intent(inout) :: parameters
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& massStellarSystematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -147,10 +144,9 @@ function localGroupStellarMassHaloMassRelationConstructorParameters(parameters)
var_str('orbital')
The type of position to use in survey geometry filters.
-
-
+
!!]
- self=outputAnalysisLocalGroupStellarMassHaloMassRelation(outputTimes_,galacticStructure_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
+ self=outputAnalysisLocalGroupStellarMassHaloMassRelation(outputTimes_,enumerationPositionTypeEncode(positionType,includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum)
!![
@@ -158,7 +154,7 @@ function localGroupStellarMassHaloMassRelationConstructorParameters(parameters)
return
end function localGroupStellarMassHaloMassRelationConstructorParameters
- function localGroupStellarMassHaloMassRelationConstructorInternal(outputTimes_,galacticStructure_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function localGroupStellarMassHaloMassRelationConstructorInternal(outputTimes_,positionType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,massStellarSystematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``localGroupStellarMassHaloMassRelation'' output analysis class for internal use.
!!}
@@ -188,7 +184,6 @@ function localGroupStellarMassHaloMassRelationConstructorInternal(outputTimes_,g
& massStellarSystematicErrorPolynomialCoefficient
type (enumerationPositionTypeType ), intent(in ) :: positionType
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (nodePropertyExtractorMassBasic ) , pointer :: nodePropertyExtractor_
type (nodePropertyExtractorMassStellar ) , pointer :: outputAnalysisWeightPropertyExtractor_
type (outputAnalysisPropertyOperatorSystmtcPolynomial ) , pointer :: outputAnalysisPropertyOperatorSystmtcPolynomial_ , outputAnalysisWeightPropertyOperatorSystmtcPolynomial_
@@ -218,7 +213,7 @@ function localGroupStellarMassHaloMassRelationConstructorInternal(outputTimes_,g
integer (c_size_t ) :: i , bufferCount
type (hdf5Object ) :: fileData
!![
-
+
!!]
! Construct the target distribution.
@@ -247,7 +242,7 @@ function localGroupStellarMassHaloMassRelationConstructorInternal(outputTimes_,g
! Create a stellar mass weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build a size weight property operator.
allocate(outputAnalysisWeightPropertyOperatorSystmtcPolynomial_)
@@ -443,9 +438,8 @@ subroutine localGroupStellarMassHaloMassRelationDestructor(self)
type(outputAnalysisLocalGroupStellarMassHaloMassRelation), intent(inout) :: self
!![
-
-
-
+
+
!!]
return
end subroutine localGroupStellarMassHaloMassRelationDestructor
diff --git a/source/output.analyses.Sunyaev-Zeldovich_Planck2013.F90 b/source/output.analyses.Sunyaev-Zeldovich_Planck2013.F90
index 8ec9dd4427..21647d5508 100644
--- a/source/output.analyses.Sunyaev-Zeldovich_Planck2013.F90
+++ b/source/output.analyses.Sunyaev-Zeldovich_Planck2013.F90
@@ -21,8 +21,6 @@
Implements a thermal Sunyaev-Zeldovich signal vs. stellar mass analysis class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A thermal Sunyaev-Zeldovich signal vs. stellar mass analysis class using the results of \cite{planck_collaboration_planck_2013}.
@@ -33,15 +31,12 @@
A thermal Sunyaev-Zeldovich signal vs stellar mass analysis class using the results of \cite{planck_collaboration_planck_2013}.
!!}
private
- double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_ => null()
- class (hotHaloTemperatureProfileClass), pointer :: hotHaloTemperatureProfile_ => null()
- class (chemicalStateClass ), pointer :: chemicalState_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- double precision :: randomErrorMinimum , randomErrorMaximum
+ double precision , allocatable, dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
+ class (cosmologyParametersClass), pointer :: cosmologyParameters_ => null()
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ class (chemicalStateClass ), pointer :: chemicalState_ => null()
+ double precision :: randomErrorMinimum , randomErrorMaximum
contains
final :: sunyaevZeldovichPlanck2013Destructor
end type outputAnalysisSunyaevZeldovichPlanck2013
@@ -60,8 +55,7 @@ function sunyaevZeldovichPlanck2013ConstructorParameters(parameters) result (sel
!!{
Constructor for the ``sunyaevZeldovichPlanck2013'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisSunyaevZeldovichPlanck2013) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -69,11 +63,8 @@ function sunyaevZeldovichPlanck2013ConstructorParameters(parameters) result (sel
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), pointer :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), pointer :: hotHaloTemperatureProfile_
class (chemicalStateClass ), pointer :: chemicalState_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: randomErrorMinimum , randomErrorMaximum
allocate(systematicErrorPolynomialCoefficient(max(1,parameters%count('systematicErrorPolynomialCoefficient',zeroIfNotPresent=.true.))))
@@ -110,29 +101,23 @@ function sunyaevZeldovichPlanck2013ConstructorParameters(parameters) result (sel
-
-
-
!!]
! Build the object.
- self=outputAnalysisSunyaevZeldovichPlanck2013(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,chemicalState_,outputTimes_,galacticStructure_)
+ self=outputAnalysisSunyaevZeldovichPlanck2013(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,chemicalState_,outputTimes_)
!![
-
-
-
-
-
-
-
-
+
+
+
+
+
!!]
return
end function sunyaevZeldovichPlanck2013ConstructorParameters
- function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,hotHaloMassDistribution_,hotHaloTemperatureProfile_,chemicalState_,outputTimes_,galacticStructure_) result (self)
+ function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloScale_,chemicalState_,outputTimes_) result (self)
!!{
Constructor for the ``sunyaevZeldovichPlanck2013'' output analysis class for internal use.
!!}
@@ -158,11 +143,8 @@ function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomial
class (cosmologyParametersClass ), intent(inout), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (hotHaloMassDistributionClass ), intent(in ), target :: hotHaloMassDistribution_
- class (hotHaloTemperatureProfileClass ), intent(in ), target :: hotHaloTemperatureProfile_
class (chemicalStateClass ), intent(in ), target :: chemicalState_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.000d08, covarianceBinomialMassHaloMaximum=1.0d16
double precision , allocatable , dimension(: ) :: masses , functionValueTarget , &
@@ -190,7 +172,7 @@ function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomial
integer (c_size_t ), parameter :: bufferCount =10
integer (c_size_t ) :: iBin , binCount
!![
-
+
!!]
! Construct the target data.
@@ -314,7 +296,7 @@ function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomial
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a thermal Sunyaev-Zeldovich property extractor.
allocate(outputAnalysisWeightPropertyExtractor_)
@@ -325,10 +307,7 @@ function sunyaevZeldovichPlanck2013ConstructorInternal(systematicErrorPolynomial
& cosmologyParameters_ , &
& cosmologyFunctions_ , &
& darkMatterHaloScale_ , &
- & hotHaloMassDistribution_ , &
- & hotHaloTemperatureProfile_ , &
& chemicalState_ , &
- & galacticStructure_ , &
& densityContrast =500.0d0 , &
& densityContrastRelativeTo=densityCosmologicalCritical, &
& distanceAngular =500.0d0 &
@@ -404,14 +383,11 @@ subroutine sunyaevZeldovichPlanck2013Destructor(self)
type(outputAnalysisSunyaevZeldovichPlanck2013), intent(inout) :: self
!![
-
-
-
-
-
-
-
-
+
+
+
+
+
!!]
return
end subroutine sunyaevZeldovichPlanck2013Destructor
diff --git a/source/output.analyses.black_hole_bulge_relation.F90 b/source/output.analyses.black_hole_bulge_relation.F90
index f9ce39c29e..8ffcf0629d 100644
--- a/source/output.analyses.black_hole_bulge_relation.F90
+++ b/source/output.analyses.black_hole_bulge_relation.F90
@@ -21,8 +21,6 @@
Contains a module which implements a black hole-bulge mass relation analysis class.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
-
!![
A black hole-bulge mass relation output analysis class.
@@ -33,7 +31,6 @@
A black hole-bulge mass relation output analysis class.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -63,7 +60,6 @@ function blackHoleBulgeRelationConstructorParameters(parameters) result (self)
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient, randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -101,20 +97,18 @@ function blackHoleBulgeRelationConstructorParameters(parameters) result (self)
-
!!]
! Build the object.
- self=outputAnalysisBlackHoleBulgeRelation(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,galacticStructure_)
+ self=outputAnalysisBlackHoleBulgeRelation(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_)
!![
-
!!]
return
end function blackHoleBulgeRelationConstructorParameters
- function blackHoleBulgeRelationConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,galacticStructure_) result (self)
+ function blackHoleBulgeRelationConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_) result (self)
!!{
Constructor for the ``blackHoleBulgeRelation'' output analysis class for internal use.
!!}
@@ -140,7 +134,6 @@ function blackHoleBulgeRelationConstructorInternal(systematicErrorPolynomialCoef
double precision , intent(in ), dimension(: ) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(inout), target :: galacticStructure_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.0d08, covarianceBinomialMassHaloMaximum=1.0d16
double precision , allocatable , dimension(: ) :: masses , functionValueTarget , &
@@ -167,7 +160,7 @@ function blackHoleBulgeRelationConstructorInternal(systematicErrorPolynomialCoef
type (hdf5Object ) :: dataFile
type (varying_string ) :: targetLabel
!![
-
+
!!]
!$ call hdf5Access%set()
@@ -285,7 +278,7 @@ function blackHoleBulgeRelationConstructorInternal(systematicErrorPolynomialCoef
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create an ISM metallicity weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
@@ -360,7 +353,6 @@ subroutine blackHoleBulgeRelationDestructor(self)
type(outputAnalysisBlackHoleBulgeRelation), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.black_hole_velocity_dispersion_relation.F90 b/source/output.analyses.black_hole_velocity_dispersion_relation.F90
index 6df2688ab2..40c3664000 100644
--- a/source/output.analyses.black_hole_velocity_dispersion_relation.F90
+++ b/source/output.analyses.black_hole_velocity_dispersion_relation.F90
@@ -24,7 +24,6 @@
!!}
use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -37,7 +36,6 @@
!!}
private
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -68,7 +66,6 @@ function blackHoleVelocityDispersionRelationConstructorParameters(parameters) re
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: randomErrorMinimum , randomErrorMaximum
double precision , parameter :: toleranceRelative =1.0d-3
@@ -107,21 +104,19 @@ function blackHoleVelocityDispersionRelationConstructorParameters(parameters) re
-
!!]
! Build the object.
- self=outputAnalysisBlackHoleVelocityDispersionRelation(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,toleranceRelative=1.0d-3,darkMatterHaloScale_=darkMatterHaloScale_,galacticStructure_=galacticStructure_)
+ self=outputAnalysisBlackHoleVelocityDispersionRelation(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,toleranceRelative=1.0d-3,darkMatterHaloScale_=darkMatterHaloScale_)
!![
-
!!]
return
end function blackHoleVelocityDispersionRelationConstructorParameters
- function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,toleranceRelative,darkMatterHaloScale_,galacticStructure_) result (self)
+ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,toleranceRelative,darkMatterHaloScale_) result (self)
!!{
Constructor for the ``blackHoleVelocityDispersionRelation'' output analysis class for internal use.
!!}
@@ -149,7 +144,6 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
double precision , intent(in ), dimension(: ) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(inout), target :: galacticStructure_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.0d08, covarianceBinomialMassHaloMaximum =1.0d16
@@ -180,7 +174,7 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
type (varying_string ) :: targetLabel
type (varying_string ) , dimension(1 ) :: radiusSpecifiers
!![
-
+
!!]
!$ call hdf5Access%set()
@@ -276,11 +270,11 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
! Build weight property operators.
allocate(outputAnalysisWeightPropertyOperatorLog10_ )
!![
-
+
!!]
allocate(outputAnalysisWeightPropertyOperatorMinMax_ )
!![
-
+
!!]
allocate(weightPropertyOperators_ )
allocate(weightPropertyOperators_%next )
@@ -288,12 +282,12 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
weightPropertyOperators_%next %operator_ => outputAnalysisWeightPropertyOperatorLog10_
allocate(outputAnalysisWeightPropertyOperator_ )
!![
-
+
!!]
! Build anti-log10() property operator.
allocate(outputAnalysisPropertyUnoperator_ )
!![
-
+
!!]
! Create a velocity dispersion property extractor.
allocate(nodePropertyExtractorVelocityDispersion_ )
@@ -301,16 +295,16 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
includeRadii =.false.
integrationFailureIsFatal =.false.
!![
-
+
!!]
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create an ISM metallicity weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build the object.
self%outputAnalysisMeanFunction1D=outputAnalysisMeanFunction1D( &
@@ -363,6 +357,7 @@ function blackHoleVelocityDispersionRelationConstructorInternal(systematicErrorP
+
@@ -380,7 +375,6 @@ subroutine blackHoleVelocityDispersionRelationDestructor(self)
type(outputAnalysisBlackHoleVelocityDispersionRelation), intent(inout) :: self
!![
-
!!]
diff --git a/source/output.analyses.concentration_distribution.CDM.COCO.F90 b/source/output.analyses.concentration_distribution.CDM.COCO.F90
index c8d92fdc97..d4288391e3 100644
--- a/source/output.analyses.concentration_distribution.CDM.COCO.F90
+++ b/source/output.analyses.concentration_distribution.CDM.COCO.F90
@@ -61,7 +61,6 @@ function concentrationDistributionCDMCOCOConstructorParameters(parameters) resul
class (outputTimesClass ), pointer :: outputTimes_
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
integer :: distributionNumber
double precision :: rootVarianceFractionalMinimum
@@ -79,12 +78,11 @@ function concentrationDistributionCDMCOCOConstructorParameters(parameters) resul
-
!!]
- self=outputAnalysisConcentrationDistributionCDMCOCO(distributionNumber,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,outputTimes_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=outputAnalysisConcentrationDistributionCDMCOCO(distributionNumber,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,outputTimes_,virialDensityContrast_)
!![
@@ -92,19 +90,17 @@ function concentrationDistributionCDMCOCOConstructorParameters(parameters) resul
-
!!]
return
end function concentrationDistributionCDMCOCOConstructorParameters
- function concentrationDistributionCDMCOCOConstructorInternal(distributionNumber,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,outputTimes_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function concentrationDistributionCDMCOCOConstructorInternal(distributionNumber,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,outputTimes_,virialDensityContrast_) result(self)
!!{
Internal constructor for the ``concentrationDistributionCDMCOCO'' output analysis class.
!!}
use :: Error , only : Error_Report
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Input_Paths , only : inputPath , pathTypeDataStatic
use :: Output_Times , only : outputTimesClass
use :: Statistics_NBody_Halo_Mass_Errors, only : nbodyHaloMassErrorClass
@@ -115,7 +111,6 @@ function concentrationDistributionCDMCOCOConstructorInternal(distributionNumber,
class (cosmologyParametersClass ), target , intent(in ) :: cosmologyParameters_
class (cosmologyFunctionsClass ), target , intent(inout) :: cosmologyFunctions_
class (virialDensityContrastClass ), target , intent(in ) :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), target , intent(in ) :: darkMatterProfileDMO_
class (outputTimesClass ), target , intent(inout) :: outputTimes_
class (nbodyHaloMassErrorClass ), target , intent(in ) :: nbodyHaloMassError_
integer , intent(in ) :: distributionNumber
@@ -151,7 +146,6 @@ function concentrationDistributionCDMCOCOConstructorInternal(distributionNumber,
& cosmologyParameters_ , &
& cosmologyFunctions_ , &
& nbodyHaloMassError_ , &
- & darkMatterProfileDMO_ , &
& virialDensityContrast_ , &
& virialDensityContrastDefinition_ , &
& outputTimes_ &
diff --git a/source/output.analyses.concentration_distribution.F90 b/source/output.analyses.concentration_distribution.F90
index 4aea35718b..80af2ac900 100644
--- a/source/output.analyses.concentration_distribution.F90
+++ b/source/output.analyses.concentration_distribution.F90
@@ -34,7 +34,6 @@ A concentration distribution function output analysis class. null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrastDefinition_ => null(), virialDensityContrast_ => null()
double precision :: rootVarianceFractionalMinimum , redshift , &
@@ -71,7 +70,6 @@ function concentrationDistributionConstructorParameters(parameters) result (self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (outputTimesClass ), pointer :: outputTimes_
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
class (virialDensityContrastClass ), pointer :: virialDensityContrastDefinition_, virialDensityContrast_
@@ -91,7 +89,6 @@ function concentrationDistributionConstructorParameters(parameters) result (self
-
@@ -119,7 +116,7 @@ The name of the file from which to read concentration distribution
A label for this analysis.
!!]
- self=outputAnalysisConcentrationDistribution(char(fileName),label,comment,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_)
+ self=outputAnalysisConcentrationDistribution(char(fileName),label,comment,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_)
else
!![
@@ -231,7 +228,6 @@ The target function covariance for likelihood calculations.The target function covariance for likelihood calculations.
-
!!]
return
end function concentrationDistributionConstructorParameters
- function concentrationDistributionConstructorFile(fileName,label,comment,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_) result(self)
+ function concentrationDistributionConstructorFile(fileName,label,comment,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_) result(self)
!!{
Constructor for the ``progenitorMassFunction'' output analysis class which reads all required properties from file.
!!}
@@ -275,7 +270,6 @@ function concentrationDistributionConstructorFile(fileName,label,comment,rootVar
class (outputTimesClass ), intent(inout) :: outputTimes_
class (cosmologyParametersClass ), intent(in ) :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout) :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ) :: darkMatterProfileDMO_
class (nbodyHaloMassErrorClass ), intent(in ) :: nbodyHaloMassError_
class (virialDensityContrastClass ), intent(in ) :: virialDensityContrastDefinition_, virialDensityContrast_
double precision , allocatable , dimension(: ) :: functionValueTarget , concentration
@@ -322,14 +316,14 @@ function concentrationDistributionConstructorFile(fileName,label,comment,rootVar
! Convert redshift to time.
time=cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift))
! Build the object.
- self=outputAnalysisConcentrationDistribution(label,comment,time,massMinimum,massMaximum,concentration(1),concentration(size(concentration)),size(concentration,kind=c_size_t),timeRecent,massParticle,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget)
+ self=outputAnalysisConcentrationDistribution(label,comment,time,massMinimum,massMaximum,concentration(1),concentration(size(concentration)),size(concentration,kind=c_size_t),timeRecent,massParticle,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget)
!![
!!]
return
end function concentrationDistributionConstructorFile
- function concentrationDistributionConstructorInternal(label,comment,time,massMinimum,massMaximum,concentrationMinimum,concentrationMaximum,countConcentrations,timeRecent,massParticle,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
+ function concentrationDistributionConstructorInternal(label,comment,time,massMinimum,massMaximum,concentrationMinimum,concentrationMaximum,countConcentrations,timeRecent,massParticle,rootVarianceFractionalMinimum,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,virialDensityContrast_,virialDensityContrastDefinition_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
!!{
Internal constructor for the ``concentrationDistribution'' output analysis class.
!!}
@@ -353,7 +347,6 @@ function concentrationDistributionConstructorInternal(label,comment,time,massMin
type (varying_string ) , intent(in ) :: label , comment
class (cosmologyParametersClass ), target , intent(in ) :: cosmologyParameters_
class (cosmologyFunctionsClass ), target , intent(in ) :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), target , intent(in ) :: darkMatterProfileDMO_
class (outputTimesClass ), target , intent(inout) :: outputTimes_
class (nbodyHaloMassErrorClass ), target , intent(in ) :: nbodyHaloMassError_
class (virialDensityContrastClass ), target , intent(in ) :: virialDensityContrastDefinition_ , virialDensityContrast_
@@ -393,7 +386,7 @@ function concentrationDistributionConstructorInternal(label,comment,time,massMin
double precision , parameter :: covarianceBinomialMassHaloMinimum = +3.000d+11, covarianceBinomialMassHaloMaximum=1.0d15
integer (c_size_t ) :: iOutput , bufferCount
!![
-
+
!!]
! Set parameters needed for descriptor.
@@ -479,7 +472,6 @@ function concentrationDistributionConstructorInternal(label,comment,time,massMin
& .false. , &
& cosmologyParameters_ , &
& cosmologyFunctions_ , &
- & darkMatterProfileDMO_ , &
& virialDensityContrast_ , &
& virialDensityContrastDefinition_ &
& )
@@ -495,7 +487,6 @@ function concentrationDistributionConstructorInternal(label,comment,time,massMin
& .false. , &
& cosmologyFunctions_ , &
& cosmologyParameters_ , &
- & darkMatterProfileDMO_ , &
& virialDensityContrast_ , &
& virialDensityContrastDefinition_ &
& )
@@ -681,7 +672,6 @@ subroutine concentrationDistributionDestructor(self)
-
!!]
diff --git a/source/output.analyses.concentration_vs_mass_relation.CDM.Ludlow_2016.F90 b/source/output.analyses.concentration_vs_mass_relation.CDM.Ludlow_2016.F90
index c839fbe7ba..97f7af5beb 100644
--- a/source/output.analyses.concentration_vs_mass_relation.CDM.Ludlow_2016.F90
+++ b/source/output.analyses.concentration_vs_mass_relation.CDM.Ludlow_2016.F90
@@ -36,7 +36,6 @@
class(cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class(virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class(nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
contains
final :: concentrationVsHaloMassCDMLudlow2016Destructor
@@ -65,7 +64,6 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorParameters(parameters) r
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(virialDensityContrastClass ), pointer :: virialDensityContrast_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(outputTimesClass ), pointer :: outputTimes_
class(nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
@@ -75,9 +73,8 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorParameters(parameters) r
-
!!]
- self=outputAnalysisConcentrationVsHaloMassCDMLudlow2016(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,nbodyHaloMassError_,outputTimes_)
+ self=outputAnalysisConcentrationVsHaloMassCDMLudlow2016(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,nbodyHaloMassError_,outputTimes_)
!![
!!]
@@ -90,13 +87,12 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorParameters(parameters) r
-
!!]
return
end function concentrationVsHaloMassCDMLudlow2016ConstructorParameters
- function concentrationVsHaloMassCDMLudlow2016ConstructorInternal(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,nbodyHaloMassError_,outputTimes_) result (self)
+ function concentrationVsHaloMassCDMLudlow2016ConstructorInternal(cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,nbodyHaloMassError_,outputTimes_) result (self)
!!{
Constructor for the ``concentrationVsHaloMassCDMLudlow2016'' output analysis class for internal use.
!!}
@@ -123,7 +119,6 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorInternal(cosmologyParame
class (cosmologyParametersClass ), target , intent(in ) :: cosmologyParameters_
class (cosmologyFunctionsClass ), target , intent(in ) :: cosmologyFunctions_
class (virialDensityContrastClass ), target , intent(in ) :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), target , intent(in ) :: darkMatterProfileDMO_
class (nbodyHaloMassErrorClass ), target , intent(in ) :: nbodyHaloMassError_
class (outputTimesClass ), target , intent(inout) :: outputTimes_
integer (c_size_t ), parameter :: massHaloCount =26
@@ -146,7 +141,7 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorInternal(cosmologyParame
integer (c_size_t ) :: iOutput
type (hdf5Object ) :: dataFile
!![
-
+
!!]
! Construct mass bins matched to those used by Ludlow et al. (2016).
@@ -176,28 +171,28 @@ function concentrationVsHaloMassCDMLudlow2016ConstructorInternal(cosmologyParame
galacticFilterAll_ = galacticFilterAll ( filters_)
! Build N-body mass error distribution operator.
allocate(outputAnalysisDistributionOperator_ )
- outputAnalysisDistributionOperator_ = outputAnalysisDistributionOperatorRndmErrNbodyMass(nbodyHaloMassError_ )
+ outputAnalysisDistributionOperator_ = outputAnalysisDistributionOperatorRndmErrNbodyMass(nbodyHaloMassError_ )
! Build identity weight operator.
allocate(outputAnalysisWeightOperator_ )
- outputAnalysisWeightOperator_ = outputAnalysisWeightOperatorIdentity ( )
+ outputAnalysisWeightOperator_ = outputAnalysisWeightOperatorIdentity ( )
! Build log10() property operator.
allocate(outputAnalysisPropertyOperator_ )
- outputAnalysisPropertyOperator_ = outputAnalysisPropertyOperatorLog10 ( )
+ outputAnalysisPropertyOperator_ = outputAnalysisPropertyOperatorLog10 ( )
! Build a log10 weight property operators.
allocate(outputAnalysisWeightPropertyOperator_ )
- outputAnalysisWeightPropertyOperator_ = outputAnalysisPropertyOperatorLog10 ( )
+ outputAnalysisWeightPropertyOperator_ = outputAnalysisPropertyOperatorLog10 ( )
! Build anti-log10() property operator.
allocate(outputAnalysisPropertyUnoperator_ )
- outputAnalysisPropertyUnoperator_ = outputAnalysisPropertyOperatorAntiLog10 ( )
+ outputAnalysisPropertyUnoperator_ = outputAnalysisPropertyOperatorAntiLog10 ( )
! Create a virial density contrast object matched to the definition used by Ludlow et al. (2016).
allocate(virialDensityContrastDefinition_ )
- virialDensityContrastDefinition_ = virialDensityContrastFixed (200.0d0,fixedDensityTypeCritical,2.0d0,cosmologyParameters_,cosmologyFunctions_ )
+ virialDensityContrastDefinition_ = virialDensityContrastFixed (200.0d0,fixedDensityTypeCritical,2.0d0,cosmologyParameters_,cosmologyFunctions_ )
! Create a concentration weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
- outputAnalysisWeightPropertyExtractor_ = nodePropertyExtractorConcentration (.false.,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ outputAnalysisWeightPropertyExtractor_ = nodePropertyExtractorConcentration (.false.,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,virialDensityContrastDefinition_)
! Create a halo mass property extractor.
allocate(nodePropertyExtractor_ )
- nodePropertyExtractor_ = nodePropertyExtractorMassHalo (.false.,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_)
+ nodePropertyExtractor_ = nodePropertyExtractorMassHalo (.false.,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_)
! Build the object.
self%outputAnalysisMeanFunction1D=outputAnalysisMeanFunction1D( &
& var_str('concentrationHaloMassRelationCDMLudlow2016'), &
@@ -255,7 +250,6 @@ subroutine concentrationVsHaloMassCDMLudlow2016Destructor(self)
-
!!]
return
diff --git a/source/output.analyses.correlation_function.F90 b/source/output.analyses.correlation_function.F90
index 70cff77c59..d0c8191901 100644
--- a/source/output.analyses.correlation_function.F90
+++ b/source/output.analyses.correlation_function.F90
@@ -21,19 +21,20 @@
Contains a module which implements a generic two-point correlation function output analysis class.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBias , darkMatterHaloBiasClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMO , darkMatterProfileDMOClass
- use :: Galactic_Filters , only : galacticFilter , galacticFilterClass
- use :: Geometry_Surveys , only : surveyGeometry , surveyGeometryClass
- use :: Halo_Model_Power_Spectrum_Modifiers , only : haloModelPowerSpectrumModifier , haloModelPowerSpectrumModifierClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBiasClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
+ use :: Galactic_Filters , only : galacticFilterClass
+ use :: Geometry_Surveys , only : surveyGeometryClass
+ use :: Halo_Model_Power_Spectrum_Modifiers , only : haloModelPowerSpectrumModifierClass
use , intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Node_Property_Extractors , only : nodePropertyExtractor , nodePropertyExtractorClass
+ use :: Node_Property_Extractors , only : nodePropertyExtractorClass
!$ use :: OMP_Lib , only : omp_lock_kind
- use :: Output_Analysis_Distribution_Operators, only : outputAnalysisDistributionOperator, outputAnalysisDistributionOperatorClass
- use :: Output_Analysis_Property_Operators , only : outputAnalysisPropertyOperator , outputAnalysisPropertyOperatorClass
- use :: Output_Times , only : outputTimes , outputTimesClass
- use :: Power_Spectra , only : powerSpectrum , powerSpectrumClass
+ use :: Output_Analysis_Distribution_Operators, only : outputAnalysisDistributionOperatorClass
+ use :: Output_Analysis_Property_Operators , only : outputAnalysisPropertyOperatorClass
+ use :: Output_Times , only : outputTimesClass
+ use :: Power_Spectra , only : powerSpectrumClass
!![
@@ -79,6 +80,7 @@ A generic two-point correlation function output analysis class.
class (nodePropertyExtractorClass ), pointer :: massPropertyExtractor_ => null()
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_ => null()
class (powerSpectrumClass ), pointer :: powerSpectrum_ => null()
double precision , allocatable, dimension(: ) :: separations , wavenumber , &
@@ -150,6 +152,7 @@ function correlationFunctionConstructorParameters(parameters) result(self)
class (nodePropertyExtractorClass ), pointer :: massPropertyExtractor_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_
class (powerSpectrumClass ), pointer :: powerSpectrum_
double precision , allocatable , dimension(: ) :: separations , massMinima , &
@@ -287,6 +290,7 @@ The target function covariance for likelihood calculations.
+
@@ -316,6 +320,7 @@ The target function covariance for likelihood calculations.The target function covariance for likelihood calculations.
+
@@ -346,7 +352,7 @@ The target function covariance for likelihood calculations.
+
!!]
! Assign 1D versions of target for use in descriptor.
@@ -507,6 +515,7 @@ subroutine correlationFunctionDestructor(self)
!![
+
@@ -612,15 +621,17 @@ subroutine correlationFunctionAccumulateHalo(self,indexOutput,node)
!!}
use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
use :: Halo_Model_Power_Spectrum_Modifiers, only : haloModelTermOneHalo , haloModelTermTwoHalo
+ use :: Mass_Distributions , only : massDistributionClass
use :: Math_Distributions_Poisson_Binomial, only : Poisson_Binomial_Distribution_Mean, Poisson_Binomial_Distribution_Mean_Pairs , Poisson_Binomial_Distribution_Mean_Pairs_Jacobian
use :: Output_Analyses_Options , only : outputAnalysisPropertyTypeLinear , enumerationOutputAnalysisPropertyTypeType
use :: Vectors , only : Vector_Outer_Product
- use :: Linear_Algebra , only : assignment(=) , matrix , operator(*)
+ use :: Linear_Algebra , only : assignment(=) , matrix , operator(*)
implicit none
class (outputAnalysisCorrelationFunction ), intent(inout) :: self
integer (c_size_t ), intent(in ) :: indexOutput
type (treeNode ), intent(inout) :: node
class (nodeComponentBasic ), pointer :: basic , basicRoot
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , dimension(self%wavenumberCount,self%massCount) :: oneHaloTerm , twoHaloTerm
double precision , dimension( self%massCount) :: galaxyDensity
logical , dimension( self%massCount) :: oneHaloTermActive , twoHaloTermActive
@@ -630,7 +641,8 @@ subroutine correlationFunctionAccumulateHalo(self,indexOutput,node)
& fourierProfile , wavenumber
double precision :: countSatellitePairsMean , countSatellitesMean , &
& haloWeightOutput , expansionFactor , &
- & biasHalo , massHalo
+ & biasHalo , massHalo , &
+ & radiusVirial
integer (c_size_t ) :: i , j , &
& indexOneHalo , indexTwoHalo , &
& indexDensity
@@ -642,7 +654,9 @@ subroutine correlationFunctionAccumulateHalo(self,indexOutput,node)
! 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. We include the weighting by the square-root of the power spectrum here.
- expansionFactor=self%cosmologyFunctions_%expansionFactor(self%outputTimes_%time(indexOutput))
+ massDistribution_ => self%darkMatterProfileDMO_%get ( node )
+ radiusVirial = self%darkMatterHaloScale_ %radiusVirial ( node )
+ expansionFactor = self%cosmologyFunctions_ %expansionFactor(self%outputTimes_%time(indexOutput))
allocate(wavenumber (self%wavenumberCount))
allocate(fourierProfile(self%wavenumberCount))
do i=1,self%wavenumberCount
@@ -650,14 +664,17 @@ subroutine correlationFunctionAccumulateHalo(self,indexOutput,node)
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 &
- & ) &
- & *sqrt( &
- & +self%powerSpectrum_%power (self%wavenumber(i ),self%outputTimes_%time(indexOutput)) &
+ fourierProfile(i)=+ massDistribution_%fourierTransform( &
+ & radiusVirial , &
+ & wavenumber (i)/expansionFactor &
+ & ) &
+ & *sqrt( &
+ & +self%powerSpectrum_%power (self%wavenumber (i),self%outputTimes_%time(indexOutput)) &
& )
end do
+ !![
+
+ !!]
! Get the mass of this halo.
basic => node %basic()
massHalo = basic%mass ()
diff --git a/source/output.analyses.correlation_function.Hearin2014_SDSS.F90 b/source/output.analyses.correlation_function.Hearin2014_SDSS.F90
index 3488171d1e..f4f6dad580 100644
--- a/source/output.analyses.correlation_function.Hearin2014_SDSS.F90
+++ b/source/output.analyses.correlation_function.Hearin2014_SDSS.F90
@@ -31,11 +31,8 @@ A correlation function output analysis class for the \cite{hearin_d
A correlation function function output analysis class for the \cite{hearin_dark_2013} analysis.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
double precision , allocatable, dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
- contains
- final :: correlationFunctionHearin2013SDSSDestructor
end type outputAnalysisCorrelationFunctionHearin2013SDSS
interface outputAnalysisCorrelationFunctionHearin2013SDSS
@@ -53,8 +50,7 @@ function correlationFunctionHearin2013SDSSConstructorParameters(parameters) resu
Constructor for the ``correlationFunctionHearin2013SDSS'' output analysis class which takes a parameter set as input.
!!}
use, intrinsic :: ISO_C_Binding , only : c_size_t
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters , only : inputParameter, inputParameters
implicit none
type (outputAnalysisCorrelationFunctionHearin2013SDSS) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -62,9 +58,9 @@ function correlationFunctionHearin2013SDSSConstructorParameters(parameters) resu
class (outputTimesClass ), pointer :: outputTimes_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (haloModelPowerSpectrumModifierClass ), pointer :: haloModelPowerSpectrumModifier_
class (powerSpectrumClass ), pointer :: powerSpectrum_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient, systematicErrorPolynomialCoefficient
double precision :: massHaloMinimum , massHaloMaximum , &
& randomErrorMinimum , randomErrorMaximum
@@ -132,25 +128,25 @@ The maximum halo mass to consider when constructing the mass functi
+
-
!!]
- self=outputAnalysisCorrelationFunctionHearin2013SDSS(massHaloBinsPerDecade,massHaloMinimum, massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_,galacticStructure_)
+ self=outputAnalysisCorrelationFunctionHearin2013SDSS(massHaloBinsPerDecade,massHaloMinimum, massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,darkMatterHaloScale_,haloModelPowerSpectrumModifier_,powerSpectrum_)
!![
+
-
!!]
return
end function correlationFunctionHearin2013SDSSConstructorParameters
- function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,haloModelPowerSpectrumModifier_,powerSpectrum_,galacticStructure_) result (self)
+ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDecade,massHaloMinimum,massHaloMaximum,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,cosmologyFunctions_,outputTimes_,darkMatterProfileDMO_,darkMatterHaloBias_,darkMatterHaloScale_,haloModelPowerSpectrumModifier_,powerSpectrum_) result (self)
!!{
Constructor for the ``correlationFunctionHearin2013SDSS'' output analysis class for internal use.
!!}
@@ -174,9 +170,9 @@ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDec
class (outputTimesClass ), intent(in ), target :: outputTimes_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (haloModelPowerSpectrumModifierClass ), intent(in ), target :: haloModelPowerSpectrumModifier_
class (powerSpectrumClass ), intent(in ), target :: powerSpectrum_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (cosmologyParametersSimple ), pointer :: cosmologyParametersData_
type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctionsData_
type (galacticFilterStellarMass ), pointer :: galacticFilter_
@@ -194,7 +190,7 @@ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDec
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.
@@ -233,7 +229,7 @@ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDec
! Stellar mass property extractor.
allocate(massPropertyExtractor_ )
!![
-
+
!!]
! Sequence of property operators to correct for cosmological model, convert to logarithm, and apply systematic errors.
allocate(massPropertyOperatorCsmlgyLmnstyDstnc_)
@@ -297,6 +293,7 @@ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDec
& outputTimes_ , &
& darkMatterProfileDMO_ , &
& darkMatterHaloBias_ , &
+ & darkMatterHaloScale_ , &
& haloModelPowerSpectrumModifier_ , &
& powerSpectrum_ , &
& massDistributionOperator_ , &
@@ -321,16 +318,3 @@ function correlationFunctionHearin2013SDSSConstructorInternal(massHaloBinsPerDec
nullify(propertyOperators_)
return
end function correlationFunctionHearin2013SDSSConstructorInternal
-
- subroutine correlationFunctionHearin2013SDSSDestructor(self)
- !!{
- Destructor for the ``correlationFunctionHearin2013SDSS'' output analysis class.
- !!}
- implicit none
- type(outputAnalysisCorrelationFunctionHearin2013SDSS), intent(inout) :: self
-
- !![
-
- !!]
- return
- end subroutine correlationFunctionHearin2013SDSSDestructor
diff --git a/source/output.analyses.galaxy_sizes_SDSS.F90 b/source/output.analyses.galaxy_sizes_SDSS.F90
index b6cd420b2d..bb1844f221 100644
--- a/source/output.analyses.galaxy_sizes_SDSS.F90
+++ b/source/output.analyses.galaxy_sizes_SDSS.F90
@@ -46,7 +46,6 @@ sizes and masses are then used to construct a mass-dependent radius function by
double precision :: massStellarRatio , sizeSourceLensing
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (gravitationalLensingClass), pointer :: gravitationalLensing_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
contains
final :: galaxySizesSDSSDestructor
end type outputAnalysisGalaxySizesSDSS
@@ -65,15 +64,13 @@ function galaxySizesSDSSConstructorParameters(parameters) result (self)
!!{
Constructor for the ``galaxySizesSDSS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisGalaxySizesSDSS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: massStellarRatio , sizeSourceLensing
integer :: distributionNumber
@@ -99,20 +96,18 @@ function galaxySizesSDSSConstructorParameters(parameters) result (self)
-
!!]
- self=outputAnalysisGalaxySizesSDSS(distributionNumber,massStellarRatio,sizeSourceLensing,cosmologyFunctions_,outputTimes_,gravitationalLensing_,galacticStructure_)
+ self=outputAnalysisGalaxySizesSDSS(distributionNumber,massStellarRatio,sizeSourceLensing,cosmologyFunctions_,outputTimes_,gravitationalLensing_)
!![
-
!!]
return
end function galaxySizesSDSSConstructorParameters
- function galaxySizesSDSSConstructorInternal(distributionNumber,massStellarRatio,sizeSourceLensing,cosmologyFunctions_,outputTimes_,gravitationalLensing_,galacticStructure_) result(self)
+ function galaxySizesSDSSConstructorInternal(distributionNumber,massStellarRatio,sizeSourceLensing,cosmologyFunctions_,outputTimes_,gravitationalLensing_) result(self)
!!{
Internal constructor for the ``galaxySizesSDSS'' output analysis class.
!!}
@@ -146,7 +141,6 @@ function galaxySizesSDSSConstructorInternal(distributionNumber,massStellarRatio,
class (cosmologyFunctionsClass ), target , intent(in ) :: cosmologyFunctions_
class (outputTimesClass ), target , intent(inout) :: outputTimes_
class (gravitationalLensingClass ), target , intent(in ) :: gravitationalLensing_
- class (galacticStructureClass ), target , intent(in ) :: galacticStructure_
type (cosmologyParametersSimple ), pointer :: cosmologyParametersData
type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctionsData
type (nodePropertyExtractorRadiusHalfMassStellar ), pointer :: nodePropertyExtractor_
@@ -191,7 +185,7 @@ function galaxySizesSDSSConstructorInternal(distributionNumber,massStellarRatio,
type (varying_string ) :: description
logical :: isLateType
!![
-
+
!!]
! Validate input.
@@ -257,12 +251,12 @@ function galaxySizesSDSSConstructorInternal(distributionNumber,massStellarRatio,
! Create a half-mass radius property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a stellar mass property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Create multiply, log10, cosmological angular distance, and cosmological luminosity distance property operators.
allocate(outputAnalysisPropertyOperatorMultiply_ )
@@ -506,7 +500,6 @@ subroutine galaxySizesSDSSDestructor(self)
!![
-
!!]
return
end subroutine galaxySizesSDSSDestructor
diff --git a/source/output.analyses.mass_function_HI.ALFALFA_Martin2010.F90 b/source/output.analyses.mass_function_HI.ALFALFA_Martin2010.F90
index 09e3203c10..1701ca6de8 100644
--- a/source/output.analyses.mass_function_HI.ALFALFA_Martin2010.F90
+++ b/source/output.analyses.mass_function_HI.ALFALFA_Martin2010.F90
@@ -62,7 +62,6 @@ function massFunctionHIALFALFAMartin2010ConstructorParameters(parameters) result
Constructor for the ``massFunctionHIALFALFAMartin2010'' output analysis class which takes a parameter set as input.
!!}
use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Output_Analysis_Molecular_Ratios, only : outputAnalysisMolecularRatio, outputAnalysisMolecularRatioClass
implicit none
@@ -74,7 +73,6 @@ function massFunctionHIALFALFAMartin2010ConstructorParameters(parameters) result
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
class (outputAnalysisMolecularRatioClass ), pointer :: outputAnalysisMolecularRatio_
class (outputAnalysisDistributionOperatorClass ), pointer :: outputAnalysisDistributionOperatorRandomError_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
double precision :: covarianceBinomialMassHaloMinimum , covarianceBinomialMassHaloMaximum, &
@@ -128,10 +126,9 @@ The maximum halo mass to consider when constructing ALFALFA HI mass
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionHIALFALFAMartin2010(cosmologyFunctions_,cosmologyParameters_,outputAnalysisDistributionOperatorRandomError_,outputAnalysisMolecularRatio_,gravitationalLensing_,outputTimes_,galacticStructure_,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionHIALFALFAMartin2010(cosmologyFunctions_,cosmologyParameters_,outputAnalysisDistributionOperatorRandomError_,outputAnalysisMolecularRatio_,gravitationalLensing_,outputTimes_,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
@@ -140,12 +137,11 @@ The maximum halo mass to consider when constructing ALFALFA HI mass
-
!!]
return
end function massFunctionHIALFALFAMartin2010ConstructorParameters
- function massFunctionHIALFALFAMartin2010ConstructorInternal(cosmologyFunctions_,cosmologyParameters_,outputAnalysisDistributionOperatorRandomError_,outputAnalysisMolecularRatio_,gravitationalLensing_,outputTimes_,galacticStructure_,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionHIALFALFAMartin2010ConstructorInternal(cosmologyFunctions_,cosmologyParameters_,outputAnalysisDistributionOperatorRandomError_,outputAnalysisMolecularRatio_,gravitationalLensing_,outputTimes_,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionHIALFALFAMartin2010'' output analysis class for internal use.
!!}
@@ -166,7 +162,6 @@ function massFunctionHIALFALFAMartin2010ConstructorInternal(cosmologyFunctions_,
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
class (outputAnalysisMolecularRatioClass ), intent(in ), target :: outputAnalysisMolecularRatio_
class (outputAnalysisDistributionOperatorClass ), intent(in ), target :: outputAnalysisDistributionOperatorRandomError_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: sizeSourceLensing
double precision , intent(in ), dimension(:) :: systematicErrorPolynomialCoefficient
integer , intent(in ) :: covarianceBinomialBinsPerDecade
@@ -265,7 +260,6 @@ function massFunctionHIALFALFAMartin2010ConstructorInternal(cosmologyFunctions_,
& outputAnalysisDistributionOperator_ , &
& outputAnalysisMolecularRatio_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
diff --git a/source/output.analyses.mass_function_HI.F90 b/source/output.analyses.mass_function_HI.F90
index cf0905d481..4ccc322b77 100644
--- a/source/output.analyses.mass_function_HI.F90
+++ b/source/output.analyses.mass_function_HI.F90
@@ -36,7 +36,6 @@ An HI mass function output analysis class.
private
class (surveyGeometryClass ), pointer :: surveyGeometry_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null(), cosmologyFunctionsData => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (outputAnalysisMolecularRatioClass), pointer :: outputAnalysisMolecularRatio_ => null()
double precision , allocatable, dimension(:) :: masses
contains
@@ -60,7 +59,6 @@ function massFunctionHIConstructorParameters(parameters) result (self)
!!}
use :: Error , only : Error_Report
use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure , only : galacticStructureClass
use :: Output_Analysis_Molecular_Ratios, only : outputAnalysisMolecularRatio, outputAnalysisMolecularRatioClass
implicit none
type (outputAnalysisMassFunctionHI ) :: self
@@ -69,7 +67,6 @@ function massFunctionHIConstructorParameters(parameters) result (self)
class (surveyGeometryClass ), pointer :: surveyGeometry_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ , cosmologyFunctionsData
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (outputAnalysisDistributionOperatorClass), pointer :: outputAnalysisDistributionOperator_
class (outputAnalysisPropertyOperatorClass ), pointer :: outputAnalysisPropertyOperator_
class (outputAnalysisMolecularRatioClass ), pointer :: outputAnalysisMolecularRatio_
@@ -168,9 +165,8 @@ The target function covariance for likelihood calculations.
-
- self=outputAnalysisMassFunctionHI(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisMassFunctionHI(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -184,12 +180,11 @@ The target function covariance for likelihood calculations.
-
!!]
return
end function massFunctionHIConstructorParameters
- function massFunctionHIConstructorFile(label,comment,fileName,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function massFunctionHIConstructorFile(label,comment,fileName,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``massFunctionHI'' output analysis class which reads bin information from a standard format file.
!!}
@@ -207,7 +202,6 @@ function massFunctionHIConstructorFile(label,comment,fileName,galacticFilter_,su
class (outputAnalysisPropertyOperatorClass ), intent(inout) , target :: outputAnalysisPropertyOperator_
class (outputAnalysisDistributionOperatorClass), intent(in ) , target :: outputAnalysisDistributionOperator_
class (outputAnalysisMolecularRatioClass ), intent(in ) , target :: outputAnalysisMolecularRatio_
- class (galacticStructureClass ), intent(in ) , target :: galacticStructure_
double precision , dimension(: ), allocatable :: masses , functionValueTarget
double precision , dimension(:,:), allocatable :: functionCovarianceTarget
integer , intent(in ) :: covarianceBinomialBinsPerDecade
@@ -230,7 +224,7 @@ function massFunctionHIConstructorFile(label,comment,fileName,galacticFilter_,su
! Construct the object.
!![
- self=outputAnalysisMassFunctionHI(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisMassFunctionHI(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -239,7 +233,7 @@ function massFunctionHIConstructorFile(label,comment,fileName,galacticFilter_,su
return
end function massFunctionHIConstructorFile
- function massFunctionHIConstructorInternal(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
+ function massFunctionHIConstructorInternal(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisMolecularRatio_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
!!{
Constructor for the ``massFunctionHI'' output analysis class which takes a parameter set as input.
!!}
@@ -270,7 +264,6 @@ function massFunctionHIConstructorInternal(label,comment,masses,galacticFilter_,
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisPropertyOperator_
class (outputAnalysisDistributionOperatorClass ), intent(in ), target :: outputAnalysisDistributionOperator_
class (outputAnalysisMolecularRatioClass ), intent(in ), target :: outputAnalysisMolecularRatio_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: covarianceBinomialBinsPerDecade
double precision , intent(in ) :: covarianceBinomialMassHaloMinimum , covarianceBinomialMassHaloMaximum
type (varying_string ), intent(in ), optional :: targetLabel
@@ -293,7 +286,7 @@ function massFunctionHIConstructorInternal(label,comment,masses,galacticFilter_,
integer (c_size_t ), parameter :: bufferCountMinimum =5
integer (c_size_t ) :: iBin , bufferCount
!![
-
+
!!]
! Compute weights that apply to each output redshift.
@@ -305,7 +298,7 @@ function massFunctionHIConstructorInternal(label,comment,masses,galacticFilter_,
! Create a HI mass property extractor.
allocate(nodePropertyExtractor_)
!![
-
+
!!]
! Prepend log10, cosmological luminosity distance, and HI mass property operators.
allocate(outputAnalysisPropertyOperatorHIMass_ )
@@ -434,7 +427,6 @@ subroutine massFunctionHIDestructor(self)
!![
-
diff --git a/source/output.analyses.mass_function_stellar.Bernardi_SDSS.F90 b/source/output.analyses.mass_function_stellar.Bernardi_SDSS.F90
index 2ee01a4a71..4db9713fe4 100644
--- a/source/output.analyses.mass_function_stellar.Bernardi_SDSS.F90
+++ b/source/output.analyses.mass_function_stellar.Bernardi_SDSS.F90
@@ -54,14 +54,12 @@ function massFunctionStellarBernardi2013SDSSConstructorParameters(parameters) re
!!{
Constructor for the ``massFunctionStellarBernardi2013SDSS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarBernardi2013SDSS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
@@ -140,21 +138,19 @@ The maximum halo mass to consider when constructing \cite{bernardi_
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarBernardi2013SDSS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarBernardi2013SDSS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarBernardi2013SDSSConstructorParameters
- function massFunctionStellarBernardi2013SDSSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarBernardi2013SDSSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarBernardi2013SDSS'' output analysis class for internal use.
!!}
@@ -171,7 +167,6 @@ function massFunctionStellarBernardi2013SDSSConstructorInternal(cosmologyFunctio
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
double precision , intent(in ), dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
@@ -285,7 +280,6 @@ function massFunctionStellarBernardi2013SDSSConstructorInternal(cosmologyFunctio
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -313,7 +307,6 @@ subroutine massFunctionStellarBernardi2013SDSSDestructor(self)
type(outputAnalysisMassFunctionStellarBernardi2013SDSS), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.F90 b/source/output.analyses.mass_function_stellar.F90
index cb6e863c89..7ccf539a78 100644
--- a/source/output.analyses.mass_function_stellar.F90
+++ b/source/output.analyses.mass_function_stellar.F90
@@ -36,7 +36,6 @@ A stellar mass function output analysis class.
private
class (surveyGeometryClass ), pointer :: surveyGeometry_ => null()
class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null(), cosmologyFunctionsData => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision , allocatable, dimension(:) :: masses
contains
final :: massFunctionStellarDestructor
@@ -57,9 +56,8 @@ function massFunctionStellarConstructorParameters(parameters) result (self)
!!{
Constructor for the ``massFunctionStellar'' output analysis class which takes a parameter set as input.
!!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Error , only : Error_Report
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellar ) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -69,7 +67,6 @@ function massFunctionStellarConstructorParameters(parameters) result (self)
class (outputAnalysisDistributionOperatorClass), pointer :: outputAnalysisDistributionOperator_
class (outputAnalysisPropertyOperatorClass ), pointer :: outputAnalysisPropertyOperator_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , dimension(: ), allocatable :: masses , functionValueTarget , &
& functionCovarianceTarget1D
double precision , dimension(:,:), allocatable :: functionCovarianceTarget
@@ -164,9 +161,8 @@ The target function covariance for likelihood calculations.
-
- self=outputAnalysisMassFunctionStellar(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisMassFunctionStellar(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -178,13 +174,12 @@ The target function covariance for likelihood calculations.
-
!!]
return
end function massFunctionStellarConstructorParameters
- function massFunctionStellarConstructorFile(label,comment,fileName,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
+ function massFunctionStellarConstructorFile(label,comment,fileName,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum) result (self)
!!{
Constructor for the ``massFunctionStellar'' output analysis class which reads bin information from a standard format file.
!!}
@@ -200,7 +195,6 @@ function massFunctionStellarConstructorFile(label,comment,fileName,galacticFilte
class (outputAnalysisPropertyOperatorClass ), intent(inout) , target :: outputAnalysisPropertyOperator_
class (outputAnalysisDistributionOperatorClass), intent(in ) , target :: outputAnalysisDistributionOperator_
class (outputTimesClass ), intent(inout) , target :: outputTimes_
- class (galacticStructureClass ), intent(in ) , target :: galacticStructure_
double precision , dimension(: ), allocatable :: masses , functionValueTarget
double precision , dimension(:,:), allocatable :: functionCovarianceTarget
integer , intent(in ) :: covarianceBinomialBinsPerDecade
@@ -223,7 +217,7 @@ function massFunctionStellarConstructorFile(label,comment,fileName,galacticFilte
! Construct the object.
!![
- self=outputAnalysisMassFunctionStellar(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisMassFunctionStellar(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -232,7 +226,7 @@ function massFunctionStellarConstructorFile(label,comment,fileName,galacticFilte
return
end function massFunctionStellarConstructorFile
- function massFunctionStellarConstructorInternal(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
+ function massFunctionStellarConstructorInternal(label,comment,masses,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
!!{
Constructor for the ``massFunctionStellar'' output analysis class which takes a parameter set as input.
!!}
@@ -257,7 +251,6 @@ function massFunctionStellarConstructorInternal(label,comment,masses,galacticFil
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisPropertyOperator_
class (outputAnalysisDistributionOperatorClass ), intent(in ), target :: outputAnalysisDistributionOperator_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: covarianceBinomialBinsPerDecade
double precision , intent(in ) :: covarianceBinomialMassHaloMinimum , covarianceBinomialMassHaloMaximum
type (varying_string ), intent(in ), optional :: targetLabel
@@ -279,7 +272,7 @@ function massFunctionStellarConstructorInternal(label,comment,masses,galacticFil
integer (c_size_t ), parameter :: bufferCountMinimum =5
integer (c_size_t ) :: iBin , bufferCount
!![
-
+
!!]
! Compute weights that apply to each output redshift.
@@ -291,7 +284,7 @@ function massFunctionStellarConstructorInternal(label,comment,masses,galacticFil
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_)
!![
-
+
!!]
! Prepend log10 and cosmological luminosity distance property operators.
allocate(outputAnalysisPropertyOperatorLog10_ )
@@ -414,7 +407,6 @@ subroutine massFunctionStellarDestructor(self)
-
!!]
return
end subroutine massFunctionStellarDestructor
diff --git a/source/output.analyses.mass_function_stellar.GAMA.F90 b/source/output.analyses.mass_function_stellar.GAMA.F90
index 230e549212..da28325f97 100644
--- a/source/output.analyses.mass_function_stellar.GAMA.F90
+++ b/source/output.analyses.mass_function_stellar.GAMA.F90
@@ -76,8 +76,7 @@ function massFunctionStellarBaldry2012GAMAConstructorParameters(parameters) resu
!!{
Constructor for the ``massFunctionStellarBaldry2012GAMA'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarBaldry2012GAMA) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -85,7 +84,6 @@ function massFunctionStellarBaldry2012GAMAConstructorParameters(parameters) resu
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
class (massFunctionIncompletenessClass ), pointer :: massFunctionIncompleteness_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -164,22 +162,20 @@ The maximum halo mass to consider when constructing \cite{baldry_ga
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarBaldry2012GAMA(cosmologyFunctions_,gravitationalLensing_,massFunctionIncompleteness_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarBaldry2012GAMA(cosmologyFunctions_,gravitationalLensing_,massFunctionIncompleteness_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarBaldry2012GAMAConstructorParameters
- function massFunctionStellarBaldry2012GAMAConstructorInternal(cosmologyFunctions_,gravitationalLensing_,massFunctionIncompleteness_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarBaldry2012GAMAConstructorInternal(cosmologyFunctions_,gravitationalLensing_,massFunctionIncompleteness_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarBaldry2012GAMA'' output analysis class for internal use.
!!}
@@ -199,7 +195,6 @@ function massFunctionStellarBaldry2012GAMAConstructorInternal(cosmologyFunctions
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
class (massFunctionIncompletenessClass ), intent(in ), target :: massFunctionIncompleteness_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
double precision , intent(in ), dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
@@ -327,7 +322,6 @@ function massFunctionStellarBaldry2012GAMAConstructorInternal(cosmologyFunctions
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -356,7 +350,6 @@ subroutine massFunctionStellarBaldry2012GAMADestructor(self)
type(outputAnalysisMassFunctionStellarBaldry2012GAMA), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.PRIMUS.F90 b/source/output.analyses.mass_function_stellar.PRIMUS.F90
index b5a1781da2..410601d7be 100644
--- a/source/output.analyses.mass_function_stellar.PRIMUS.F90
+++ b/source/output.analyses.mass_function_stellar.PRIMUS.F90
@@ -77,15 +77,13 @@ function massFunctionStellarPRIMUSConstructorParameters(parameters) result (self
!!{
Constructor for the ``massFunctionStellarPRIMUS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarPRIMUS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade , redshiftInterval
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -169,21 +167,19 @@ The maximum halo mass to consider when constructing PRIMUS stellar
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarPRIMUS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarPRIMUS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarPRIMUSConstructorParameters
- function massFunctionStellarPRIMUSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarPRIMUSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarPRIMUS'' output analysis class for internal use.
!!}
@@ -203,7 +199,6 @@ function massFunctionStellarPRIMUSConstructorInternal(cosmologyFunctions_,gravit
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: redshiftInterval
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
@@ -356,7 +351,6 @@ function massFunctionStellarPRIMUSConstructorInternal(cosmologyFunctions_,gravit
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -384,7 +378,6 @@ subroutine massFunctionStellarPRIMUSDestructor(self)
type(outputAnalysisMassFunctionStellarPRIMUS), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.SDSS.F90 b/source/output.analyses.mass_function_stellar.SDSS.F90
index 7a40dfe3a1..5c4cea65b6 100644
--- a/source/output.analyses.mass_function_stellar.SDSS.F90
+++ b/source/output.analyses.mass_function_stellar.SDSS.F90
@@ -81,15 +81,13 @@ function massFunctionStellarSDSSConstructorParameters(parameters) result (self)
!!{
Constructor for the ``massFunctionStellarSDSS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarSDSS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -167,21 +165,19 @@ The maximum halo mass to consider when constructing SDSS stellar ma
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarSDSS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarSDSS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarSDSSConstructorParameters
- function massFunctionStellarSDSSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarSDSSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarSDSS'' output analysis class for internal use.
!!}
@@ -198,7 +194,6 @@ function massFunctionStellarSDSSConstructorInternal(cosmologyFunctions_,gravitat
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ) , target :: galacticStructure_
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
double precision , intent(in ), dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
@@ -312,7 +307,6 @@ function massFunctionStellarSDSSConstructorInternal(cosmologyFunctions_,gravitat
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -340,7 +334,6 @@ subroutine massFunctionStellarSDSSDestructor(self)
type(outputAnalysisMassFunctionStellarSDSS), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.UKIDSS_UDS.F90 b/source/output.analyses.mass_function_stellar.UKIDSS_UDS.F90
index e19a4c9b0b..184e0b405e 100644
--- a/source/output.analyses.mass_function_stellar.UKIDSS_UDS.F90
+++ b/source/output.analyses.mass_function_stellar.UKIDSS_UDS.F90
@@ -77,15 +77,13 @@ function massFunctionStellarUKIDSSUDSConstructorParameters(parameters) result (s
!!{
Constructor for the ``massFunctionStellarUKIDSSUDS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarUKIDSSUDS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade , redshiftInterval
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -169,21 +167,19 @@ The maximum halo mass to consider when constructing UKIDSS UDS stel
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarUKIDSSUDS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarUKIDSSUDS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarUKIDSSUDSConstructorParameters
- function massFunctionStellarUKIDSSUDSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarUKIDSSUDSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarUKIDSSUDS'' output analysis class for internal use.
!!}
@@ -203,7 +199,6 @@ function massFunctionStellarUKIDSSUDSConstructorInternal(cosmologyFunctions_,gra
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: redshiftInterval
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
@@ -334,7 +329,6 @@ function massFunctionStellarUKIDSSUDSConstructorInternal(cosmologyFunctions_,gra
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -362,7 +356,6 @@ subroutine massFunctionStellarUKIDSSUDSDestructor(self)
type(outputAnalysisMassFunctionStellarUKIDSSUDS), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.ULTRAVISTA.F90 b/source/output.analyses.mass_function_stellar.ULTRAVISTA.F90
index 0a3df76a36..83af0d5292 100644
--- a/source/output.analyses.mass_function_stellar.ULTRAVISTA.F90
+++ b/source/output.analyses.mass_function_stellar.ULTRAVISTA.F90
@@ -77,15 +77,13 @@ function massFunctionStellarULTRAVISTAConstructorParameters(parameters) result (
!!{
Constructor for the ``massFunctionStellarULTRAVISTA'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarULTRAVISTA) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade , redshiftInterval
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -169,21 +167,19 @@ The maximum halo mass to consider when constructing ULTRAVISTA stel
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarULTRAVISTA(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarULTRAVISTA(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarULTRAVISTAConstructorParameters
- function massFunctionStellarULTRAVISTAConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarULTRAVISTAConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarULTRAVISTA'' output analysis class for internal use.
!!}
@@ -203,7 +199,6 @@ function massFunctionStellarULTRAVISTAConstructorInternal(cosmologyFunctions_,gr
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ) , target :: galacticStructure_
integer , intent(in ) :: redshiftInterval
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
@@ -353,8 +348,7 @@ function massFunctionStellarULTRAVISTAConstructorInternal(cosmologyFunctions_,gr
& cosmologyFunctionsData , &
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
- & outputTimes_ , &
- & galacticStructure_ , &
+ & outputTimes_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -382,7 +376,6 @@ subroutine massFunctionStellarULTRAVISTADestructor(self)
type(outputAnalysisMassFunctionStellarULTRAVISTA), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.VIPERS.F90 b/source/output.analyses.mass_function_stellar.VIPERS.F90
index f7cd7c6d62..43ddf7aa21 100644
--- a/source/output.analyses.mass_function_stellar.VIPERS.F90
+++ b/source/output.analyses.mass_function_stellar.VIPERS.F90
@@ -77,15 +77,13 @@ function massFunctionStellarVIPERSConstructorParameters(parameters) result (self
!!{
Constructor for the ``massFunctionStellarVIPERS'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisMassFunctionStellarVIPERS) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade , redshiftInterval
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -169,21 +167,19 @@ The maximum halo mass to consider when constructing VIPERS stellar
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarVIPERS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarVIPERS(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarVIPERSConstructorParameters
- function massFunctionStellarVIPERSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarVIPERSConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarVIPERS'' output analysis class for internal use.
!!}
@@ -203,7 +199,6 @@ function massFunctionStellarVIPERSConstructorInternal(cosmologyFunctions_,gravit
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: redshiftInterval
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
@@ -334,7 +329,6 @@ function massFunctionStellarVIPERSConstructorInternal(cosmologyFunctions_,gravit
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -362,7 +356,6 @@ subroutine massFunctionStellarVIPERSDestructor(self)
type(outputAnalysisMassFunctionStellarVIPERS), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_function_stellar.ZFOURGE.F90 b/source/output.analyses.mass_function_stellar.ZFOURGE.F90
index 281806504e..ca3b640436 100644
--- a/source/output.analyses.mass_function_stellar.ZFOURGE.F90
+++ b/source/output.analyses.mass_function_stellar.ZFOURGE.F90
@@ -78,7 +78,6 @@ function massFunctionStellarZFOURGEConstructorParameters(parameters) result (sel
Constructor for the ``massFunctionStellarZFOURGE'' output analysis class which takes a parameter set as input.
!!}
use :: Gravitational_Lensing, only : gravitationalLensingClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (outputAnalysisMassFunctionStellarZFOURGE) :: self
@@ -86,7 +85,6 @@ function massFunctionStellarZFOURGEConstructorParameters(parameters) result (sel
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
integer :: covarianceBinomialBinsPerDecade , redshiftInterval
double precision :: covarianceBinomialMassHaloMinimum, covarianceBinomialMassHaloMaximum , &
@@ -170,21 +168,19 @@ The maximum halo mass to consider when constructing ZFOURGE stellar
-
!!]
! Build the object.
- self=outputAnalysisMassFunctionStellarZFOURGE(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisMassFunctionStellarZFOURGE(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function massFunctionStellarZFOURGEConstructorParameters
- function massFunctionStellarZFOURGEConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function massFunctionStellarZFOURGEConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,redshiftInterval,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``massFunctionStellarZFOURGE'' output analysis class for internal use.
!!}
@@ -204,7 +200,6 @@ function massFunctionStellarZFOURGEConstructorInternal(cosmologyFunctions_,gravi
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , intent(in ) :: redshiftInterval
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
& sizeSourceLensing
@@ -367,7 +362,6 @@ function massFunctionStellarZFOURGEConstructorInternal(cosmologyFunctions_,gravi
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& covarianceBinomialBinsPerDecade , &
& covarianceBinomialMassHaloMinimum , &
& covarianceBinomialMassHaloMaximum &
@@ -395,7 +389,6 @@ subroutine massFunctionStellarZFOURGEDestructor(self)
type(outputAnalysisMassFunctionStellarZFOURGE), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.mass_metallicity_relation.Andrews2013.F90 b/source/output.analyses.mass_metallicity_relation.Andrews2013.F90
index 1ae002e9b5..14b5144b32 100644
--- a/source/output.analyses.mass_metallicity_relation.Andrews2013.F90
+++ b/source/output.analyses.mass_metallicity_relation.Andrews2013.F90
@@ -36,7 +36,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: randomErrorMinimum , randomErrorMaximum , &
& fractionGasThreshold
contains
@@ -61,7 +60,6 @@ function massMetallicityAndrews2013ConstructorParameters(parameters) result (sel
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Star_Formation_Rates_Disks , only : starFormationRateDisksClass
use :: Star_Formation_Rates_Spheroids, only : starFormationRateSpheroidsClass
- use :: Galactic_Structure , only : galacticStructureClass
implicit none
type (outputAnalysisMassMetallicityAndrews2013) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -71,7 +69,6 @@ function massMetallicityAndrews2013ConstructorParameters(parameters) result (sel
class (outputTimesClass ), pointer :: outputTimes_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: randomErrorMinimum , randomErrorMaximum , &
& fractionGasThreshold
@@ -125,22 +122,20 @@ function massMetallicityAndrews2013ConstructorParameters(parameters) result (sel
-
!!]
! Build the object.
- self=outputAnalysisMassMetallicityAndrews2013(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisMassMetallicityAndrews2013(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
-
!!]
return
end function massMetallicityAndrews2013ConstructorParameters
- function massMetallicityAndrews2013ConstructorInternal(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result (self)
+ function massMetallicityAndrews2013ConstructorInternal(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_) result (self)
!!{
Constructor for the ``massMetallicityAndrews2013'' output analysis class for internal use.
!!}
@@ -177,7 +172,6 @@ function massMetallicityAndrews2013ConstructorInternal(metallicitySystematicErro
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.0d08, covarianceBinomialMassHaloMaximum =1.0d16
double precision , allocatable , dimension(: ) :: masses , functionValueTarget
@@ -210,7 +204,7 @@ function massMetallicityAndrews2013ConstructorInternal(metallicitySystematicErro
type (hdf5Object ) :: dataFile
integer :: indexOxygen
!![
-
+
!!]
! Read masses at which fraction was measured.
@@ -375,7 +369,7 @@ function massMetallicityAndrews2013ConstructorInternal(metallicitySystematicErro
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Find the index for the oxygen abundance.
indexOxygen=Abundances_Index_From_Name("O")
@@ -472,7 +466,6 @@ subroutine massMetallicityAndrews2013Destructor(self)
-
!!]
return
end subroutine massMetallicityAndrews2013Destructor
diff --git a/source/output.analyses.mass_metallicity_relation.Blanc2019.F90 b/source/output.analyses.mass_metallicity_relation.Blanc2019.F90
index 75850ac91a..e06a692d5c 100644
--- a/source/output.analyses.mass_metallicity_relation.Blanc2019.F90
+++ b/source/output.analyses.mass_metallicity_relation.Blanc2019.F90
@@ -36,7 +36,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: randomErrorMinimum , randomErrorMaximum , &
& fractionGasThreshold
contains
@@ -61,7 +60,6 @@ function massMetallicityBlanc2019ConstructorParameters(parameters) result (self)
use :: Input_Parameters , only : inputParameter , inputParameters
use :: Star_Formation_Rates_Disks , only : starFormationRateDisksClass
use :: Star_Formation_Rates_Spheroids, only : starFormationRateSpheroidsClass
- use :: Galactic_Structure , only : galacticStructureClass
implicit none
type (outputAnalysisMassMetallicityBlanc2019) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -71,7 +69,6 @@ function massMetallicityBlanc2019ConstructorParameters(parameters) result (self)
class (outputTimesClass ), pointer :: outputTimes_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: randomErrorMinimum , randomErrorMaximum , &
& fractionGasThreshold
@@ -125,22 +122,20 @@ function massMetallicityBlanc2019ConstructorParameters(parameters) result (self)
-
!!]
! Build the object.
- self=outputAnalysisMassMetallicityBlanc2019(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisMassMetallicityBlanc2019(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
-
!!]
return
end function massMetallicityBlanc2019ConstructorParameters
- function massMetallicityBlanc2019ConstructorInternal(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result (self)
+ function massMetallicityBlanc2019ConstructorInternal(metallicitySystematicErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,fractionGasThreshold,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_) result (self)
!!{
Constructor for the ``massMetallicityBlanc2019'' output analysis class for internal use.
!!}
@@ -177,7 +172,6 @@ function massMetallicityBlanc2019ConstructorInternal(metallicitySystematicErrorP
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.0d08, covarianceBinomialMassHaloMaximum =1.0d16
double precision , allocatable , dimension(: ) :: masses , functionValueTarget , &
@@ -211,7 +205,7 @@ function massMetallicityBlanc2019ConstructorInternal(metallicitySystematicErrorP
type (hdf5Object ) :: dataFile
integer :: indexOxygen
!![
-
+
!!]
! Read masses at which fraction was measured.
@@ -380,7 +374,7 @@ function massMetallicityBlanc2019ConstructorInternal(metallicitySystematicErrorP
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Find the index for the oxygen abundance.
indexOxygen=Abundances_Index_From_Name("O")
@@ -476,7 +470,6 @@ subroutine massMetallicityBlanc2019Destructor(self)
-
!!]
return
end subroutine massMetallicityBlanc2019Destructor
diff --git a/source/output.analyses.morphological_fraction.GAMA_Moffett2016.F90 b/source/output.analyses.morphological_fraction.GAMA_Moffett2016.F90
index d14dae7e7a..0f1ec040bb 100644
--- a/source/output.analyses.morphological_fraction.GAMA_Moffett2016.F90
+++ b/source/output.analyses.morphological_fraction.GAMA_Moffett2016.F90
@@ -35,7 +35,6 @@
& functionErrorLowerTarget , functionErrorUpperTarget , &
& systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass), pointer :: cosmologyFunctions_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: ratioEarlyType , ratioEarlyTypeError , &
& randomErrorMinimum , randomErrorMaximum
contains
@@ -58,15 +57,13 @@ function morphologicalFractionGAMAMoffett2016ConstructorParameters(parameters) r
!!{
Constructor for the ``morphologicalFractionGAMAMoffett2016'' output analysis class which takes a parameter set as input.
!!}
- use :: Cosmology_Functions, only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Cosmology_Functions, only : cosmologyFunctions, cosmologyFunctionsClass
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (outputAnalysisMorphologicalFractionGAMAMoffett2016) :: self
type (inputParameters ), intent(inout) :: parameters
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient, randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (outputTimesClass ), pointer :: outputTimes_
double precision :: ratioEarlyType , ratioEarlyTypeError , &
& randomErrorMinimum , randomErrorMaximum
@@ -118,20 +115,18 @@ function morphologicalFractionGAMAMoffett2016ConstructorParameters(parameters) r
-
!!]
! Build the object.
- self=outputAnalysisMorphologicalFractionGAMAMoffett2016(ratioEarlyType,ratioEarlyTypeError,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,galacticStructure_)
+ self=outputAnalysisMorphologicalFractionGAMAMoffett2016(ratioEarlyType,ratioEarlyTypeError,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_)
!![
-
!!]
return
end function morphologicalFractionGAMAMoffett2016ConstructorParameters
- function morphologicalFractionGAMAMoffett2016ConstructorInternal(ratioEarlyType,ratioEarlyTypeError,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_,galacticStructure_) result (self)
+ function morphologicalFractionGAMAMoffett2016ConstructorInternal(ratioEarlyType,ratioEarlyTypeError,systematicErrorPolynomialCoefficient,randomErrorPolynomialCoefficient,randomErrorMinimum,randomErrorMaximum,cosmologyFunctions_,outputTimes_) result (self)
!!{
Constructor for the ``morphologicalFractionGAMAMoffett2016'' output analysis class for internal use.
!!}
@@ -159,7 +154,6 @@ function morphologicalFractionGAMAMoffett2016ConstructorInternal(ratioEarlyType,
double precision , intent(in ), dimension(: ) :: systematicErrorPolynomialCoefficient , randomErrorPolynomialCoefficient
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
integer , parameter :: covarianceBinomialBinsPerDecade =10
double precision , parameter :: covarianceBinomialMassHaloMinimum = 1.000d08 , covarianceBinomialMassHaloMaximum=1.0d16
double precision , allocatable , dimension(: ) :: masses , functionValueTarget
@@ -189,7 +183,7 @@ function morphologicalFractionGAMAMoffett2016ConstructorInternal(ratioEarlyType,
type (hdf5Object ) :: dataFile
double precision :: probit,sqrtArg
!![
-
+
!!]
! Read masses at which fraction was measured.
@@ -356,12 +350,12 @@ function morphologicalFractionGAMAMoffett2016ConstructorInternal(ratioEarlyType,
! Create a stellar mass property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a morphology weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
! Build the object.
self%outputAnalysisMeanFunction1D=outputAnalysisMeanFunction1D( &
@@ -431,7 +425,6 @@ subroutine morphologicalFractionGAMAMoffett2016Destructor(self)
!![
-
!!]
return
end subroutine morphologicalFractionGAMAMoffett2016Destructor
diff --git a/source/output.analyses.progenitor_mass_functions.F90 b/source/output.analyses.progenitor_mass_functions.F90
index 22aee95e3e..f197adaa73 100644
--- a/source/output.analyses.progenitor_mass_functions.F90
+++ b/source/output.analyses.progenitor_mass_functions.F90
@@ -46,7 +46,6 @@ A dark matter halo progenitor mass function output analysis class.
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrastDefinition_ => null(), virialDensityContrast_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
type (galacticFilterAll ), pointer :: galacticFilterParentMass_ => null()
type (outputAnalysisWeightOperatorNbodyMass), pointer :: outputAnalysisWeightOperatorNbodyMass_ => null()
type (nodePropertyExtractorMassHalo ), pointer :: nodePropertyExtractorMassParent_ => null()
@@ -99,7 +98,6 @@ function progenitorMassFunctionConstructorParameters(parameters) result (self)
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
class (outputTimesClass ), pointer :: outputTimes_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrastDefinition_, virialDensityContrast_
double precision , dimension(: ), allocatable :: functionValueTarget , functionCovarianceTarget1D, &
& rootVarianceTargetFractional
@@ -119,7 +117,6 @@ function progenitorMassFunctionConstructorParameters(parameters) result (self)
!![
-
@@ -204,7 +201,7 @@ The name of the file from which to read progenitor mass function pa
Label for the target dataset.
!!]
- self=outputAnalysisProgenitorMassFunction(char(fileName),label,comment,targetLabel,indexParent,indexRedshift,redshiftParent,massRatioLikelihoodMinimum,massRatioLikelihoodMaximum,covarianceDiagonalize,covarianceTargetOnly,rootVarianceTargetFractional,likelihoodInLog,alwaysIsolatedOnly,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,nbodyHaloMassError_,outputTimes_)
+ self=outputAnalysisProgenitorMassFunction(char(fileName),label,comment,targetLabel,indexParent,indexRedshift,redshiftParent,massRatioLikelihoodMinimum,massRatioLikelihoodMaximum,covarianceDiagonalize,covarianceTargetOnly,rootVarianceTargetFractional,likelihoodInLog,alwaysIsolatedOnly,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_,virialDensityContrastDefinition_,nbodyHaloMassError_,outputTimes_)
else
!![
@@ -308,7 +305,6 @@ The target function covariance for likelihood calculations.The target function covariance for likelihood calculations.
-
@@ -335,7 +330,7 @@ The target function covariance for likelihood calculations.
!!]
return
end function progenitorMassFunctionConstructorFile
- function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimum,massRatioMaximum,countMassRatio,massParentMinimum,massParentMaximum,timeProgenitor,timeParent,alwaysIsolatedOnly,massRatioLikelihoodMinimum,massRatioLikelihoodMaximum,covarianceDiagonalize,covarianceTargetOnly,rootVarianceTargetFractional,likelihoodInLog,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,virialDensityContrastDefinition_,nbodyHaloMassError_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
+ function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimum,massRatioMaximum,countMassRatio,massParentMinimum,massParentMaximum,timeProgenitor,timeParent,alwaysIsolatedOnly,massRatioLikelihoodMinimum,massRatioLikelihoodMaximum,covarianceDiagonalize,covarianceTargetOnly,rootVarianceTargetFractional,likelihoodInLog,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,virialDensityContrastDefinition_,nbodyHaloMassError_,outputTimes_,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
!!{
Internal constructor for the ``progenitorMassFunction'' output analysis class.
!!}
@@ -453,7 +447,6 @@ function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimu
double precision , intent(in ) :: massRatioLikelihoodMinimum , massRatioLikelihoodMaximum
class (cosmologyParametersClass ), intent(inout), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (nbodyHaloMassErrorClass ), intent(in ), target :: nbodyHaloMassError_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrastDefinition_ , virialDensityContrast_
@@ -492,7 +485,7 @@ function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimu
type (outputAnalysisPropertyOperatorIdentity ), pointer :: outputAnalysisPropertyIdentity_
integer (c_size_t ) :: iOutput , bufferCount
!![
-
+
!!]
! Initialize state.
@@ -547,9 +540,9 @@ function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimu
end if
!![
-
-
-
+
+
+
@@ -561,10 +554,10 @@ function progenitorMassFunctionConstructorInternal(label,comment,massRatioMinimu
allocate( nodePropertyExtractorMassRatio_ )
allocate( nodePropertyExtractorParentNode_ )
!![
-
-
-
-
+
+
+
+
!!]
! Create a distribution normalizer which normalizes to bin width.
allocate(outputAnalysisDistributionNormalizerBinWidth_ )
@@ -708,7 +701,6 @@ subroutine progenitorMassFunctionDestructor(self)
-
diff --git a/source/output.analyses.quiescent_fraction.F90 b/source/output.analyses.quiescent_fraction.F90
index 23c75739de..8209696040 100644
--- a/source/output.analyses.quiescent_fraction.F90
+++ b/source/output.analyses.quiescent_fraction.F90
@@ -41,7 +41,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null(), cosmologyFunctionsData => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (varying_string ) :: fileName
double precision :: massMinimum , massMaximum , &
& starFormationRateSpecificQuiescentLogarithmic , starFormationRateSpecificLogarithmicError , &
@@ -63,10 +62,9 @@ function quiescentFractionConstructorParameters(parameters) result (self)
!!{
Constructor for the ``quiescentFraction'' output analysis class which takes a parameter set as input.
!!}
- use :: Error , only : Error_Report
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Error , only : Error_Report
+ use :: Input_Parameters, only : inputParameter, inputParameters
+ use :: Numerical_Ranges, only : Make_Range , rangeTypeLogarithmic
implicit none
type (outputAnalysisQuiescentFraction ) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -78,7 +76,6 @@ function quiescentFractionConstructorParameters(parameters) result (self)
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
class (outputAnalysisPropertyOperatorClass ), pointer :: outputAnalysisPropertyOperator_ , outputAnalysisWeightPropertyOperator_
class (outputAnalysisDistributionOperatorClass), pointer :: outputAnalysisDistributionOperator_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , dimension(: ), allocatable :: meanValueTarget , meanCovarianceTarget1D , &
& massesStellar
double precision , dimension(:,:), allocatable :: meanCovarianceTarget
@@ -102,7 +99,6 @@ function quiescentFractionConstructorParameters(parameters) result (self)
-
starFormationRateSpecificQuiescentLogarithmic
@@ -132,7 +128,7 @@ The name of the file from which to read quiescent fraction function
A label for this analysis.
!!]
- self=outputAnalysisQuiescentFraction(char(fileName),label,comment,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisQuiescentFraction(char(fileName),label,comment,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_)
else
!![
@@ -221,8 +217,7 @@ The target function covariance for likelihood calculations.
@@ -244,12 +239,11 @@ The target function covariance for likelihood calculations.
-
!!]
return
end function quiescentFractionConstructorParameters
- function quiescentFractionConstructorFile(fileName,label,comment,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function quiescentFractionConstructorFile(fileName,label,comment,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Constructor for the ``quiescentFraction'' output analysis class which reads all required properties from file.
!!}
@@ -269,7 +263,6 @@ function quiescentFractionConstructorFile(fileName,label,comment,starFormationRa
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisPropertyOperator_
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisWeightPropertyOperator_
class (outputAnalysisDistributionOperatorClass), intent(inout), target :: outputAnalysisDistributionOperator_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: starFormationRateSpecificQuiescentLogarithmic, starFormationRateSpecificLogarithmicError
double precision , allocatable , dimension(: ) :: meanValueTarget , massesStellar
double precision , allocatable , dimension(:,:) :: meanCovarianceTarget
@@ -285,14 +278,14 @@ function quiescentFractionConstructorFile(fileName,label,comment,starFormationRa
call dataFile%close ( )
!$ call hdf5Access%unset()
! Build the object.
- self=quiescentFractionConstructorInternal(label,comment,massesStellar,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_,targetLabel,meanValueTarget,meanCovarianceTarget)
+ self=quiescentFractionConstructorInternal(label,comment,massesStellar,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,targetLabel,meanValueTarget,meanCovarianceTarget)
!![
!!]
return
end function quiescentFractionConstructorFile
- function quiescentFractionConstructorInternal(label,comment,massesStellar,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_,targetLabel,meanValueTarget,meanCovarianceTarget) result(self)
+ function quiescentFractionConstructorInternal(label,comment,massesStellar,starFormationRateSpecificQuiescentLogarithmic,starFormationRateSpecificLogarithmicError,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,targetLabel,meanValueTarget,meanCovarianceTarget) result(self)
!!{
Internal constructor for the ``quiescentFraction'' output analysis class.
!!}
@@ -319,7 +312,6 @@ function quiescentFractionConstructorInternal(label,comment,massesStellar,starFo
class (outputAnalysisDistributionOperatorClass ), intent(inout), target :: outputAnalysisDistributionOperator_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (varying_string ), optional , intent(in ) :: targetLabel
double precision , optional , dimension(: ), intent(in ) :: meanValueTarget , massesStellar
double precision , optional , dimension(:,:), intent(in ) :: meanCovarianceTarget
@@ -342,7 +334,7 @@ function quiescentFractionConstructorInternal(label,comment,massesStellar,starFo
integer (c_size_t ) :: iBin , bufferCount , &
& countMasses
!![
-
+
!!]
! Set properties needed for descriptor.
@@ -430,7 +422,7 @@ function quiescentFractionConstructorInternal(label,comment,massesStellar,starFo
!![
- nodePropertyExtractorMassStellar(galacticStructure_)
+ nodePropertyExtractorMassStellar()
!!]
diff --git a/source/output.analyses.quiescent_fraction.Wagner2016.F90 b/source/output.analyses.quiescent_fraction.Wagner2016.F90
index e8e44fab5f..e2282744c1 100644
--- a/source/output.analyses.quiescent_fraction.Wagner2016.F90
+++ b/source/output.analyses.quiescent_fraction.Wagner2016.F90
@@ -46,7 +46,6 @@
!!}
private
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& weightSystematicErrorPolynomialCoefficient
@@ -70,12 +69,10 @@ function quiescentFractionWagner2016ConstructorParameters(parameters) result (se
!!{
Constructor for the ``quiescentFractionWagner2016'' output analysis class which takes a parameter set as input.
!!}
- use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (outputAnalysisQuiescentFractionWagner2016) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -84,9 +81,7 @@ function quiescentFractionWagner2016ConstructorParameters(parameters) result (se
class (outputTimesClass ), pointer :: outputTimes_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& weightSystematicErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -150,36 +145,31 @@ function quiescentFractionWagner2016ConstructorParameters(parameters) result (se
-
-
!!]
- self=outputAnalysisQuiescentFractionWagner2016(enumerationWagner2016QuiescentRedshiftRangeEncode(char(redshiftRange),includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisQuiescentFractionWagner2016(enumerationWagner2016QuiescentRedshiftRangeEncode(char(redshiftRange),includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
-
-
!!]
return
end function quiescentFractionWagner2016ConstructorParameters
- function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Internal constructor for the ``quiescentFractionWagner2016'' output analysis class.
!!}
use :: Error , only : Error_Report
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Input_Paths , only : inputPath , pathTypeDataStatic
use :: Output_Times , only : outputTimesClass
use :: Statistics_NBody_Halo_Mass_Errors , only : nbodyHaloMassErrorClass
@@ -203,8 +193,6 @@ function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErro
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (galacticFilterHaloNotIsolated ) , pointer :: galacticFilterIsSubhalo_
type (galacticFilterHighPass ) , pointer :: galacticFilterHostHaloMass_
type (galacticFilterStellarMass ) , pointer :: galacticFilterStellarMass_
@@ -228,7 +216,7 @@ function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErro
type (varying_string ) :: fileName , label , &
& description
!![
-
+
!!]
! Construct file name and label for the analysis.
@@ -282,7 +270,7 @@ function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErro
!!]
allocate(nodePropertyExtractorHostMass_)
!![
-
+
!!]
allocate(nodePropertyExtractorHost_)
!![
@@ -371,8 +359,7 @@ function quiescentFractionWagner2016ConstructorInternal(redshiftRange,randomErro
& outputAnalysisDistributionOperator_ , &
& outputAnalysisWeightPropertyOperator_ , &
& starFormationRateDisks_ , &
- & starFormationRateSpheroids_ , &
- & galacticStructure_ &
+ & starFormationRateSpheroids_ &
& )
!![
@@ -399,7 +386,6 @@ subroutine quiescentFractionWagner2016Destructor(self)
!![
-
!!]
return
diff --git a/source/output.analyses.satellite_radius_velocity_maximum.F90 b/source/output.analyses.satellite_radius_velocity_maximum.F90
index 040f281f5d..94652c2fc4 100644
--- a/source/output.analyses.satellite_radius_velocity_maximum.F90
+++ b/source/output.analyses.satellite_radius_velocity_maximum.F90
@@ -180,17 +180,26 @@ subroutine satelliteRadiusVelocityMaximumAnalyze(self,node,iOutput)
Analyze the maximum velocity tidal track.
!!}
use :: Numerical_Constants_Math, only : Pi
- !$ use :: OMP_Lib , only : OMP_Set_Lock, OMP_Unset_Lock
+ use :: Mass_Distributions , only : massDistributionClass
+ !$ use :: OMP_Lib , only : OMP_Set_Lock , OMP_Unset_Lock
implicit none
class (outputAnalysisSatelliteRadiusVelocityMaximum), intent(inout) :: self
type (treeNode ), intent(inout) :: node
integer (c_size_t ), intent(in ) :: iOutput
+ class (massDistributionClass ), pointer :: massDistributionUnheated_ , massDistribution_
double precision :: fractionRadiusVelocityMaximum, varianceFractionRadiusVelocityMaximum
! Skip non-satellites.
if (.not.node%isSatellite()) return
! Extract the maximum circular velocity fraction.
- fractionRadiusVelocityMaximum=self%darkMatterProfileDMO_%radiusCircularVelocityMaximum(node)/self%darkMatterProfileDMOUnheated%radiusCircularVelocityMaximum(node)
+ massDistribution_ => self%darkMatterProfileDMO_ %get(node)
+ massDistributionUnheated_ => self%darkMatterProfileDMOUnheated%get(node)
+ fractionRadiusVelocityMaximum=+massDistribution_ %radiusRotationCurveMaximum() &
+ & /massDistributionUnheated_%radiusRotationCurveMaximum()
+ !![
+
+
+ !!]
!$ call OMP_Set_Lock(self%accumulateLock)
self%fractionRadiusVelocityMaximum(iOutput)=fractionRadiusVelocityMaximum
! Add model uncertainty.
diff --git a/source/output.analyses.satellite_velocity_maximum.F90 b/source/output.analyses.satellite_velocity_maximum.F90
index 7264df3a13..19b321353e 100644
--- a/source/output.analyses.satellite_velocity_maximum.F90
+++ b/source/output.analyses.satellite_velocity_maximum.F90
@@ -180,18 +180,26 @@ subroutine satelliteVelocityMaximumAnalyze(self,node,iOutput)
Analyze the maximum velocity tidal track.
!!}
use :: Numerical_Constants_Math, only : Pi
- !$ use :: OMP_Lib , only : OMP_Set_Lock, OMP_Unset_Lock
+ use :: Mass_Distributions , only : massDistributionClass
+ !$ use :: OMP_Lib , only : OMP_Set_Lock , OMP_Unset_Lock
implicit none
class (outputAnalysisSatelliteVelocityMaximum), intent(inout) :: self
type (treeNode ), intent(inout) :: node
integer (c_size_t ), intent(in ) :: iOutput
- double precision :: fractionVelocityMaximum, varianceFractionVelocityMaximum
+ class (massDistributionClass ), pointer :: massDistributionUnheated_, massDistribution_
+ double precision :: fractionVelocityMaximum , varianceFractionVelocityMaximum
! Skip non-satellites.
if (.not.node%isSatellite()) return
! Extract the maximum circular velocity fraction.
- fractionVelocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum(node)/self%darkMatterProfileDMOUnheated%circularVelocityMaximum(node)
- !$ call OMP_Set_Lock(self%accumulateLock)
+ massDistribution_ => self%darkMatterProfileDMO_ %get(node)
+ massDistributionUnheated_ => self%darkMatterProfileDMOUnheated%get(node)
+ fractionVelocityMaximum=+massDistribution_ %velocityRotationCurveMaximum() &
+ & /massDistributionUnheated_%velocityRotationCurveMaximum()
+ !![
+
+
+ !!]
self%fractionVelocityMaximum(iOutput)=fractionVelocityMaximum
! Add model uncertainty.
varianceFractionVelocityMaximum =+ self%varianceFractionVelocityMaximumTarget(iOutput) &
diff --git a/source/output.analyses.spin_distribution.Bett2007.F90 b/source/output.analyses.spin_distribution.Bett2007.F90
index 028d9de054..616682ea00 100644
--- a/source/output.analyses.spin_distribution.Bett2007.F90
+++ b/source/output.analyses.spin_distribution.Bett2007.F90
@@ -60,7 +60,6 @@ function spinDistributionBett2007ConstructorParameters(parameters) result (self)
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
class (haloMassFunctionClass ), pointer :: haloMassFunction_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterProfileScaleRadiusClass ), pointer :: darkMatterProfileScaleRadius_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
class (* ), pointer :: percolationObjects_
@@ -87,12 +86,11 @@ function spinDistributionBett2007ConstructorParameters(parameters) result (self)
-
!!]
percolationObjects_ => Virial_Density_Contrast_Percolation_Objects_Constructor_(parameters)
- self = outputAnalysisSpinDistributionBett2007(logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileDMO_,darkMatterProfileScaleRadius_,virialDensityContrast_,outputTimes_,percolationObjects_)
+ self = outputAnalysisSpinDistributionBett2007(logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileScaleRadius_,virialDensityContrast_,outputTimes_,percolationObjects_)
!![
@@ -101,14 +99,13 @@ function spinDistributionBett2007ConstructorParameters(parameters) result (self)
-
!!]
return
end function spinDistributionBett2007ConstructorParameters
- function spinDistributionBett2007ConstructorInternal(logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileDMO_,darkMatterProfileScaleRadius_,virialDensityContrast_,outputTimes_,percolationObjects_) result(self)
+ function spinDistributionBett2007ConstructorInternal(logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileScaleRadius_,virialDensityContrast_,outputTimes_,percolationObjects_) result(self)
!!{
Internal constructor for the ``spinDistributionBett2007'' output analysis class.
!!}
@@ -129,7 +126,6 @@ function spinDistributionBett2007ConstructorInternal(logNormalRange,errorToleran
class (nbodyHaloMassErrorClass ), target , intent(in ) :: nbodyHaloMassError_
class (haloMassFunctionClass ), target , intent(in ) :: haloMassFunction_
class (darkMatterHaloScaleClass ), target , intent(in ) :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), target , intent(in ) :: darkMatterProfileDMO_
class (darkMatterProfileScaleRadiusClass ), target , intent(in ) :: darkMatterProfileScaleRadius_
class (virialDensityContrastClass ), target , intent(in ) :: virialDensityContrast_
class (* ), target , intent(in ) :: percolationObjects_
@@ -151,7 +147,6 @@ function spinDistributionBett2007ConstructorInternal(logNormalRange,errorToleran
& nbodyHaloMassError_ , &
& haloMassFunction_ , &
& darkMatterHaloScale_ , &
- & darkMatterProfileDMO_ , &
& darkMatterProfileScaleRadius_ , &
& outputTimes_ , &
& virialDensityContrast_ , &
diff --git a/source/output.analyses.spin_distribution.F90 b/source/output.analyses.spin_distribution.F90
index 557a6e156a..7f5ae7f61c 100644
--- a/source/output.analyses.spin_distribution.F90
+++ b/source/output.analyses.spin_distribution.F90
@@ -39,7 +39,6 @@ A stellar mass function output analysis class.
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_ => null()
class (haloMassFunctionClass ), pointer :: haloMassFunction_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
double precision :: timeRecent , logNormalRange , &
@@ -82,7 +81,6 @@ function spinDistributionConstructorParameters(parameters) result (self)
class (nbodyHaloMassErrorClass ), pointer :: nbodyHaloMassError_
class (haloMassFunctionClass ), pointer :: haloMassFunction_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ , virialDensityContrastDefinition_
double precision , dimension(: ), allocatable :: functionValueTarget , functionCovarianceTarget1D
@@ -118,7 +116,6 @@ function spinDistributionConstructorParameters(parameters) result (self)
-
@@ -142,7 +139,7 @@ The name of the file from which to read spin distribution function
A label for this analysis.
!!]
- self=outputAnalysisSpinDistribution(char(fileName),label,comment,logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileDMO_,darkMatterProfileScaleRadius_,outputTimes_,virialDensityContrast_,virialDensityContrastDefinition_)
+ self=outputAnalysisSpinDistribution(char(fileName),label,comment,logNormalRange,errorTolerant,cosmologyParameters_,cosmologyFunctions_,nbodyHaloMassError_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileScaleRadius_,outputTimes_,virialDensityContrast_,virialDensityContrastDefinition_)
else
!![
@@ -269,7 +266,6 @@ The target function covariance for likelihood calculations.The target function covariance for likelihood calculations.
-
@@ -299,7 +294,7 @@ The target function covariance for likelihood calculations.
+
!!]
! Build grid of spins.
@@ -477,7 +470,6 @@ function spinDistributionConstructorInternal(label,comment,time,massMinimum,mass
& cosmologyFunctions_ =cosmologyFunctions_ , &
& haloMassFunction_ =haloMassFunction_ , &
& darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMO_ , &
& darkMatterProfileScaleRadius_ =darkMatterProfileScaleRadius_ &
& )
@@ -486,7 +478,7 @@ function spinDistributionConstructorInternal(label,comment,time,massMinimum,mass
! Create a spin parameter property extractor.
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Create a log10 property operator.
allocate(outputAnalysisPropertyOperator_ )
@@ -515,7 +507,7 @@ function spinDistributionConstructorInternal(label,comment,time,massMinimum,mass
!!]
allocate(galacticFilterHaloMassRange_ )
!![
-
+
!!]
allocate(galacticFilterNodeMajorMergerRecent_ )
!![
@@ -694,7 +686,6 @@ subroutine spinDistributionDestructor(self)
-
diff --git a/source/output.analyses.star_formation_rate_function.F90 b/source/output.analyses.star_formation_rate_function.F90
index de9d072d6f..296b56d2a4 100755
--- a/source/output.analyses.star_formation_rate_function.F90
+++ b/source/output.analyses.star_formation_rate_function.F90
@@ -39,7 +39,6 @@ A stellar mass function output analysis class.
private
class (surveyGeometryClass ), pointer :: surveyGeometry_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null(), cosmologyFunctionsData => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
double precision , allocatable, dimension(:) :: starFormationRates
@@ -62,9 +61,8 @@ function starFormationRateFunctionConstructorParameters(parameters) result (self
!!{
Constructor for the ``starFormationRateFunction'' output analysis class which takes a parameter set as input.
!!}
- use :: Error , only : Error_Report
- use :: Galactic_Structure, only : galacticStructureClass
- use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Error , only : Error_Report
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisStarFormationRateFunction) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -74,7 +72,6 @@ function starFormationRateFunctionConstructorParameters(parameters) result (self
class (outputAnalysisDistributionOperatorClass), pointer :: outputAnalysisDistributionOperator_
class (outputAnalysisPropertyOperatorClass ), pointer :: outputAnalysisPropertyOperator_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
double precision , dimension(: ), allocatable :: starFormationRates , functionValueTarget , &
@@ -171,11 +168,10 @@ The target function covariance for likelihood calculations.
-
- self=outputAnalysisStarFormationRateFunction(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisStarFormationRateFunction(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -187,7 +183,6 @@ The target function covariance for likelihood calculations.
-
@@ -195,7 +190,7 @@ The target function covariance for likelihood calculations.
- self=outputAnalysisStarFormationRateFunction(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
+ self=outputAnalysisStarFormationRateFunction(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum{conditions})
@@ -245,7 +239,7 @@ function starFormationRateFunctionConstructorFile(label,comment,fileName,galacti
return
end function starFormationRateFunctionConstructorFile
- function starFormationRateFunctionConstructorInternal(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,galacticStructure_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
+ function starFormationRateFunctionConstructorInternal(label,comment,starFormationRates,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,targetLabel,functionValueTarget,functionCovarianceTarget) result(self)
!!{
Constructor for the ``starFormationRateFunction'' output analysis class which takes a parameter set as input.
!!}
@@ -270,7 +264,6 @@ function starFormationRateFunctionConstructorInternal(label,comment,starFormatio
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisPropertyOperator_
class (outputAnalysisDistributionOperatorClass ), intent(in ), target :: outputAnalysisDistributionOperator_
class (outputTimesClass ), intent(inout), target :: outputTimes_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
integer , intent(in ) :: covarianceBinomialBinsPerDecade
@@ -294,7 +287,7 @@ function starFormationRateFunctionConstructorInternal(label,comment,starFormatio
integer (c_size_t ), parameter :: bufferCountMinimum =5
integer (c_size_t ) :: iBin , bufferCount
!![
-
+
!!]
! Compute weights that apply to each output redshift.
@@ -429,7 +422,6 @@ subroutine starFormationRateFunctionDestructor(self)
-
!!]
diff --git a/source/output.analyses.star_formation_rate_function.Robotham2011.F90 b/source/output.analyses.star_formation_rate_function.Robotham2011.F90
index bcfd27f8c7..9e17f7149e 100755
--- a/source/output.analyses.star_formation_rate_function.Robotham2011.F90
+++ b/source/output.analyses.star_formation_rate_function.Robotham2011.F90
@@ -56,14 +56,12 @@ function starFormationRateFunctionRobotham2011ConstructorParameters(parameters)
!!{
Constructor for the ``StarFormationRateFunctionRobotham2011'' output analysis class which takes a parameter set as input.
!!}
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (outputAnalysisStarFormationRateFunctionRobotham2011) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
class (gravitationalLensingClass ), pointer :: gravitationalLensing_
@@ -144,25 +142,23 @@ The maximum halo mass to consider when constructing \cite{robotham_
-
!!]
! Build the object.
- self=outputAnalysisStarFormationRateFunctionRobotham2011(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,starFormationRateDisks_,starFormationRateSpheroids_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
+ self=outputAnalysisStarFormationRateFunctionRobotham2011(cosmologyFunctions_,gravitationalLensing_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing)
!![
-
!!]
return
end function starFormationRateFunctionRobotham2011ConstructorParameters
- function starFormationRateFunctionRobotham2011ConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,galacticStructure_,starFormationRateDisks_,starFormationRateSpheroids_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
+ function starFormationRateFunctionRobotham2011ConstructorInternal(cosmologyFunctions_,gravitationalLensing_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,covarianceBinomialBinsPerDecade,covarianceBinomialMassHaloMinimum,covarianceBinomialMassHaloMaximum,sizeSourceLensing) result (self)
!!{
Constructor for the ``StarFormationRateFunctionRobotham2011'' output analysis class for internal use.
!!}
@@ -179,7 +175,6 @@ function starFormationRateFunctionRobotham2011ConstructorInternal(cosmologyFunct
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (gravitationalLensingClass ), intent(in ), target :: gravitationalLensing_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
double precision , intent(in ) :: randomErrorMinimum , randomErrorMaximum , &
@@ -295,7 +290,6 @@ function starFormationRateFunctionRobotham2011ConstructorInternal(cosmologyFunct
& outputAnalysisPropertyOperator_ , &
& outputAnalysisDistributionOperator_ , &
& outputTimes_ , &
- & galacticStructure_ , &
& starFormationRateDisks_ , &
& starFormationRateSpheroids_ , &
& covarianceBinomialBinsPerDecade , &
@@ -325,7 +319,6 @@ subroutine starFormationRateFunctionRobotham2011Destructor(self)
type(outputAnalysisStarFormationRateFunctionRobotham2011), intent(inout) :: self
!![
-
!!]
return
diff --git a/source/output.analyses.star_forming_main_sequence.F90 b/source/output.analyses.star_forming_main_sequence.F90
index 49fc80e69e..0685dd903b 100644
--- a/source/output.analyses.star_forming_main_sequence.F90
+++ b/source/output.analyses.star_forming_main_sequence.F90
@@ -41,7 +41,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null(), cosmologyFunctionsData => null()
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_ => null()
class (starFormationRateSpheroidsClass), pointer :: starFormationRateSpheroids_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (varying_string ) :: fileName
double precision :: massMinimum , massMaximum , &
& countMassesPerDecade
@@ -63,9 +62,8 @@ function starFormingMainSequenceConstructorParameters(parameters) result (self)
Constructor for the ``starFormingMainSequence'' output analysis class which takes a parameter set as input.
!!}
use :: Error , only : Error_Report
- use :: Input_Parameters , only : inputParameter , inputParameters
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Input_Parameters , only : inputParameter, inputParameters
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
implicit none
type (outputAnalysisStarFormingMainSequence ) :: self
type (inputParameters ), intent(inout) :: parameters
@@ -77,7 +75,6 @@ function starFormingMainSequenceConstructorParameters(parameters) result (self)
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
class (outputAnalysisPropertyOperatorClass ), pointer :: outputAnalysisPropertyOperator_ , outputAnalysisWeightPropertyOperator_
class (outputAnalysisDistributionOperatorClass), pointer :: outputAnalysisDistributionOperator_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , dimension(: ), allocatable :: meanValueTarget , meanCovarianceTarget1D , &
& massesStellar
double precision , dimension(:,:), allocatable :: meanCovarianceTarget
@@ -100,7 +97,6 @@ function starFormingMainSequenceConstructorParameters(parameters) result (self)
-
!!]
if (parameters%isPresent('fileName')) then
!![
@@ -120,7 +116,7 @@ The name of the file from which to read star forming main sequence
A label for this analysis.
!!]
- self=outputAnalysisStarFormingMainSequence(char(fileName),label,comment,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisStarFormingMainSequence(char(fileName),label,comment,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_)
else
!![
@@ -208,8 +204,7 @@ The target function covariance for likelihood calculations.
@@ -231,12 +226,11 @@ The target function covariance for likelihood calculations.
-
!!]
return
end function starFormingMainSequenceConstructorParameters
- function starFormingMainSequenceConstructorFile(fileName,label,comment,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function starFormingMainSequenceConstructorFile(fileName,label,comment,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Constructor for the ``starFormingMainSequence'' output analysis class which reads all required properties from file.
!!}
@@ -256,7 +250,6 @@ function starFormingMainSequenceConstructorFile(fileName,label,comment,galacticF
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisPropertyOperator_
class (outputAnalysisPropertyOperatorClass ), intent(inout), target :: outputAnalysisWeightPropertyOperator_
class (outputAnalysisDistributionOperatorClass), intent(inout), target :: outputAnalysisDistributionOperator_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , allocatable , dimension(: ) :: meanValueTarget , massesStellar
double precision , allocatable , dimension(:,:) :: meanCovarianceTarget
double precision :: massesStellarBinWidthLogarithmic
@@ -287,7 +280,7 @@ function starFormingMainSequenceConstructorFile(fileName,label,comment,galacticF
! Build the object.
!![
- self=starFormingMainSequenceConstructorInternal(label,comment,massesStellar,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_,targetLabel,meanValueTarget,meanCovarianceTarget{conditions})
+ self=starFormingMainSequenceConstructorInternal(label,comment,massesStellar,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,targetLabel,meanValueTarget,meanCovarianceTarget{conditions})
@@ -295,7 +288,7 @@ function starFormingMainSequenceConstructorFile(fileName,label,comment,galacticF
return
end function starFormingMainSequenceConstructorFile
- function starFormingMainSequenceConstructorInternal(label,comment,massesStellar,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_,targetLabel,meanValueTarget,meanCovarianceTarget,massesStellarBinWidthLogarithmic) result(self)
+ function starFormingMainSequenceConstructorInternal(label,comment,massesStellar,galacticFilter_,surveyGeometry_,cosmologyFunctions_,cosmologyFunctionsData,outputTimes_,outputAnalysisPropertyOperator_,outputAnalysisDistributionOperator_,outputAnalysisWeightPropertyOperator_,starFormationRateDisks_,starFormationRateSpheroids_,targetLabel,meanValueTarget,meanCovarianceTarget,massesStellarBinWidthLogarithmic) result(self)
!!{
Internal constructor for the ``starFormingMainSequence'' output analysis class.
!!}
@@ -323,7 +316,6 @@ function starFormingMainSequenceConstructorInternal(label,comment,massesStellar,
class (outputAnalysisDistributionOperatorClass ), intent(inout), target :: outputAnalysisDistributionOperator_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (varying_string ), optional , intent(in ) :: targetLabel
double precision , optional , dimension(: ), intent(in ) :: meanValueTarget , massesStellar
double precision , optional , dimension(:,:), intent(in ) :: meanCovarianceTarget
@@ -345,7 +337,7 @@ function starFormingMainSequenceConstructorInternal(label,comment,massesStellar,
integer (c_size_t ) :: iBin , bufferCount , &
& countMasses
!![
-
+
!!]
! Set properties needed for descriptor.
@@ -415,7 +407,7 @@ function starFormingMainSequenceConstructorInternal(label,comment,massesStellar,
!![
- nodePropertyExtractorMassStellar(galacticStructure_)
+ nodePropertyExtractorMassStellar()
!!]
diff --git a/source/output.analyses.star_forming_main_sequence.Schreiber2015.F90 b/source/output.analyses.star_forming_main_sequence.Schreiber2015.F90
index 04f5e407e2..b901773bf0 100644
--- a/source/output.analyses.star_forming_main_sequence.Schreiber2015.F90
+++ b/source/output.analyses.star_forming_main_sequence.Schreiber2015.F90
@@ -56,7 +56,6 @@ function starFormingMainSequenceSchreiber2015ConstructorParameters(parameters) r
!!}
use :: Cosmology_Parameters, only : cosmologyParameters , cosmologyParametersClass
use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (outputAnalysisStarFormingMainSequenceSchreiber2015) :: self
@@ -66,7 +65,6 @@ function starFormingMainSequenceSchreiber2015ConstructorParameters(parameters) r
class (outputTimesClass ), pointer :: outputTimes_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& weightSystematicErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -133,9 +131,8 @@ function starFormingMainSequenceSchreiber2015ConstructorParameters(parameters) r
-
!!]
- self=outputAnalysisStarFormingMainSequenceSchreiber2015(redshiftIndex,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisStarFormingMainSequenceSchreiber2015(redshiftIndex,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
@@ -143,12 +140,11 @@ function starFormingMainSequenceSchreiber2015ConstructorParameters(parameters) r
-
!!]
return
end function starFormingMainSequenceSchreiber2015ConstructorParameters
- function starFormingMainSequenceSchreiber2015ConstructorInternal(redshiftIndex,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function starFormingMainSequenceSchreiber2015ConstructorInternal(redshiftIndex,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Internal constructor for the ``starFormingMainSequenceSchreiber2015'' output analysis class.
!!}
@@ -174,7 +170,6 @@ function starFormingMainSequenceSchreiber2015ConstructorInternal(redshiftIndex,r
class (outputTimesClass ), intent(inout), target :: outputTimes_
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (galacticFilterStellarMass ) , pointer :: galacticFilterStellarMass_
type (galacticFilterStarFormationRate ) , pointer :: galacticFilterStarFormationRate_
type (galacticFilterAll ) , pointer :: galacticFilter_
@@ -332,8 +327,7 @@ function starFormingMainSequenceSchreiber2015ConstructorInternal(redshiftIndex,r
& outputAnalysisDistributionOperator_ , &
& outputAnalysisWeightPropertyOperator_, &
& starFormationRateDisks_ , &
- & starFormationRateSpheroids_ , &
- & galacticStructure_ &
+ & starFormationRateSpheroids_ &
& )
!![
diff --git a/source/output.analyses.star_forming_main_sequence.Wagner2016.F90 b/source/output.analyses.star_forming_main_sequence.Wagner2016.F90
index ff941d2f8d..0a8b7ec7b7 100644
--- a/source/output.analyses.star_forming_main_sequence.Wagner2016.F90
+++ b/source/output.analyses.star_forming_main_sequence.Wagner2016.F90
@@ -54,7 +54,6 @@
!!}
private
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -78,23 +77,19 @@ function starFormingMainSequenceWagner2016ConstructorParameters(parameters) resu
!!{
Constructor for the ``starFormingMainSequenceWagner2016'' output analysis class which takes a parameter set as input.
!!}
- use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Input_Parameters , only : inputParameter , inputParameters
+ use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
+ use :: Input_Parameters , only : inputParameter , inputParameters
implicit none
type (outputAnalysisStarFormingMainSequenceWagner2016) :: self
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
class (outputTimesClass ), pointer :: outputTimes_
class (starFormationRateDisksClass ), pointer :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), pointer :: starFormationRateSpheroids_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision , allocatable , dimension(:) :: randomErrorPolynomialCoefficient , systematicErrorPolynomialCoefficient, &
& weightSystematicErrorPolynomialCoefficient
double precision :: randomErrorMinimum , randomErrorMaximum
@@ -156,36 +151,31 @@ function starFormingMainSequenceWagner2016ConstructorParameters(parameters) resu
-
-
!!]
- self=outputAnalysisStarFormingMainSequenceWagner2016(enumerationWagner2016SSFRRedshiftRangeEncode(char(redshiftRange),includesPrefix=.false.),enumerationWagner2016SSFRGalaxyTypeEncode(char(galaxyType),includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_)
+ self=outputAnalysisStarFormingMainSequenceWagner2016(enumerationWagner2016SSFRRedshiftRangeEncode(char(redshiftRange),includesPrefix=.false.),enumerationWagner2016SSFRGalaxyTypeEncode(char(galaxyType),includesPrefix=.false.),randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_)
!![
-
-
!!]
return
end function starFormingMainSequenceWagner2016ConstructorParameters
- function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,galaxyType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_,galacticStructure_) result(self)
+ function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,galaxyType,randomErrorMinimum,randomErrorMaximum,randomErrorPolynomialCoefficient,systematicErrorPolynomialCoefficient,weightSystematicErrorPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_,starFormationRateDisks_,starFormationRateSpheroids_) result(self)
!!{
Internal constructor for the ``starFormingMainSequenceWagner2016'' output analysis class.
!!}
use :: Error , only : Error_Report
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Input_Paths , only : inputPath , pathTypeDataStatic
use :: Output_Times , only : outputTimesClass
use :: Statistics_NBody_Halo_Mass_Errors , only : nbodyHaloMassErrorClass
@@ -211,8 +201,6 @@ function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,gala
class (starFormationRateDisksClass ), intent(in ), target :: starFormationRateDisks_
class (starFormationRateSpheroidsClass ), intent(in ), target :: starFormationRateSpheroids_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (galacticFilterHaloNotIsolated ) , pointer :: galacticFilterIsSubhalo_
type (galacticFilterHighPass ) , pointer :: galacticFilterHostHaloMass_
type (galacticFilterStellarMass ) , pointer :: galacticFilterStellarMass_
@@ -235,7 +223,7 @@ function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,gala
type (varying_string ) :: fileName , label , &
& description
!![
-
+
!!]
! Construct file name and label for the analysis.
@@ -322,7 +310,7 @@ function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,gala
!!]
allocate(nodePropertyExtractorHostMass_)
!![
-
+
!!]
allocate(nodePropertyExtractorHost_)
!![
@@ -413,8 +401,7 @@ function starFormingMainSequenceWagner2016ConstructorInternal(redshiftRange,gala
& outputAnalysisDistributionOperator_ , &
& outputAnalysisWeightPropertyOperator_, &
& starFormationRateDisks_ , &
- & starFormationRateSpheroids_ , &
- & galacticStructure_ &
+ & starFormationRateSpheroids_ &
& )
!![
@@ -443,7 +430,6 @@ subroutine starFormingMainSequenceWagner2016Destructor(self)
!![
-
!!]
return
diff --git a/source/output.analyses.stellar_vs_halo_mass_relation.COSMOS_Leauthaud2012.F90 b/source/output.analyses.stellar_vs_halo_mass_relation.COSMOS_Leauthaud2012.F90
index e942156d28..c0ccf61222 100644
--- a/source/output.analyses.stellar_vs_halo_mass_relation.COSMOS_Leauthaud2012.F90
+++ b/source/output.analyses.stellar_vs_halo_mass_relation.COSMOS_Leauthaud2012.F90
@@ -36,8 +36,6 @@
class (outputAnalysisClass ), pointer :: outputAnalysis_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
logical :: computeScatter
@@ -70,7 +68,6 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorParameters(parameters)
use :: Cosmology_Functions , only : cosmologyFunctionsClass
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Virial_Density_Contrast , only : virialDensityContrastClass
use :: Input_Parameters , only : inputParameters
implicit none
@@ -79,8 +76,6 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorParameters(parameters)
double precision , allocatable , dimension(:) :: systematicErrorPolynomialCoefficient, systematicErrorMassHaloPolynomialCoefficient
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
class (outputTimesClass ), pointer :: outputTimes_
integer :: redshiftInterval
@@ -140,26 +135,22 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorParameters(parameters)
-
-
!!]
! Build the object.
- self=outputAnalysisStellarVsHaloMassRelationLeauthaud2012(redshiftInterval,likelihoodBins,computeScatter,systematicErrorPolynomialCoefficient,systematicErrorMassHaloPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,galacticStructure_,outputTimes_)
+ self=outputAnalysisStellarVsHaloMassRelationLeauthaud2012(redshiftInterval,likelihoodBins,computeScatter,systematicErrorPolynomialCoefficient,systematicErrorMassHaloPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_)
!![
-
-
!!]
return
end function stellarVsHaloMassRelationLeauthaud2012ConstructorParameters
- function stellarVsHaloMassRelationLeauthaud2012ConstructorInternal(redshiftInterval,likelihoodBins,computeScatter,systematicErrorPolynomialCoefficient,systematicErrorMassHaloPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_,galacticStructure_,outputTimes_) result (self)
+ function stellarVsHaloMassRelationLeauthaud2012ConstructorInternal(redshiftInterval,likelihoodBins,computeScatter,systematicErrorPolynomialCoefficient,systematicErrorMassHaloPolynomialCoefficient,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,outputTimes_) result (self)
!!{
Constructor for the ``stellarVsHaloMassRelationLeauthaud2012'' output analysis class for internal use.
!!}
@@ -195,8 +186,6 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorInternal(redshiftInter
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
class (outputTimesClass ), intent(inout), target :: outputTimes_
integer (c_size_t ), parameter :: massHaloCount =26
double precision , allocatable , dimension(: ) :: massHalo , massStellarDataLogarithmic , &
@@ -241,7 +230,7 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorInternal(redshiftInter
type (table1DGeneric ) :: interpolator
character (len=4 ) :: redshiftMinimumLabel , redshiftMaximumLabel
!![
-
+
!!]
! Construct survey geometry.
@@ -441,34 +430,34 @@ function stellarVsHaloMassRelationLeauthaud2012ConstructorInternal(redshiftInter
! Create a stellar mass weight property extractor.
allocate(outputAnalysisWeightPropertyExtractor_ )
!![
-
+
!!]
allocate(outputAnalysisWeightPropertyOperator_ )
!![
-
+
!!]
allocate(outputAnalysisWeightPropertyOperatorNormalized_ )
!![
-
+
!!]
! Build weight operator.
allocate (outputAnalysisWeightOperator_ )
!![
-
+
!!]
! Build anti-log10() property operator.
allocate(outputAnalysisPropertyUnoperator_ )
!![
-
+
!!]
! Create a halo mass weight property extractor.
allocate(virialDensityContrastDefinition_ )
!![
-
+
!!]
allocate(nodePropertyExtractor_ )
!![
-
+
!!]
! Build the object.
if (computeScatter) then
@@ -633,8 +622,6 @@ subroutine stellarVsHaloMassRelationLeauthaud2012Destructor(self)
-
-
!!]
diff --git a/source/output.analyses.subhalo_mass_function.F90 b/source/output.analyses.subhalo_mass_function.F90
index 9404452f60..9a16ac577e 100644
--- a/source/output.analyses.subhalo_mass_function.F90
+++ b/source/output.analyses.subhalo_mass_function.F90
@@ -44,7 +44,6 @@
class (outputTimesClass ), pointer :: outputTimes_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
type (outputAnalysisVolumeFunction1D), pointer :: volumeFunctionsSubHalos => null(), volumeFunctionsHostHalos => null()
double precision , allocatable, dimension(: ) :: massRatios , massFunction , &
& massFunctionTarget
@@ -95,7 +94,6 @@ function subhaloMassFunctionConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
double precision :: massRatioMinimum , massRatioMaximum , &
& redshift , negativeBinomialScatterFractional
integer (c_size_t ) :: countMassRatios
@@ -150,31 +148,29 @@ function subhaloMassFunctionConstructorParameters(parameters) result(self)
-
!!]
if (parameters%isPresent('fileName')) then
!![
- self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,fileName,negativeBinomialScatterFractional{conditions})
+ self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,fileName,negativeBinomialScatterFractional{conditions})
!!]
else
- self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional)
+ self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional)
end if
!![
-
!!]
return
end function subhaloMassFunctionConstructorParameters
- function subhaloMassFunctionConstructorFile(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,fileName,negativeBinomialScatterFractional,redshift) result (self)
+ function subhaloMassFunctionConstructorFile(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,fileName,negativeBinomialScatterFractional,redshift) result (self)
!!{
Constructor for the ``subhaloMassFunction'' output analysis class for internal use.
!!}
@@ -192,7 +188,6 @@ function subhaloMassFunctionConstructorFile(outputTimes_,virialDensityContrastDe
class (virialDensityContrastClass ), intent(in ) :: virialDensityContrast_ , virialDensityContrastDefinition_
class (cosmologyParametersClass ), intent(inout) :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ) :: darkMatterProfileDMO_
double precision , intent(in ), optional :: redshift
double precision , allocatable , dimension(: ) :: massRatiosTarget , massFunctionTarget , &
& massFunctionErrorTarget
@@ -227,14 +222,14 @@ function subhaloMassFunctionConstructorFile(outputTimes_,virialDensityContrastDe
do i=1_c_size_t,countMassRatios
massFunctionCovarianceTarget(i,i)=massFunctionErrorTarget(i)**2
end do
- self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,time,massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional,massFunctionTarget,massFunctionCovarianceTarget,labelTarget)
+ self=outputAnalysisSubhaloMassFunction(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,time,massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional,massFunctionTarget,massFunctionCovarianceTarget,labelTarget)
!![
!!]
return
end function subhaloMassFunctionConstructorFile
- function subhaloMassFunctionConstructorInternal(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,time,massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional,massFunctionTarget,massFunctionCovarianceTarget,labelTarget) result (self)
+ function subhaloMassFunctionConstructorInternal(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,time,massRatioMinimum,massRatioMaximum,countMassRatios,negativeBinomialScatterFractional,massFunctionTarget,massFunctionCovarianceTarget,labelTarget) result (self)
!!{
Constructor for the ``subhaloMassFunction'' output analysis class for internal use.
!!}
@@ -260,7 +255,6 @@ function subhaloMassFunctionConstructorInternal(outputTimes_,virialDensityContra
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_ , virialDensityContrastDefinition_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
double precision , intent(in ), dimension(:) , optional :: massFunctionTarget
double precision , intent(in ), dimension(:,:), optional :: massFunctionCovarianceTarget
type (varying_string ), intent(in ) , optional :: labelTarget
@@ -286,7 +280,7 @@ function subhaloMassFunctionConstructorInternal(outputTimes_,virialDensityContra
double precision , parameter :: massHostLogarithmicMaximum =1.0d2
integer (c_size_t ) :: i
!![
-
+
!!]
! Initialize.
@@ -311,8 +305,8 @@ function subhaloMassFunctionConstructorInternal(outputTimes_,virialDensityContra
!![
-
-
+
+
@@ -470,7 +464,6 @@ subroutine subhaloMassFunctionDestructor(self)
-
!!]
diff --git a/source/output.analyses.subhalo_radial_distribution.F90 b/source/output.analyses.subhalo_radial_distribution.F90
index d2fa51ff8a..6fe7f4a810 100644
--- a/source/output.analyses.subhalo_radial_distribution.F90
+++ b/source/output.analyses.subhalo_radial_distribution.F90
@@ -44,7 +44,6 @@
class (outputTimesClass ), pointer :: outputTimes_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
type (outputAnalysisVolumeFunction1D), pointer :: volumeFunctionsSubHalos => null(), volumeFunctionsHostHalos => null()
double precision , allocatable, dimension(: ) :: radiiFractional , radialDistribution , &
& radialDistributionTarget
@@ -95,7 +94,6 @@ function subhaloRadialDistributionConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (outputTimesClass ), pointer :: outputTimes_
class (virialDensityContrastClass ), pointer :: virialDensityContrastDefinition_ , virialDensityContrast_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
double precision :: radiusFractionMinimum , radiusFractionMaximum , &
& redshift , negativeBinomialScatterFractional, &
& massRatioThreshold
@@ -157,31 +155,29 @@ function subhaloRadialDistributionConstructorParameters(parameters) result(self)
-
!!]
if (parameters%isPresent('fileName')) then
!![
- self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,fileName,negativeBinomialScatterFractional{conditions})
+ self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,fileName,negativeBinomialScatterFractional{conditions})
!!]
else
- self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional)
+ self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift)),radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional)
end if
!![
-
!!]
return
end function subhaloRadialDistributionConstructorParameters
- function subhaloRadialDistributionConstructorFile(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,fileName,negativeBinomialScatterFractional,redshift) result (self)
+ function subhaloRadialDistributionConstructorFile(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,fileName,negativeBinomialScatterFractional,redshift) result (self)
!!{
Constructor for the ``subhaloRadialDistribution'' output analysis class for internal use.
!!}
@@ -199,7 +195,6 @@ function subhaloRadialDistributionConstructorFile(outputTimes_,virialDensityCont
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_ , virialDensityContrastDefinition_
class (cosmologyParametersClass ), intent(inout), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(inout), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
double precision , intent(in ), optional :: redshift
double precision , allocatable , dimension(: ) :: radiiFractionalTarget , radialDistributionTarget , &
& radialDistributionErrorTarget
@@ -236,14 +231,14 @@ function subhaloRadialDistributionConstructorFile(outputTimes_,virialDensityCont
do i=1_c_size_t,countRadiiFractional
radialDistributionCovarianceTarget(i,i)=radialDistributionErrorTarget(i)**2
end do
- self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,time,radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional,radialDistributionTarget,radialDistributionCovarianceTarget,labelTarget)
+ self=outputAnalysisSubhaloRadialDistribution(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,time,radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional,radialDistributionTarget,radialDistributionCovarianceTarget,labelTarget)
!![
!!]
return
end function subhaloRadialDistributionConstructorFile
- function subhaloRadialDistributionConstructorInternal(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,darkMatterProfileDMO_,time,radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional,radialDistributionTarget,radialDistributionCovarianceTarget,labelTarget) result (self)
+ function subhaloRadialDistributionConstructorInternal(outputTimes_,virialDensityContrastDefinition_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_,time,radiusFractionMinimum,radiusFractionMaximum,countRadiiFractional,massRatioThreshold,negativeBinomialScatterFractional,radialDistributionTarget,radialDistributionCovarianceTarget,labelTarget) result (self)
!!{
Constructor for the ``subhaloRadialDistribution'' output analysis class for internal use.
!!}
@@ -270,7 +265,6 @@ function subhaloRadialDistributionConstructorInternal(outputTimes_,virialDensity
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_ , virialDensityContrastDefinition_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
double precision , intent(in ), dimension(:) , optional :: radialDistributionTarget
double precision , intent(in ), dimension(:,:), optional :: radialDistributionCovarianceTarget
type (varying_string ), intent(in ) , optional :: labelTarget
@@ -296,7 +290,7 @@ function subhaloRadialDistributionConstructorInternal(outputTimes_,virialDensity
double precision , parameter :: massHostLogarithmicMaximum =1.0d2
integer (c_size_t ) :: i
!![
-
+
!!]
! Initialize.
@@ -322,8 +316,8 @@ function subhaloRadialDistributionConstructorInternal(outputTimes_,virialDensity
!![
-
-
+
+
@@ -481,7 +475,6 @@ subroutine subhaloRadialDistributionDestructor(self)
-
!!]
diff --git a/source/output.analyses.subhalo_velocity_maximum_vs_mass.F90 b/source/output.analyses.subhalo_velocity_maximum_vs_mass.F90
index c95f18d12b..7e0a330d48 100644
--- a/source/output.analyses.subhalo_velocity_maximum_vs_mass.F90
+++ b/source/output.analyses.subhalo_velocity_maximum_vs_mass.F90
@@ -174,7 +174,7 @@ function subhaloVMaxVsMassConstructorFile(outputTimes_,virialDensityContrastDefi
type (varying_string ) :: labelTarget
type (hdf5Object ) :: file , velocityMaximumVsMassGroup
!![
-
+
!!]
! Read properties from the file.
@@ -274,7 +274,7 @@ function subhaloVMaxVsMassConstructorInternal(outputTimes_,virialDensityContrast
!![
-
+
diff --git a/source/output.analyses.tidal_tracks.velocity_maximum.F90 b/source/output.analyses.tidal_tracks.velocity_maximum.F90
index 0f599473d8..71615117d4 100644
--- a/source/output.analyses.tidal_tracks.velocity_maximum.F90
+++ b/source/output.analyses.tidal_tracks.velocity_maximum.F90
@@ -135,14 +135,16 @@ subroutine tidalTracksVelocityMaximumAnalyze(self,node,iOutput)
!!{
Analyze the maximum velocity tidal track.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic, nodeComponentSatellite
- !$ use :: OMP_Lib , only : OMP_Set_Lock , OMP_Unset_Lock
+ use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentSatellite
+ use :: Mass_Distributions, only : massDistributionClass
+ !$ use :: OMP_Lib , only : OMP_Set_Lock , OMP_Unset_Lock
implicit none
class (outputAnalysisTidalTracksVelocityMaximum), intent(inout) :: self
type (treeNode ), intent(inout) :: node
integer (c_size_t ), intent(in ) :: iOutput
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionUnheated
double precision , dimension(:), allocatable :: fractionMassBound_ , fractionVelocityMaximum_ , &
& fractionVelocityMaximumTarget_
double precision :: fractionMassBound , fractionVelocityMaximum , &
@@ -151,10 +153,16 @@ subroutine tidalTracksVelocityMaximumAnalyze(self,node,iOutput)
! Skip non-satellites.
if (.not.node%isSatellite()) return
! Extract the bound mass and maximum velocity fractions.
- basic => node%basic()
- satellite => node%satellite()
- fractionMassBound = satellite %boundMass ( )/basic %mass ( )
- fractionVelocityMaximum = self %darkMatterProfileDMO_%circularVelocityMaximum(node)/self %darkMatterProfileDMOUnheated%circularVelocityMaximum(node)
+ basic => node %basic ( )
+ satellite => node %satellite ( )
+ massDistribution_ => self %darkMatterProfileDMO_ %get(node)
+ massDistributionUnheated => self %darkMatterProfileDMOUnheated%get(node)
+ fractionMassBound = satellite %boundMass ( )/basic %mass ()
+ fractionVelocityMaximum = massDistribution_%velocityRotationCurveMaximum ( )/massDistributionUnheated%velocityRotationCurveMaximum()
+ !![
+
+
+ !!]
! Evaluate the target value. Uses the Penarrubia et al. (2010) fitting function.
fractionVelocityMaximumTarget =+ 2.0d0 **self%mu &
& * fractionMassBound **self%eta &
diff --git a/source/ram_pressure_stripping.mass_loss_rate.simple_cylindrical.F90 b/source/ram_pressure_stripping.mass_loss_rate.simple_cylindrical.F90
index 3f41ad94e5..c1f5a35631 100644
--- a/source/ram_pressure_stripping.mass_loss_rate.simple_cylindrical.F90
+++ b/source/ram_pressure_stripping.mass_loss_rate.simple_cylindrical.F90
@@ -22,7 +22,6 @@
!!}
use :: Hot_Halo_Ram_Pressure_Forces, only : hotHaloRamPressureForceClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -50,7 +49,6 @@
!!}
private
class (hotHaloRamPressureForceClass), pointer :: hotHaloRamPressureForce_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: rateFractionalMaximum , beta
contains
final :: simpleCylindricalDestructor
@@ -77,7 +75,6 @@ function simpleCylindricalConstructorParameters(parameters) result(self)
type (ramPressureStrippingSimpleCylindrical) :: self
type (inputParameters ), intent(inout) :: parameters
class (hotHaloRamPressureForceClass ), pointer :: hotHaloRamPressureForce_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: rateFractionalMaximum , beta
!![
@@ -94,18 +91,16 @@ function simpleCylindricalConstructorParameters(parameters) result(self)
-
!!]
- self=ramPressureStrippingSimpleCylindrical(rateFractionalMaximum,beta,hotHaloRamPressureForce_,galacticStructure_)
+ self=ramPressureStrippingSimpleCylindrical(rateFractionalMaximum,beta,hotHaloRamPressureForce_)
!![
-
!!]
return
end function simpleCylindricalConstructorParameters
- function simpleCylindricalConstructorInternal(rateFractionalMaximum,beta,hotHaloRamPressureForce_,galacticStructure_) result(self)
+ function simpleCylindricalConstructorInternal(rateFractionalMaximum,beta,hotHaloRamPressureForce_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily simpleCylindrical} model of ram pressure stripping class.
!!}
@@ -113,9 +108,8 @@ function simpleCylindricalConstructorInternal(rateFractionalMaximum,beta,hotHalo
type (ramPressureStrippingSimpleCylindrical) :: self
double precision , intent(in ) :: rateFractionalMaximum , beta
class (hotHaloRamPressureForceClass ), intent(in ), target :: hotHaloRamPressureForce_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -130,7 +124,6 @@ subroutine simpleCylindricalDestructor(self)
!![
-
!!]
return
end subroutine simpleCylindricalDestructor
@@ -152,23 +145,26 @@ double precision function simpleCylindricalRateMassLoss(self,component)
\end{equation}
is the gravitational restoring force at the half-mass radius, $r_\mathrm{1/2}$.
!!}
- use :: Display , only : displayGreen , displayBlue , displayMagenta, displayReset
- use :: Galactic_Structure_Options , only : componentTypeDisk , coordinateSystemCylindrical , massTypeAll , massTypeGaseous, &
- & enumerationComponentTypeType
- use :: Galacticus_Nodes , only : nodeComponentDisk , treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
+ use :: Display , only : displayGreen , displayBlue , displayMagenta, displayReset
+ use :: Galactic_Structure_Options , only : componentTypeDisk , enumerationComponentTypeType , massTypeAll , massTypeGaseous
+ use :: Galacticus_Nodes , only : nodeComponentDisk , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : kilo
implicit none
class (ramPressureStrippingSimpleCylindrical), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
type (treeNode ), pointer :: node
+ class (massDistributionClass ), pointer :: massDistributionGaseous, massDistributionTotal
type (enumerationComponentTypeType ) :: componentType
- double precision :: forceGravitational , forceRamPressure , &
- & rateMassLossFractional, radiusHalfMass , &
- & surfaceDensityGas , surfaceDensityTotal, &
- & timeDynamical , radius , &
- & radiusHalfMass , velocity , &
+ type (coordinateCylindrical ) :: coordinates
+ double precision :: forceGravitational , forceRamPressure , &
+ & rateMassLossFractional , radiusHalfMass , &
+ & surfaceDensityGas , surfaceDensityTotal, &
+ & timeDynamical , radius , &
+ & radiusHalfMass , velocity , &
& massGas
! Assume no mass loss rate due to ram pressure by default.
@@ -198,20 +194,15 @@ double precision function simpleCylindricalRateMassLoss(self,component)
& )
end select
! Compute the surface densities at the half mass radius.
- surfaceDensityGas = self%galacticStructure_%surfaceDensity( &
- & node , &
- & [radiusHalfMass,0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & massType =massTypeGaseous , &
- & componentType =componentType &
- & )
- surfaceDensityTotal = self%galacticStructure_%surfaceDensity( &
- & node , &
- & [radiusHalfMass,0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical, &
- & massType =massTypeAll , &
- & componentType =componentType &
- & )
+ coordinates = [radiusHalfMass,0.0d0,0.0d0]
+ massDistributionGaseous => node %massDistribution(massType=massTypeGaseous,componentType=componentType)
+ massDistributionTotal => node %massDistribution(massType=massTypeAll ,componentType=componentType)
+ surfaceDensityGas = massDistributionGaseous%surfaceDensity ( coordinates )
+ surfaceDensityTotal = massDistributionTotal %surfaceDensity ( coordinates )
+ !![
+
+
+ !!]
! Compute the gravitational restoring force in the midplane.
forceGravitational = +2.0d0 &
& *Pi &
diff --git a/source/ram_pressure_stripping.mass_loss_rate.simple_spherical.F90 b/source/ram_pressure_stripping.mass_loss_rate.simple_spherical.F90
index 299e98679c..c845ce5da0 100644
--- a/source/ram_pressure_stripping.mass_loss_rate.simple_spherical.F90
+++ b/source/ram_pressure_stripping.mass_loss_rate.simple_spherical.F90
@@ -22,7 +22,6 @@
!!}
use :: Hot_Halo_Ram_Pressure_Forces, only : hotHaloRamPressureForceClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -51,7 +50,6 @@
!!}
private
class (hotHaloRamPressureForceClass), pointer :: hotHaloRamPressureForce_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: rateFractionalMaximum , beta
contains
final :: simpleSphericalDestructor
@@ -78,7 +76,6 @@ function simpleSphericalConstructorParameters(parameters) result(self)
type (ramPressureStrippingSimpleSpherical) :: self
type (inputParameters ), intent(inout) :: parameters
class (hotHaloRamPressureForceClass ), pointer :: hotHaloRamPressureForce_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: rateFractionalMaximum , beta
!![
@@ -95,18 +92,16 @@ function simpleSphericalConstructorParameters(parameters) result(self)
-
!!]
- self=ramPressureStrippingSimpleSpherical(rateFractionalMaximum,beta,hotHaloRamPressureForce_,galacticStructure_)
+ self=ramPressureStrippingSimpleSpherical(rateFractionalMaximum,beta,hotHaloRamPressureForce_)
!![
-
!!]
return
end function simpleSphericalConstructorParameters
- function simpleSphericalConstructorInternal(rateFractionalMaximum,beta,hotHaloRamPressureForce_,galacticStructure_) result(self)
+ function simpleSphericalConstructorInternal(rateFractionalMaximum,beta,hotHaloRamPressureForce_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily simpleSpherical} model of ram pressure stripping of spheroids class.
!!}
@@ -114,9 +109,8 @@ function simpleSphericalConstructorInternal(rateFractionalMaximum,beta,hotHaloRa
type (ramPressureStrippingSimpleSpherical) :: self
double precision , intent(in ) :: rateFractionalMaximum , beta
class (hotHaloRamPressureForceClass ), intent(in ), target :: hotHaloRamPressureForce_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -131,7 +125,6 @@ subroutine simpleSphericalDestructor(self)
!![
-
!!]
return
end subroutine simpleSphericalDestructor
@@ -153,22 +146,25 @@ double precision function simpleSphericalRateMassLoss(self,component)
\end{equation}
is the gravitational restoring force at the half-mass radius, $r_\mathrm{1/2}$ \citep{takeda_ram_1984}.
!!}
- use :: Display , only : displayGreen , displayBlue , displayMagenta, displayReset
- use :: Galactic_Structure_Options , only : componentTypeSpheroid , coordinateSystemSpherical , massTypeAll , massTypeGaseous, &
- & enumerationComponentTypeType
- use :: Galacticus_Nodes , only : nodeComponentSpheroid , treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Display , only : displayGreen , displayBlue , displayMagenta, displayReset
+ use :: Galactic_Structure_Options , only : componentTypeSpheroid, enumerationComponentTypeType , massTypeAll , massTypeGaseous
+ use :: Galacticus_Nodes , only : nodeComponentSpheroid, treeNode
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
implicit none
class (ramPressureStrippingSimpleSpherical), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
type (treeNode ), pointer :: node
+ class (massDistributionClass ), pointer :: massDistributionGaseous, massDistributionTotal
type (enumerationComponentTypeType ) :: componentType
- double precision :: forceGravitational , forceRamPressure, &
- & rateMassLossFractional, radiusHalfMass , &
- & densityGas , massHalf , &
- & timeDynamical , radius , &
- & radiusHalfMass , velocity , &
+ type (coordinateSpherical ) :: coordinates
+ double precision :: forceGravitational , forceRamPressure, &
+ & rateMassLossFractional , radiusHalfMass , &
+ & densityGas , massHalf , &
+ & timeDynamical , radius , &
+ & radiusHalfMass , velocity , &
& massGas
! Assume no mass loss rate due to ram pressure by default.
@@ -198,19 +194,15 @@ double precision function simpleSphericalRateMassLoss(self,component)
& )
end select
! Compute the densities at the half mass radius.
- densityGas=self%galacticStructure_%density ( &
- & node , &
- & [radiusHalfMass,0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemSpherical, &
- & massType =massTypeGaseous , &
- & componentType =componentType &
- & )
- massHalf =self%galacticStructure_%massEnclosed( &
- & node , &
- & radiusHalfMass , &
- & massType =massTypeAll , &
- & componentType =componentType &
- & )
+ coordinates = [radiusHalfMass,0.0d0,0.0d0]
+ massDistributionGaseous => node %massDistribution (massType=massTypeGaseous,componentType=componentType)
+ massDistributionTotal => node %massDistribution (massType=massTypeAll ,componentType=componentType)
+ densityGas = massDistributionGaseous%density ( coordinates )
+ massHalf = massDistributionTotal %massEnclosedBySphere( radiusHalfMass )
+ !![
+
+
+ !!]
! Compute the gravitational restoring force.
if (massHalf > 0.0d0 .and. densityGas > 0.0d0) then
forceGravitational = +4.0d0 &
diff --git a/source/satellites.deceleration_SIDM.Kummer2018.F90 b/source/satellites.deceleration_SIDM.Kummer2018.F90
index e714051dc6..7b46f06817 100644
--- a/source/satellites.deceleration_SIDM.Kummer2018.F90
+++ b/source/satellites.deceleration_SIDM.Kummer2018.F90
@@ -25,7 +25,6 @@
use :: Dark_Matter_Particles , only : darkMatterParticleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
use :: Numerical_Interpolation , only : interpolator
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -40,7 +39,6 @@
private
class (darkMatterParticleClass ), pointer :: darkMatterParticle_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (interpolator ), allocatable :: decelerationFactor
double precision :: rateScatteringNormalization , xMaximum
contains
@@ -75,24 +73,21 @@ function kummer2018ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class(darkMatterParticleClass ), pointer :: darkMatterParticle_
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=satelliteDecelerationSIDMKummer2018(darkMatterParticle_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteDecelerationSIDMKummer2018(darkMatterParticle_,darkMatterProfileDMO_)
!![
-
!!]
return
end function kummer2018ConstructorParameters
- function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily kummer2018} satellite deceleration due to dark matter self-interactions
class.
@@ -104,9 +99,8 @@ function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_
type (satelliteDecelerationSIDMKummer2018) :: self
class(darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
select type (darkMatterParticle_ => self%darkMatterParticle_)
@@ -138,7 +132,6 @@ subroutine kummer2018Destructor(self)
!![
-
!!]
return
end subroutine kummer2018Destructor
@@ -147,8 +140,10 @@ function kummer2018Acceleration(self,node)
!!{
Return a deceleration for satellites due to dark matter self-interactions using the formulation of \cite{kummer_effective_2018}.
!!}
+ use :: Coordinates , only : coordinateSpherical , coordinateCartesian , assignment(=)
use :: Galactic_Structure_Options , only : coordinateSystemCartesian , radiusLarge
use :: Galacticus_Nodes , only : nodeComponentSatellite , nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Vectors , only : Vector_Magnitude
implicit none
@@ -158,29 +153,38 @@ function kummer2018Acceleration(self,node)
class (nodeComponentSatellite ), pointer :: satellite
class (nodeComponentBasic ), pointer :: basic
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionHost_
+ class (kinematicsDistributionClass ), pointer :: kinematics_ , kinematicsHost_
double precision , dimension(3) :: position , velocity
double precision :: radiusOrbital , speedOrbital , &
& densityHost , rateScattering , &
- & potentialHalfMass , potentialBoundary , &
& massBoundary , radiusBoundary , &
& velocityEscape , speedHalfMass , &
& velocityDispersionHost, velocityDispersionSatellite, &
& x , radiusHalfMass , &
& velocityDispersion , dispersionFactor , &
& potentialEscape
-
+ type (coordinateSpherical ) :: coordinates , coordinatesHost , &
+ & coordinatesBoundary , coordinatesHalfMass
+ type (coordinateCartesian ) :: coordinatesCartesian
+
! Set zero acceleration by default.
kummer2018Acceleration=0.0d0
! If the scattering cross section is zero, we can return immediately.
if (self%rateScatteringNormalization == 0.0d0) return
! Evaluate satellite and host properties.
- nodeHost => node %mergesWith( )
- satellite => node %satellite ( )
- position = satellite %position ( )
- velocity = satellite %velocity ( )
- radiusOrbital = Vector_Magnitude ( position )
- speedOrbital = Vector_Magnitude ( velocity )
- densityHost = self %galacticStructure_%density (nodeHost,position,coordinateSystemCartesian)
+ nodeHost => node %mergesWith ( )
+ satellite => node %satellite ( )
+ massDistributionHost_ => nodeHost %massDistribution( )
+ position = satellite %position ( )
+ velocity = satellite %velocity ( )
+ radiusOrbital = Vector_Magnitude ( position)
+ speedOrbital = Vector_Magnitude ( velocity)
+ coordinatesCartesian = position
+ densityHost = massDistributionHost_%density (coordinatesCartesian)
+ !![
+
+ !!]
! Find the escape velocity from the half-mass radius of the subhalo. This is equal to the potential difference between the
! half-mass radius and outer boundary of the subhalo, plus the potential difference from the outer boundary to infinity (for
! which we can treat the subhalo as a point mass).
@@ -195,16 +199,16 @@ function kummer2018Acceleration(self,node)
& basic % mass() &
& )
if (massBoundary > 0.0d0) then
- radiusBoundary =self%galacticStructure_%radiusEnclosingMass(node,mass = massBoundary )
- radiusHalfMass =self%galacticStructure_%radiusEnclosingMass(node,mass =0.5d0*massBoundary )
+ massDistribution_ => node %massDistribution ( )
+ radiusBoundary = massDistribution_%radiusEnclosingMass(mass= massBoundary)
+ radiusHalfMass = massDistribution_%radiusEnclosingMass(mass=0.5d0*massBoundary)
if (radiusBoundary < 0.5d0*radiusLarge) then
- potentialBoundary=self%galacticStructure_%potential (node,radius= radiusBoundary)
- potentialHalfMass=self%galacticStructure_%potential (node,radius= radiusHalfMass)
- potentialEscape =+potentialBoundary &
- & -potentialHalfMass &
- & +gravitationalConstantGalacticus &
- & *massBoundary &
- & /radiusBoundary
+ coordinatesBoundary=[radiusBoundary,0.0d0,0.0d0]
+ coordinatesHalfMass=[radiusHalfMass,0.0d0,0.0d0]
+ potentialEscape =+massDistribution_%potentialDifference(coordinatesBoundary,coordinatesHalfMass) &
+ & +gravitationalConstantGalacticus &
+ & *massBoundary &
+ & /radiusBoundary
if (potentialEscape > 0.0d0) then
velocityEscape=sqrt(2.0d0*potentialEscape)
else
@@ -213,6 +217,9 @@ function kummer2018Acceleration(self,node)
else
velocityEscape=0.0d0
end if
+ !![
+
+ !!]
! Get the speed of a host particle at the half-mass radius of the subhalo - this is the sum of the kinetic energy or host
! particles in the rest-frame of the subhalo, plus the energy they gain by falling in to the half-mass radius of the
! subhalo.
@@ -225,20 +232,32 @@ function kummer2018Acceleration(self,node)
if (x > self%xMaximum) call self%tabulate(x+1.0d0)
! Find the combined velocity dispersion of satellite and host, and evaluate the correction factor given in Appendix A of
! Kummer et al. (2018).
- velocityDispersionHost =+self%darkMatterProfileDMO_%radialVelocityDispersion(nodeHost,radiusOrbital )
- velocityDispersionSatellite=+self%darkMatterProfileDMO_%radialVelocityDispersion(node ,radiusHalfMass)
- velocityDispersion =+sqrt( &
- & +velocityDispersionHost **2 &
- & +velocityDispersionSatellite**2 &
- & )
- dispersionFactor =+1.0d0 &
- & /( &
- & +1.0d0 &
- & +( &
- & +velocityDispersion &
- & /speedHalfMass &
- & )**3 &
- & )
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ massDistributionHost_ => self %darkMatterProfileDMO_%get (nodeHost )
+ kinematics_ => massDistribution_ %kinematicsDistribution( )
+ kinematicsHost_ => massDistributionHost_ %kinematicsDistribution( )
+ coordinates = [radiusHalfMass,0.0d0,0.0d0]
+ coordinatesHost = [radiusOrbital ,0.0d0,0.0d0]
+ velocityDispersionHost = +kinematicsHost_ %velocityDispersion1D (coordinatesHost,massDistributionHost_)
+ velocityDispersionSatellite = +kinematics_ %velocityDispersion1D (coordinates ,massDistribution_ )
+ velocityDispersion = +sqrt( &
+ & +velocityDispersionHost **2 &
+ & +velocityDispersionSatellite**2 &
+ & )
+ dispersionFactor = +1.0d0 &
+ & /( &
+ & +1.0d0 &
+ & +( &
+ & +velocityDispersion &
+ & /speedHalfMass &
+ & )**3 &
+ & )
+ !![
+
+
+
+
+ !!]
! Evaluate the scattering rate and acceleration.
rateScattering = + speedOrbital &
& * densityHost &
diff --git a/source/satellites.dynamical_friction.acceleration.Chandrasekhar1943.F90 b/source/satellites.dynamical_friction.acceleration.Chandrasekhar1943.F90
index e6d19b77cd..76c6f5bf5b 100644
--- a/source/satellites.dynamical_friction.acceleration.Chandrasekhar1943.F90
+++ b/source/satellites.dynamical_friction.acceleration.Chandrasekhar1943.F90
@@ -25,7 +25,6 @@
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -51,7 +50,6 @@
private
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: logarithmCoulomb
contains
!![
@@ -84,7 +82,6 @@ function chandrasekhar1943ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: logarithmCoulomb
!![
@@ -96,19 +93,17 @@ function chandrasekhar1943ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteDynamicalFrictionChandrasekhar1943(logarithmCoulomb,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteDynamicalFrictionChandrasekhar1943(logarithmCoulomb,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
!!]
return
end function chandrasekhar1943ConstructorParameters
- function chandrasekhar1943ConstructorInternal(logarithmCoulomb,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function chandrasekhar1943ConstructorInternal(logarithmCoulomb,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily chandrasekhar1943} satellite dynamical friction class.
!!}
@@ -116,10 +111,9 @@ function chandrasekhar1943ConstructorInternal(logarithmCoulomb,darkMatterHaloSca
type (satelliteDynamicalFrictionChandrasekhar1943) :: self
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: logarithmCoulomb
!![
-
+
!!]
return
@@ -135,7 +129,6 @@ subroutine chandrasekhar1943Destructor(self)
!![
-
!!]
return
end subroutine chandrasekhar1943Destructor
@@ -144,9 +137,11 @@ function chandrasekhar1943Acceleration(self,node)
!!{
Return an acceleration for satellites due to dynamical friction using the formulation of \cite{chandrasekhar_dynamical_1943}.
!!}
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
use :: Error_Functions , only : Error_Function
use :: Galactic_Structure_Options , only : coordinateSystemCartesian, componentTypeDarkHalo , massTypeDark
use :: Galacticus_Nodes , only : nodeComponentSatellite , nodeComponentBasic , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : kilo
@@ -158,24 +153,31 @@ function chandrasekhar1943Acceleration(self,node)
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSatellite ), pointer :: satellite
type (treeNode ), pointer :: nodeHost
- double precision , dimension(3) :: position , velocity
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionHost_
double precision :: massSatellite
-
- nodeHost => node %mergesWith ( )
- basic => node %basic ( )
- satellite => node %satellite ( )
- massSatellite = satellite %boundMass ( )
- position = satellite %position ( )
- velocity = satellite %velocity ( )
- chandrasekhar1943Acceleration = +4.0d0 &
- & *Pi &
- & *self%galacticStructure_%chandrasekharIntegral(nodeHost,node,position,velocity) &
- & *self %coulombLogarithm ( node ) &
- & *gravitationalConstantGalacticus**2 &
- & *massSatellite &
- & *kilo &
- & *gigaYear &
+ type (coordinateCartesian ) :: position , velocity
+
+ nodeHost => node %mergesWith ()
+ basic => node %basic ()
+ satellite => node %satellite ()
+ massSatellite = satellite%boundMass ()
+ position = satellite%position ()
+ velocity = satellite%velocity ()
+ massDistribution_ => node %massDistribution()
+ massDistributionHost_ => nodeHost %massDistribution()
+ chandrasekhar1943Acceleration = +4.0d0 &
+ & *Pi &
+ & *massDistributionHost_%chandrasekharIntegral(massDistributionHost_,massDistribution_,massSatellite,position,velocity) &
+ & *self %coulombLogarithm (node ) &
+ & *gravitationalConstantGalacticus**2 &
+ & *massSatellite &
+ & *kilo &
+ & *gigaYear &
& /megaParsec
+ !![
+
+
+ !!]
return
end function chandrasekhar1943Acceleration
diff --git a/source/satellites.dynamical_friction.acceleration.Kaur2018.F90 b/source/satellites.dynamical_friction.acceleration.Kaur2018.F90
index 8e56ba1579..e5b51d2b42 100644
--- a/source/satellites.dynamical_friction.acceleration.Kaur2018.F90
+++ b/source/satellites.dynamical_friction.acceleration.Kaur2018.F90
@@ -26,7 +26,6 @@
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
use :: Numerical_Interpolation , only : interpolator
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -56,7 +55,6 @@
private
class (satelliteDynamicalFrictionClass), pointer :: satelliteDynamicalFriction_ => null()
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (interpolator ) :: factorSuppressionLeadingLogarithmic , factorSuppressionTrailingLogarithmic
double precision :: radiusDimensionlessMaximum
contains
@@ -88,24 +86,21 @@ function kaur2018ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(satelliteDynamicalFrictionClass ), pointer :: satelliteDynamicalFriction_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=satelliteDynamicalFrictionKaur2018(satelliteDynamicalFriction_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteDynamicalFrictionKaur2018(satelliteDynamicalFriction_,darkMatterProfileDMO_)
!![
-
!!]
return
end function kaur2018ConstructorParameters
- function kaur2018ConstructorInternal(satelliteDynamicalFriction_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function kaur2018ConstructorInternal(satelliteDynamicalFriction_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily kaur2018} satellite dynamical friction class.
!!}
@@ -114,9 +109,8 @@ function kaur2018ConstructorInternal(satelliteDynamicalFriction_,darkMatterProfi
type (satelliteDynamicalFrictionKaur2018) :: self
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class(satelliteDynamicalFrictionClass ), intent(in ), target :: satelliteDynamicalFriction_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
! Build interpolators for the suppression factor as a function of radius. These are extracted directly from the arXiv source
@@ -180,7 +174,6 @@ subroutine kaur2018Destructor(self)
!![
-
!!]
return
end subroutine kaur2018Destructor
@@ -189,15 +182,18 @@ function kaur2018Acceleration(self,node)
!!{
Return an acceleration for satellites due to dynamical friction using the core-stalling model of \cite{kaur_stalling_2018}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentSatellite
- use :: Galactic_Structure_Options , only : coordinateSystemCartesian
- use :: Root_Finder , only : rangeExpandMultiplicative , rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galacticus_Nodes , only : nodeComponentSatellite
+ use :: Galactic_Structure_Options, only : coordinateSystemCartesian
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
implicit none
double precision , dimension(3) :: kaur2018Acceleration
class (satelliteDynamicalFrictionKaur2018), intent(inout), target :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentSatellite ), pointer :: satellite
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , dimension(3) :: position
double precision , parameter :: toleranceAbsolute =0.0d+0, toleranceRelative =1.0d-3
double precision :: massSatellite , densityHostCentral , &
@@ -205,6 +201,7 @@ function kaur2018Acceleration(self,node)
& radiusOrbital , radiusStalling , &
& radiusDimensionless
type (rootFinder ) :: finder
+ type (coordinateSpherical ) :: coordinates
! Compute the base acceleration.
kaur2018Acceleration=+self%satelliteDynamicalFriction_%acceleration(node)
@@ -218,22 +215,31 @@ function kaur2018Acceleration(self,node)
if (massSatellite <= 0.0d0) return
! Check if the density profile has a finite density at the center. We do this by considering the logarithmic slope of the
! density profile. For cusped density profiles, we assume no stalling.
- logSlopeDensityProfileDarkMatterHost=self%darkMatterProfileDMO_%densityLogSlope(nodeHost,radius=0.0d0)
+ coordinates = [0.0d0,0.0d0,0.0d0]
+ massDistribution_ => self %darkMatterProfileDMO_%get (nodeHost )
+ logSlopeDensityProfileDarkMatterHost = massDistribution_ %densityGradientRadial(coordinates,logarithmic=.true.)
+ !![
+
+ !!]
if (logSlopeDensityProfileDarkMatterHost < 0.0d0) return
! Find the stalling radius.
- self_ => self
- densityHostCentral=self%galacticStructure_%density(nodeHost,[0.0d0,0.0d0,0.0d0],coordinateSystemCartesian)
- finder=rootFinder( &
- & rootFunction =radiusStallingRoot , &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
- & )
+ self_ => self
+ massDistribution_ => nodeHost %massDistribution( )
+ densityHostCentral = massDistribution_%density (coordinates)
+ finder = rootFinder( &
+ & rootFunction =radiusStallingRoot , &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandType =rangeExpandMultiplicative , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative, &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive &
+ & )
radiusStalling=finder%find(rootGuess=radiusOrbital)
+ !![
+
+ !!]
! Compute the suppression factor.
radiusDimensionless=+radiusOrbital &
& /radiusStalling
@@ -258,12 +264,12 @@ Root function used in finding the stalling radius.
implicit none
double precision, intent(in ) :: radiusStalling
- radiusStallingRoot=+4.0d0 &
- & *Pi &
- & /3.0d0 &
- & *densityHostCentral &
- & *radiusStalling **3 &
- & -self_%galacticStructure_%massEnclosed(nodeHost,radiusStalling) &
+ radiusStallingRoot=+4.0d0 &
+ & *Pi &
+ & /3.0d0 &
+ & *densityHostCentral &
+ & *radiusStalling **3 &
+ & -massDistribution_%massEnclosedBySphere(radiusStalling) &
& -massSatellite
return
end function radiusStallingRoot
diff --git a/source/satellites.dynamical_friction.acceleration.Petts2015.F90 b/source/satellites.dynamical_friction.acceleration.Petts2015.F90
index 1034acf43a..87acb7e1bb 100644
--- a/source/satellites.dynamical_friction.acceleration.Petts2015.F90
+++ b/source/satellites.dynamical_friction.acceleration.Petts2015.F90
@@ -64,7 +64,6 @@ function petts2015ConstructorParameters(parameters) result(self)
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (galacticStructureClass ), pointer :: galacticStructure_
logical :: logarithmCoulombApproximate
!![
@@ -77,20 +76,18 @@ function petts2015ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteDynamicalFrictionPetts2015(logarithmCoulombApproximate,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteDynamicalFrictionPetts2015(logarithmCoulombApproximate,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
!!]
return
end function petts2015ConstructorParameters
- function petts2015ConstructorInternal(logarithmCoulombApproximate,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function petts2015ConstructorInternal(logarithmCoulombApproximate,cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily petts2015} satellite dynamical friction class.
!!}
@@ -99,10 +96,9 @@ function petts2015ConstructorInternal(logarithmCoulombApproximate,cosmologyParam
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
logical , intent(in ) :: logarithmCoulombApproximate
!![
-
+
!!]
return
@@ -119,7 +115,6 @@ subroutine petts2015Destructor(self)
-
!!]
return
end subroutine petts2015Destructor
@@ -128,8 +123,10 @@ double precision function petts2015CoulombLogarithm(self,node) result(coulombLog
!!{
Evaluate the Coulomb logarithm for the \cite{petts_semi-analytic_2015} dynamical friction model.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentSatellite, treeNode
use :: Galactic_Structure_Options , only : componentTypeAll , massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Vectors , only : Vector_Magnitude
implicit none
@@ -138,12 +135,14 @@ double precision function petts2015CoulombLogarithm(self,node) result(coulombLog
class (nodeComponentSatellite ), pointer :: satellite
class (nodeComponentBasic ), pointer :: basic
type (treeNode ), pointer :: nodeHost
- double precision , dimension(3) :: position , velocity
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision , dimension(3) :: position , velocity
double precision :: speedOrbital , radiusOrbital , &
& impactParameterMinimum , impactParameterMaximum , &
& massSatellite , densitySlopeLogarithmic, &
& radiusHalfMassSatellite, fractionDarkMatter , &
& massHalfSatellite
+ type (coordinateSpherical ) :: coordinates
nodeHost => node %mergesWith( )
satellite => node %satellite ( )
@@ -164,13 +163,17 @@ double precision function petts2015CoulombLogarithm(self,node) result(coulombLog
& massSatellite, &
& basic%mass() &
& )
- radiusHalfMassSatellite = self%galacticStructure_%radiusEnclosingMass( &
- & node , &
- & mass =massHalfSatellite, &
- & componentType=componentTypeAll , &
- & massType =massTypeDark &
- & )
- densitySlopeLogarithmic = abs(self%darkMatterProfileDMO_%densityLogSlope(nodeHost,radiusOrbital))
+ massDistribution_ => node %massDistribution (componentType=componentTypeAll ,massType=massTypeDark)
+ radiusHalfMassSatellite = massDistribution_%radiusEnclosingMass(mass =massHalfSatellite )
+ !![
+
+ !!]
+ coordinates = [radiusOrbital,0.0d0,0.0d0]
+ massDistribution_ => self %darkMatterProfileDMO_%get (nodeHost )
+ densitySlopeLogarithmic = abs(massDistribution_ %densityGradientRadial(coordinates,logarithmic=.true.))
+ !![
+
+ !!]
! Evaluate the minimum and maximum impact parameters.
if (densitySlopeLogarithmic > 1.0d0) then
impactParameterMaximum=radiusOrbital/densitySlopeLogarithmic
diff --git a/source/satellites.evaporation_SIDM.Kummer2018.F90 b/source/satellites.evaporation_SIDM.Kummer2018.F90
index c0973ea2dd..cf13a5bf50 100644
--- a/source/satellites.evaporation_SIDM.Kummer2018.F90
+++ b/source/satellites.evaporation_SIDM.Kummer2018.F90
@@ -25,7 +25,6 @@
use :: Dark_Matter_Particles , only : darkMatterParticleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
use :: Numerical_Interpolation , only : interpolator
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -40,7 +39,6 @@
private
class (darkMatterParticleClass ), pointer :: darkMatterParticle_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
type (interpolator ), allocatable :: evaporationFactor
double precision :: rateScatteringNormalization , xMaximum
contains
@@ -75,24 +73,21 @@ function kummer2018ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class(darkMatterParticleClass ), pointer :: darkMatterParticle_
class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class(galacticStructureClass ), pointer :: galacticStructure_
!![
-
!!]
- self=satelliteEvaporationSIDMKummer2018(darkMatterParticle_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteEvaporationSIDMKummer2018(darkMatterParticle_,darkMatterProfileDMO_)
!![
-
!!]
return
end function kummer2018ConstructorParameters
- function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily kummer2018} satellite evaporation due to dark matter self-interactions
class.
@@ -104,9 +99,8 @@ function kummer2018ConstructorInternal(darkMatterParticle_,darkMatterProfileDMO_
type (satelliteEvaporationSIDMKummer2018) :: self
class(darkMatterParticleClass ), intent(in ), target :: darkMatterParticle_
class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
select type (darkMatterParticle_ => self%darkMatterParticle_)
@@ -138,7 +132,6 @@ subroutine kummer2018Destructor(self)
!![
-
!!]
return
end subroutine kummer2018Destructor
@@ -147,38 +140,49 @@ double precision function kummer2018MassLossRate(self,node)
!!{
Return a evaporation for satellites due to dark matter self-interactions using the formulation of \cite{kummer_effective_2018}.
!!}
- use :: Galactic_Structure_Options , only : coordinateSystemCartesian , radiusLarge
- use :: Galacticus_Nodes , only : nodeComponentSatellite,nodeComponentBasic
+ use :: Coordinates , only : coordinateSpherical , coordinateCartesian , assignment(=)
+ use :: Galactic_Structure_Options , only : coordinateSystemCartesian , radiusLarge
+ use :: Galacticus_Nodes , only : nodeComponentSatellite , nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass , kinematicsDistributionClass
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Vectors , only : Vector_Magnitude
implicit none
class (satelliteEvaporationSIDMKummer2018), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- class (nodeComponentSatellite ), pointer :: satellite
- class (nodeComponentBasic ), pointer :: basic
- type (treeNode ), pointer :: nodeHost
- double precision , dimension(3) :: position , velocity
- double precision :: radiusOrbital , speedOrbital , &
- & densityHost , rateScattering , &
- & potentialHalfMass , potentialBoundary , &
- & massBoundary , radiusBoundary , &
- & velocityEscape , speedHalfMass , &
- & velocityDispersionHost, velocityDispersionSatellite, &
- & x , radiusHalfMass , &
- & velocityDispersion , potentialEscape
+ type (treeNode ), intent(inout) :: node
+ class (nodeComponentSatellite ), pointer :: satellite
+ class (nodeComponentBasic ), pointer :: basic
+ type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionHost_
+ class (kinematicsDistributionClass ), pointer :: kinematics_ , kinematicsHost_
+ double precision , dimension(3) :: position , velocity
+ double precision :: radiusOrbital , speedOrbital , &
+ & densityHost , rateScattering , &
+ & massBoundary , radiusBoundary , &
+ & velocityEscape , speedHalfMass , &
+ & velocityDispersionHost, velocityDispersionSatellite, &
+ & x , radiusHalfMass , &
+ & velocityDispersion , potentialEscape
+ type (coordinateSpherical ) :: coordinates , coordinatesHost , &
+ & coordinatesBoundary , coordinatesHalfMass
+ type (coordinateCartesian ) :: coordinatesCartesian
! Set zero mass loss rate by default.
kummer2018MassLossRate=0.0d0
! If the scattering cross section is zero, we can return immediately.
if (self%rateScatteringNormalization == 0.0d0) return
! Evaluate satellite and host properties.
- nodeHost => node %mergesWith( )
- satellite => node %satellite ( )
- position = satellite %position ( )
- velocity = satellite %velocity ( )
- radiusOrbital = Vector_Magnitude ( position )
- speedOrbital = Vector_Magnitude ( velocity )
- densityHost = self %galacticStructure_%density (nodeHost,position,coordinateSystemCartesian)
+ nodeHost => node %mergesWith ( )
+ satellite => node %satellite ( )
+ massDistributionHost_ => nodeHost %massDistribution( )
+ position = satellite %position ( )
+ velocity = satellite %velocity ( )
+ radiusOrbital = Vector_Magnitude ( position)
+ speedOrbital = Vector_Magnitude ( velocity)
+ coordinatesCartesian = position
+ densityHost = massDistributionHost_%density (coordinatesCartesian)
+ !![
+
+ !!]
! Find the escape velocity from the half-mass radius of the subhalo. This is equal to the potential difference between the
! half-mass radius and outer boundary of the subhalo, plus the potential difference from the outer boundary to infinity (for
! which we can treat the subhalo as a point mass).
@@ -193,16 +197,16 @@ double precision function kummer2018MassLossRate(self,node)
& basic % mass() &
& )
if (massBoundary > 0.0d0) then
- radiusBoundary =self%galacticStructure_%radiusEnclosingMass(node,mass = massBoundary )
- radiusHalfMass =self%galacticStructure_%radiusEnclosingMass(node,mass =0.5d0*massBoundary )
+ massDistribution_ => node %massDistribution ( )
+ radiusBoundary = massDistribution_%radiusEnclosingMass(mass= massBoundary)
+ radiusHalfMass = massDistribution_%radiusEnclosingMass(mass=0.5d0*massBoundary)
if (radiusBoundary < 0.5d0*radiusLarge) then
- potentialBoundary=self%galacticStructure_%potential (node,radius= radiusBoundary)
- potentialHalfMass=self%galacticStructure_%potential (node,radius= radiusHalfMass)
- potentialEscape =+potentialBoundary &
- & -potentialHalfMass &
- & +gravitationalConstantGalacticus &
- & *massBoundary &
- & /radiusBoundary
+ coordinatesBoundary=[radiusBoundary,0.0d0,0.0d0]
+ coordinatesHalfMass=[radiusHalfMass,0.0d0,0.0d0]
+ potentialEscape =+massDistribution_%potentialDifference(coordinatesBoundary,coordinatesHalfMass) &
+ & +gravitationalConstantGalacticus &
+ & *massBoundary &
+ & /radiusBoundary
if (potentialEscape > 0.0d0) then
velocityEscape=sqrt(2.0d0*potentialEscape)
else
@@ -211,13 +215,28 @@ double precision function kummer2018MassLossRate(self,node)
else
velocityEscape=0.0d0
end if
+ !![
+
+ !!]
! Find the combined velocity dispersion of satellite and host.
- velocityDispersionHost =+self%darkMatterProfileDMO_%radialVelocityDispersion(nodeHost,radiusOrbital )
- velocityDispersionSatellite=+self%darkMatterProfileDMO_%radialVelocityDispersion(node ,radiusHalfMass)
- velocityDispersion =+sqrt( &
- & +velocityDispersionHost **2 &
- & +velocityDispersionSatellite**2 &
- & )
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ massDistributionHost_ => self %darkMatterProfileDMO_%get (nodeHost )
+ kinematics_ => massDistribution_ %kinematicsDistribution( )
+ kinematicsHost_ => massDistributionHost_ %kinematicsDistribution( )
+ coordinates = [radiusHalfMass,0.0d0,0.0d0]
+ coordinatesHost = [radiusOrbital ,0.0d0,0.0d0]
+ velocityDispersionHost = +kinematicsHost_ %velocityDispersion1D (coordinatesHost,massDistributionHost_)
+ velocityDispersionSatellite = +kinematics_ %velocityDispersion1D (coordinates ,massDistribution_ )
+ velocityDispersion = +sqrt( &
+ & +velocityDispersionHost **2 &
+ & +velocityDispersionSatellite**2 &
+ & )
+ !![
+
+
+
+
+ !!]
! Get the speed of a host particle at the half-mass radius of the subhalo - this is the sum of the kinetic energy or host
! particles in the rest-frame of the subhalo, plus the energy they gain by falling in to the half-mass radius of the
! subhalo. We include the correction factor of the velocity dispersion as suggested in equation (A4) of Kummer et
diff --git a/source/satellites.merging.mass_movements.Baugh2005.F90 b/source/satellites.merging.mass_movements.Baugh2005.F90
index 21c2c831b9..0989324729 100644
--- a/source/satellites.merging.mass_movements.Baugh2005.F90
+++ b/source/satellites.merging.mass_movements.Baugh2005.F90
@@ -21,8 +21,7 @@
Implements a merger mass movements class using the \cite{baugh_can_2005} model.
!!}
- use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Kind_Numbers, only : kind_int8
!![
@@ -51,14 +50,13 @@
A merger mass movements class which uses the \cite{baugh_can_2005} calculation.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- double precision :: massRatioMajorMerger , ratioMassBurst , &
- & fractionGasCriticalBurst
- type (enumerationDestinationMergerType) :: destinationGasMinorMerger
- integer (kind=kind_int8 ) :: lastUniqueID
- type (enumerationDestinationMergerType) :: destinationGasSatellite , destinationStarsSatellite, &
- & destinationGasHost , destinationStarsHost
- logical :: mergerIsMajor , movementsCalculated
+ double precision :: massRatioMajorMerger , ratioMassBurst , &
+ & fractionGasCriticalBurst
+ type (enumerationDestinationMergerType) :: destinationGasMinorMerger
+ integer (kind=kind_int8 ) :: lastUniqueID
+ type (enumerationDestinationMergerType) :: destinationGasSatellite , destinationStarsSatellite, &
+ & destinationGasHost , destinationStarsHost
+ logical :: mergerIsMajor , movementsCalculated
contains
final :: baugh2005Destructor
procedure :: autoHook => baugh2005AutoHook
@@ -83,7 +81,6 @@ function baugh2005ConstructorParameters(parameters) result(self)
implicit none
type (mergerMassMovementsBaugh2005) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: massRatioMajorMerger , ratioMassBurst, &
& fractionGasCriticalBurst
type (varying_string ) :: destinationGasMinorMerger
@@ -113,28 +110,25 @@ function baugh2005ConstructorParameters(parameters) result(self)
The component to which satellite galaxy gas moves to as a result of a minor merger.
-
!!]
- self=mergerMassMovementsBaugh2005(massRatioMajorMerger,enumerationDestinationMergerEncode(char(destinationGasMinorMerger),includesPrefix=.false.),ratioMassBurst,fractionGasCriticalBurst,galacticStructure_)
+ self=mergerMassMovementsBaugh2005(massRatioMajorMerger,enumerationDestinationMergerEncode(char(destinationGasMinorMerger),includesPrefix=.false.),ratioMassBurst,fractionGasCriticalBurst)
!![
-
!!]
return
end function baugh2005ConstructorParameters
- function baugh2005ConstructorInternal(massRatioMajorMerger,destinationGasMinorMerger,ratioMassBurst,fractionGasCriticalBurst,galacticStructure_) result(self)
+ function baugh2005ConstructorInternal(massRatioMajorMerger,destinationGasMinorMerger,ratioMassBurst,fractionGasCriticalBurst) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily baugh2005} merger mass movements.
!!}
implicit none
- type (mergerMassMovementsBaugh2005 ) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
- double precision , intent(in ) :: massRatioMajorMerger , ratioMassBurst, &
- & fractionGasCriticalBurst
- type (enumerationDestinationMergerType), intent(in ) :: destinationGasMinorMerger
+ type (mergerMassMovementsBaugh2005 ) :: self
+ double precision , intent(in ) :: massRatioMajorMerger , ratioMassBurst, &
+ & fractionGasCriticalBurst
+ type (enumerationDestinationMergerType), intent(in ) :: destinationGasMinorMerger
!![
-
+
!!]
self%lastUniqueID =-huge(0_kind_int8)
@@ -170,9 +164,6 @@ subroutine baugh2005Destructor(self)
if (calculationResetEvent%isAttached(self,baugh2005CalculationReset)) call calculationResetEvent%detach(self,baugh2005CalculationReset)
if (satelliteMergerEvent %isAttached(self,baugh2005GetHook )) call satelliteMergerEvent %detach(self,baugh2005GetHook )
- !![
-
- !!]
return
end subroutine baugh2005Destructor
@@ -224,16 +215,20 @@ subroutine baugh2005Get(self,node,destinationGasSatellite,destinationStarsSatell
Determine how different mass components should be redistributed as the result of a merger according to the model of
\cite{baugh_can_2005}.
!!}
- use :: Galactic_Structure_Options, only : componentTypeSpheroid, massTypeGalactic, massTypeGaseous
+ use :: Galactic_Structure_Options, only : componentTypeSpheroid, componentTypeDisk, massTypeGalactic, massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (mergerMassMovementsBaugh2005 ), intent(inout) :: self
type (treeNode ), intent(inout), target :: node
- type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite, destinationGasHost , &
- & destinationStarsHost , destinationStarsSatellite
+ type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite , destinationGasHost , &
+ & destinationStarsHost , destinationStarsSatellite
logical , intent( out) :: mergerIsMajor
type (treeNode ), pointer :: nodeHost
- double precision :: massHost , massSatellite , &
- & massSpheroidHost , massGasHost
+ class (massDistributionClass ), pointer :: massDistributionSatellite , massDistributionHost , &
+ & massDistributionHostDiskGas , massDistributionHostSpheroidGas, &
+ & massDistributionHostSpheroid
+ double precision :: massHost , massSatellite , &
+ & massSpheroidHost , massGasHost
logical :: triggersBurst
! The calculation of how mass moves as a result of the merger is computed when first needed and then stored. This ensures that
@@ -242,20 +237,33 @@ subroutine baugh2005Get(self,node,destinationGasSatellite,destinationStarsSatell
if (node%uniqueID() /= self%lastUniqueID) call baugh2005CalculationReset(self,node,node%uniqueID())
if (.not.self%movementsCalculated) then
self%movementsCalculated = .true.
- nodeHost => node%mergesWith()
- massSatellite = self%galacticStructure_%massEnclosed(node ,massType=massTypeGalactic)
- massHost = self%galacticStructure_%massEnclosed(nodeHost ,massType=massTypeGalactic)
- massGasHost = self%galacticStructure_%massEnclosed(nodeHost ,massType=massTypeGaseous )
- massSpheroidHost = self%galacticStructure_%massEnclosed(nodeHost,componentType=componentTypeSpheroid,massType=massTypeGalactic)
- self%mergerIsMajor = massSatellite >= self%massRatioMajorMerger*massHost
- triggersBurst = self%mergerIsMajor &
- & .or. &
- & ( &
- & massSpheroidHost < self%ratioMassBurst *massHost &
- & .and. &
- & massGasHost >= self%fractionGasCriticalBurst*massHost &
- & )
- if (self%mergerIsMajor) then
+ nodeHost => node %mergesWith ( )
+ massDistributionSatellite => node %massDistribution( massType=massTypeGalactic)
+ massDistributionHost => nodeHost %massDistribution( massType=massTypeGalactic)
+ massDistributionHostSpheroid => nodeHost %massDistribution(componentType=componentTypeSpheroid,massType=massTypeGalactic)
+ massDistributionHostDiskGas => nodeHost %massDistribution(componentType=componentTypeDisk ,massType=massTypeGaseous )
+ massDistributionHostSpheroidGas => nodeHost %massDistribution(componentType=componentTypeSpheroid,massType=massTypeGaseous )
+ massSatellite = +massDistributionSatellite %massTotal ( )
+ massHost = +massDistributionHost %massTotal ( )
+ massGasHost = +massDistributionHostDiskGas %massTotal ( ) &
+ & +massDistributionHostSpheroidGas%massTotal ( )
+ massSpheroidHost = +massDistributionHostSpheroid %massTotal ( )
+ self%mergerIsMajor = massSatellite >= self%massRatioMajorMerger *massHost
+ triggersBurst = self%mergerIsMajor &
+ & .or. &
+ & ( &
+ & massSpheroidHost < self%ratioMassBurst *massHost &
+ & .and. &
+ & massGasHost >= self%fractionGasCriticalBurst*massHost &
+ & )
+ !![
+
+
+
+
+
+ !!]
+ if (self%mergerIsMajor) then
self%destinationGasSatellite = destinationMergerSpheroid
self%destinationStarsSatellite = destinationMergerSpheroid
self%destinationGasHost = destinationMergerSpheroid
diff --git a/source/satellites.merging.mass_movements.simple.F90 b/source/satellites.merging.mass_movements.simple.F90
index f021b3be1e..8684fd3feb 100644
--- a/source/satellites.merging.mass_movements.simple.F90
+++ b/source/satellites.merging.mass_movements.simple.F90
@@ -21,8 +21,7 @@
Implements a merger mass movements class which uses a simple calculation.
!!}
- use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Kind_Numbers, only : kind_int8
!![
@@ -45,13 +44,12 @@
A merger mass movements class which uses a simple calculation.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- double precision :: massRatioMajorMerger
- type (enumerationDestinationMergerType) :: destinationGasMinorMerger , destinationStarsMinorMerger
- integer (kind=kind_int8 ) :: lastUniqueID
- type (enumerationDestinationMergerType) :: destinationGasSatellite , destinationStarsSatellite , &
- & destinationGasHost , destinationStarsHost
- logical :: mergerIsMajor , movementsCalculated
+ double precision :: massRatioMajorMerger
+ type (enumerationDestinationMergerType) :: destinationGasMinorMerger, destinationStarsMinorMerger
+ integer (kind=kind_int8 ) :: lastUniqueID
+ type (enumerationDestinationMergerType) :: destinationGasSatellite , destinationStarsSatellite , &
+ & destinationGasHost , destinationStarsHost
+ logical :: mergerIsMajor , movementsCalculated
contains
final :: simpleDestructor
procedure :: autoHook => simpleAutoHook
@@ -76,7 +74,6 @@ function simpleConstructorParameters(parameters) result(self)
implicit none
type (mergerMassMovementsSimple) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: massRatioMajorMerger
type (varying_string ) :: destinationGasMinorMerger, destinationStarsMinorMerger
@@ -99,27 +96,24 @@ function simpleConstructorParameters(parameters) result(self)
The component to which satellite galaxy stars move to as a result of a minor merger.
-
!!]
- self=mergerMassMovementsSimple(massRatioMajorMerger,enumerationDestinationMergerEncode(char(destinationGasMinorMerger),includesPrefix=.false.),enumerationDestinationMergerEncode(char(destinationStarsMinorMerger),includesPrefix=.false.),galacticStructure_)
+ self=mergerMassMovementsSimple(massRatioMajorMerger,enumerationDestinationMergerEncode(char(destinationGasMinorMerger),includesPrefix=.false.),enumerationDestinationMergerEncode(char(destinationStarsMinorMerger),includesPrefix=.false.))
!![
-
!!]
return
end function simpleConstructorParameters
- function simpleConstructorInternal(massRatioMajorMerger,destinationGasMinorMerger,destinationStarsMinorMerger,galacticStructure_) result(self)
+ function simpleConstructorInternal(massRatioMajorMerger,destinationGasMinorMerger,destinationStarsMinorMerger) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily simple} merger mass movements class.
!!}
implicit none
type (mergerMassMovementsSimple ) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: massRatioMajorMerger
type (enumerationDestinationMergerType), intent(in ) :: destinationGasMinorMerger, destinationStarsMinorMerger
!![
-
+
!!]
self%lastUniqueID =-huge(0_kind_int8)
@@ -155,9 +149,6 @@ subroutine simpleDestructor(self)
if (calculationResetEvent%isAttached(self,simpleCalculationReset)) call calculationResetEvent%detach(self,simpleCalculationReset)
if (satelliteMergerEvent %isAttached(self,simpleGetHook )) call satelliteMergerEvent %detach(self,simpleGetHook )
- !![
-
- !!]
return
end subroutine simpleDestructor
@@ -208,16 +199,19 @@ subroutine simpleGet(self,node,destinationGasSatellite,destinationStarsSatellite
!!{
Determine where stars and gas move as the result of a merger event using a simple algorithm.
!!}
- use :: Galactic_Structure_Options, only : componentTypeDisk, componentTypeSpheroid, massTypeGalactic
+ use :: Galactic_Structure_Options, only : componentTypeDisk , componentTypeSpheroid, massTypeGalactic
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (mergerMassMovementsSimple ), intent(inout) :: self
type (treeNode ), intent(inout), target :: node
- type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite, destinationGasHost , &
- & destinationStarsHost , destinationStarsSatellite
+ type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite , destinationGasHost , &
+ & destinationStarsHost , destinationStarsSatellite
logical , intent( out) :: mergerIsMajor
- type (treeNode ), pointer :: nodeHost , nodeMajor
- double precision :: massHost , massSatellite , &
- & massSpheroid , massDisk
+ type (treeNode ), pointer :: nodeHost , nodeMajor
+ class (massDistributionClass ), pointer :: massDistributionSatellite, massDistributionHost , &
+ & massDistributionDisk , massDistributionSpheroid
+ double precision :: massHost , massSatellite , &
+ & massSpheroid , massDisk
type (enumerationDestinationMergerType) :: destinationDominant
! The calculation of how mass moves as a result of the merger is computed when first needed and then stored. This ensures that
@@ -225,11 +219,17 @@ subroutine simpleGet(self,node,destinationGasSatellite,destinationStarsSatellite
! components are modified in response to the merger.
if (node%uniqueID() /= self%lastUniqueID) call simpleCalculationReset(self,node,node%uniqueID())
if (.not.self%movementsCalculated) then
- self%movementsCalculated = .true.
- nodeHost => node%mergesWith()
- massSatellite = self%galacticStructure_%massEnclosed(node ,massType=massTypeGalactic)
- massHost = self%galacticStructure_%massEnclosed(nodeHost,massType=massTypeGalactic)
- self%mergerIsMajor = massSatellite > 0.0d0 .and. massHost > 0.0d0 .and. min(massSatellite,massHost) >= self%massRatioMajorMerger*max(massSatellite,massHost)
+ self%movementsCalculated = .true.
+ nodeHost => node %mergesWith ( )
+ massDistributionHost => nodeHost %massDistribution(massType=massTypeGalactic)
+ massDistributionSatellite => node %massDistribution(massType=massTypeGalactic)
+ massSatellite = massDistributionSatellite%massTotal ( )
+ massHost = massDistributionHost %massTotal ( )
+ self%mergerIsMajor = massSatellite > 0.0d0 .and. massHost > 0.0d0 .and. min(massSatellite,massHost) >= self%massRatioMajorMerger*max(massSatellite,massHost)
+ !![
+
+
+ !!]
if (self%mergerIsMajor) then
self%destinationGasSatellite = destinationMergerSpheroid
self%destinationStarsSatellite= destinationMergerSpheroid
@@ -243,8 +243,14 @@ subroutine simpleGet(self,node,destinationGasSatellite,destinationStarsSatellite
else
nodeMajor => node
end if
- massDisk =self%galacticStructure_%massEnclosed(nodeMajor,massType=massTypeGalactic,componentType=componentTypeDisk )
- massSpheroid=self%galacticStructure_%massEnclosed(nodeMajor,massType=massTypeGalactic,componentType=componentTypeSpheroid)
+ massDistributionDisk => nodeMajor %massDistribution(massType=massTypeGalactic,componentType=componentTypeDisk )
+ massDistributionSpheroid => nodeMajor %massDistribution(massType=massTypeGalactic,componentType=componentTypeSpheroid)
+ massDisk = massDistributionDisk %massTotal ( )
+ massSpheroid = massDistributionSpheroid%massTotal ( )
+ !![
+
+
+ !!]
if (massDisk > massSpheroid) then
destinationDominant=destinationMergerDisk
else
@@ -252,12 +258,12 @@ subroutine simpleGet(self,node,destinationGasSatellite,destinationStarsSatellite
end if
end if
if (self%destinationGasMinorMerger == destinationMergerDominant) then
- self%destinationGasSatellite =destinationDominant
+ self%destinationGasSatellite = destinationDominant
else
self%destinationGasSatellite =self%destinationGasMinorMerger
end if
if (self%destinationStarsMinorMerger == destinationMergerDominant) then
- self%destinationStarsSatellite=destinationDominant
+ self%destinationStarsSatellite= destinationDominant
else
self%destinationStarsSatellite=self%destinationStarsMinorMerger
end if
diff --git a/source/satellites.merging.mass_movements.very_simple.F90 b/source/satellites.merging.mass_movements.very_simple.F90
index a1ba06b9fb..0a6c470b3b 100644
--- a/source/satellites.merging.mass_movements.very_simple.F90
+++ b/source/satellites.merging.mass_movements.very_simple.F90
@@ -21,8 +21,7 @@
Implements a merger mass movements class which uses a simple calculation.
!!}
- use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Kind_Numbers, only : kind_int8
!![
@@ -37,10 +36,9 @@
A merger mass movements class which uses a simple calculation.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
- double precision :: massRatioMajorMerger
- integer (kind=kind_int8 ) :: lastUniqueID
- logical :: mergerIsMajor , movementsCalculated
+ double precision :: massRatioMajorMerger
+ integer (kind=kind_int8) :: lastUniqueID
+ logical :: mergerIsMajor , movementsCalculated
contains
final :: verySimpleDestructor
procedure :: autoHook => verySimpleAutoHook
@@ -65,7 +63,6 @@ function verySimpleConstructorParameters(parameters) result(self)
implicit none
type (mergerMassMovementsVerySimple) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: massRatioMajorMerger
!![
@@ -75,26 +72,23 @@ function verySimpleConstructorParameters(parameters) result(self)
The mass ratio above which mergers are considered to be ``major''.
-
!!]
- self=mergerMassMovementsVerySimple(massRatioMajorMerger,galacticStructure_)
+ self=mergerMassMovementsVerySimple(massRatioMajorMerger)
!![
-
!!]
return
end function verySimpleConstructorParameters
- function verySimpleConstructorInternal(massRatioMajorMerger,galacticStructure_) result(self)
+ function verySimpleConstructorInternal(massRatioMajorMerger) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily verySimple} merger mass movements.
!!}
implicit none
- type (mergerMassMovementsVerySimple) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
- double precision , intent(in ) :: massRatioMajorMerger
+ type (mergerMassMovementsVerySimple) :: self
+ double precision , intent(in ) :: massRatioMajorMerger
!![
-
+
!!]
self%lastUniqueID =-huge(0_kind_int8)
@@ -126,9 +120,6 @@ subroutine verySimpleDestructor(self)
if (calculationResetEvent%isAttached(self,verySimpleCalculationReset)) call calculationResetEvent%detach(self,verySimpleCalculationReset)
if (satelliteMergerEvent %isAttached(self,verySimpleGetHook )) call satelliteMergerEvent %detach(self,verySimpleGetHook )
- !![
-
- !!]
return
end subroutine verySimpleDestructor
@@ -180,14 +171,16 @@ subroutine verySimpleGet(self,node,destinationGasSatellite,destinationStarsSatel
Determine where stars and gas move as the result of a merger event using a very simple algorithm.
!!}
use :: Galactic_Structure_Options, only : massTypeGalactic
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (mergerMassMovementsVerySimple ), intent(inout) :: self
type (treeNode ), intent(inout), target :: node
- type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite, destinationGasHost , &
- & destinationStarsHost , destinationStarsSatellite
+ type (enumerationDestinationMergerType), intent( out) :: destinationGasSatellite , destinationGasHost , &
+ & destinationStarsHost , destinationStarsSatellite
logical , intent( out) :: mergerIsMajor
type (treeNode ), pointer :: nodeHost
- double precision :: massHost , massSatellite
+ class (massDistributionClass ), pointer :: massDistributionSatellite, massDistributionHost
+ double precision :: massHost , massSatellite
! The calculation of how mass moves as a result of the merger is computed when first needed and then stored. This ensures that
! the results are determined by the properties of the merge target prior to any modification that will occur as node
@@ -200,10 +193,16 @@ subroutine verySimpleGet(self,node,destinationGasSatellite,destinationStarsSatel
else if (self%massRatioMajorMerger > 1.0d0) then
self%mergerIsMajor=.false.
else
- nodeHost => node%mergesWith()
- massSatellite = self%galacticStructure_%massEnclosed(node ,massType=massTypeGalactic)
- massHost = self%galacticStructure_%massEnclosed(nodeHost,massType=massTypeGalactic)
- self%mergerIsMajor = massSatellite >= self%massRatioMajorMerger*massHost
+ nodeHost => node %mergesWith ( )
+ massDistributionHost => nodeHost %massDistribution(massType=massTypeGalactic)
+ massDistributionSatellite => node %massDistribution(massType=massTypeGalactic)
+ massSatellite = massDistributionSatellite%massTotal ( )
+ massHost = massDistributionHost %massTotal ( )
+ self%mergerIsMajor = massSatellite >= self%massRatioMajorMerger*massHost
+ !![
+
+
+ !!]
end if
end if
mergerIsMajor =self%mergerIsMajor
diff --git a/source/satellites.merging.progenitor_properties.Cole2000.F90 b/source/satellites.merging.progenitor_properties.Cole2000.F90
index 686ade808c..b1f5d56975 100644
--- a/source/satellites.merging.progenitor_properties.Cole2000.F90
+++ b/source/satellites.merging.progenitor_properties.Cole2000.F90
@@ -23,7 +23,7 @@
use :: Root_Finder , only : rootFinder
use :: Satellite_Merging_Mass_Movements, only : mergerMassMovementsClass, enumerationDestinationMergerType
- use :: Galactic_Structure , only : galacticStructureClass
+ use :: Mass_Distributions , only : massDistributionClass
!![
@@ -67,7 +67,6 @@
A merger progenitor properties class which uses the algorithm of \cite{cole_hierarchical_2000}.
!!}
private
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
class(mergerMassMovementsClass), pointer :: mergerMassMovements_ => null()
type (rootFinder ) :: finder
contains
@@ -86,9 +85,11 @@
! Module global variables used in root finding.
class (mergerProgenitorPropertiesCole2000), pointer :: self_
type (treeNode ), pointer :: node_
- type (enumerationDestinationMergerType ) :: destinationGas_, destinationStars_
+ class (massDistributionClass ), pointer :: massDistributionSpheroidStellar_, massDistributionDiskStellar_, &
+ & massDistributionSpheroidGaseous_, massDistributionDiskGaseous_
+ type (enumerationDestinationMergerType ) :: destinationGas_ , destinationStars_
double precision :: massHalf_
- !$omp threadprivate(self_,node_,destinationGas_,destinationStars_,massHalf_)
+ !$omp threadprivate(self_,node_,destinationGas_,destinationStars_,massHalf_,massDistributionSpheroidStellar_,massDistributionDiskStellar_,massDistributionSpheroidGaseous_,massDistributionDiskGaseous_)
contains
@@ -104,7 +105,6 @@ function cole2000ConstructorParameters(parameters) result(self)
type (mergerProgenitorPropertiesCole2000) :: self
type (inputParameters ), intent(inout) :: parameters
class(mergerMassMovementsClass ), pointer :: mergerMassMovements_
- class(galacticStructureClass ), pointer :: galacticStructure_
! Ensure that required methods are supported.
if ( &
@@ -149,18 +149,16 @@ function cole2000ConstructorParameters(parameters) result(self)
& )
!![
-
!!]
- self=mergerProgenitorPropertiesCole2000(mergerMassMovements_,galacticStructure_)
+ self=mergerProgenitorPropertiesCole2000(mergerMassMovements_)
!![
-
!!]
return
end function cole2000ConstructorParameters
- function cole2000ConstructorInternal(mergerMassMovements_,galacticStructure_) result(self)
+ function cole2000ConstructorInternal(mergerMassMovements_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily cole2000} merger progenitor properties class.
!!}
@@ -168,9 +166,8 @@ function cole2000ConstructorInternal(mergerMassMovements_,galacticStructure_) re
implicit none
type (mergerProgenitorPropertiesCole2000) :: self
class(mergerMassMovementsClass ), intent(in ), target :: mergerMassMovements_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
self%finder=rootFinder( &
@@ -195,7 +192,6 @@ subroutine cole2000Destructor(self)
!![
-
!!]
return
end subroutine cole2000Destructor
@@ -204,7 +200,8 @@ subroutine cole2000Get(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
!!{
Computes various properties of the progenitor galaxies useful for calculations of merger remnant sizes.
!!}
- use :: Galactic_Structure_Options , only : massTypeGalactic , radiusLarge
+ use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid , massTypeGaseous , massTypeStellar, &
+ & massTypeGalactic , radiusLarge
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
@@ -219,6 +216,7 @@ subroutine cole2000Get(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
& radiusSatellite , massSpheroidSatellite
class (nodeComponentDisk ), pointer :: diskHost , diskSatelite
class (nodeComponentSpheroid ), pointer :: spheroidHost , spheroidSatellite
+ class (massDistributionClass ), pointer :: massDistributionHost , massDistributionSatellite
double precision :: massComponent , factorDarkMatterDiskHost , &
& radiusHalfMassDiskHost , factorDarkMatterSpheroidHost , &
& radiusHalfMassSpheroidHost , factorDarkMatterDiskSatellite , &
@@ -236,8 +234,10 @@ subroutine cole2000Get(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
diskSatelite => nodeSatellite%disk ()
spheroidSatellite => nodeSatellite%spheroid()
! Find the baryonic masses of the two galaxies.
- massSatellite=self%galacticStructure_%massEnclosed(nodeSatellite,massType=massTypeGalactic)
- massHost =self%galacticStructure_%massEnclosed( nodeHost,massType=massTypeGalactic)
+ massDistributionSatellite => nodeSatellite %massDistribution(massType=massTypeGalactic)
+ massDistributionHost => nodeHost %massDistribution(massType=massTypeGalactic)
+ massSatellite = massDistributionSatellite%massTotal ( )
+ massHost = massDistributionHost %massTotal ( )
! Compute dark matter factors. These are the specific angular momenta of components divided by sqrt(G M r) where M is the
! component mass and r its half-mass radius. We use a weighted average of these factors to infer the specific angular momentum
! of the remnant from its mass and radius.
@@ -351,44 +351,54 @@ subroutine cole2000Get(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
! Compute the half-mass radii of the material that will end up in the remnant spheroid.
! Host node.
if (massSpheroidHost > 0.0d0) then
- self_ => self
- node_ => nodeHost
- destinationGas_ = destinationGasHost
- destinationStars_= destinationStarsHost
- massHalf_ = 0.0d0 ! Set to zero here so that cole2000HalfMassRadiusRoot() returns the actual half mass.
- massHalf_ = 0.5d0*cole2000HalfMassRadiusRoot(radiusLarge)
+ self_ => self
+ node_ => nodeHost
+ massDistributionSpheroidStellar_ => nodeHost%massDistribution(componentType=componentTypeSpheroid,massType=massTypeStellar)
+ massDistributionDiskStellar_ => nodeHost%massDistribution(componentType=componentTypeDisk ,massType=massTypeStellar)
+ massDistributionSpheroidGaseous_ => nodeHost%massDistribution(componentType=componentTypeSpheroid,massType=massTypeGaseous)
+ massDistributionDiskGaseous_ => nodeHost%massDistribution(componentType=componentTypeDisk ,massType=massTypeGaseous)
+ destinationGas_ = destinationGasHost
+ destinationStars_ = destinationStarsHost
+ massHalf_ = 0.0d0 ! Set to zero here so that cole2000HalfMassRadiusRoot() returns the actual half mass.
+ massHalf_ = 0.5d0*cole2000HalfMassRadiusRoot(radiusLarge)
if (cole2000HalfMassRadiusRoot(0.0d0) <= 0.0d0) then
- radiusHost=self%finder%find(rootGuess=self%galacticStructure_%radiusEnclosingMass( &
- & node_ , &
- & massFractional=0.50d0 , &
- & massType =massTypeGalactic &
- & ) &
- & )
+ radiusHost=self%finder%find(rootGuess=massDistributionHost%radiusEnclosingMass(massFractional=0.50d0))
else
radiusHost =0.0d0
massSpheroidHost=0.0d0
end if
+ !![
+
+
+
+
+ !!]
else
radiusHost=0.0d0
end if
if (massSpheroidSatellite > 0.0d0) then
- self_ => self
- node_ => nodeSatellite
- destinationGas_ = destinationGasSatellite
- destinationStars_ = destinationStarsSatellite
- massHalf_ = 0.0d0 ! Set to zero here so that cole2000HalfMassRadiusRoot() returns the actual half mass.
- massHalf_ = 0.50d0*cole2000HalfMassRadiusRoot(radiusLarge)
+ self_ => self
+ node_ => nodeSatellite
+ massDistributionSpheroidStellar_ => nodeSatellite%massDistribution(componentType=componentTypeSpheroid,massType=massTypeStellar)
+ massDistributionDiskStellar_ => nodeSatellite%massDistribution(componentType=componentTypeDisk ,massType=massTypeStellar)
+ massDistributionSpheroidGaseous_ => nodeSatellite%massDistribution(componentType=componentTypeSpheroid,massType=massTypeGaseous)
+ massDistributionDiskGaseous_ => nodeSatellite%massDistribution(componentType=componentTypeDisk ,massType=massTypeGaseous)
+ destinationGas_ = destinationGasSatellite
+ destinationStars_ = destinationStarsSatellite
+ massHalf_ = 0.0d0 ! Set to zero here so that cole2000HalfMassRadiusRoot() returns the actual half mass.
+ massHalf_ = 0.50d0*cole2000HalfMassRadiusRoot(radiusLarge)
if (cole2000HalfMassRadiusRoot(0.0d0) <= 0.0d0) then
- radiusSatellite=self%finder%find(rootGuess=self%galacticStructure_%radiusEnclosingMass( &
- & node_ , &
- & massFractional=0.50d0 , &
- & massType =massTypeGalactic &
- & ) &
- & )
+ radiusSatellite=self%finder%find(rootGuess=massDistributionSatellite%radiusEnclosingMass(massFractional=0.50d0))
else
radiusSatellite =0.0d0
massSpheroidSatellite=0.0d0
end if
+ !![
+
+
+
+
+ !!]
else
radiusSatellite=0.0d0
end if
@@ -400,6 +410,11 @@ subroutine cole2000Get(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
end if
! Compute the mass of the host spheroid before the merger.
massSpheroidHostPreMerger=spheroidHost%massStellar()+spheroidHost%massGas()
+ ! Clean up.
+ !![
+
+
+ !!]
return
end subroutine cole2000Get
@@ -407,7 +422,6 @@ double precision function cole2000HalfMassRadiusRoot(radius)
!!{
Function used in root finding for progenitor galaxy half-mass radii.
!!}
- use :: Galactic_Structure_Options , only : componentTypeDisk , componentTypeSpheroid , massTypeGaseous, massTypeStellar
use :: Satellite_Merging_Mass_Movements, only : destinationMergerSpheroid, destinationMergerUnmoved
implicit none
double precision, intent(in ) :: radius
@@ -417,22 +431,22 @@ Function used in root finding for progenitor galaxy half-mass radii.
! Account for gas mass.
select case (destinationGas_%ID)
case (destinationMergerSpheroid%ID)
- cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeSpheroid,massType=massTypeGaseous) &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeDisk ,massType=massTypeGaseous)
+ cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
+ & +massDistributionSpheroidGaseous_%massEnclosedBySphere(radius) &
+ & +massDistributionDiskGaseous_ %massEnclosedBySphere(radius)
case (destinationMergerUnmoved %ID)
- cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeSpheroid,massType=massTypeGaseous)
+ cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
+ & +massDistributionSpheroidGaseous_%massEnclosedBySphere(radius)
end select
! Account for stellar mass.
select case (destinationStars_%ID)
case (destinationMergerSpheroid%ID)
- cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeSpheroid,massType=massTypeStellar) &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeDisk ,massType=massTypeStellar)
+ cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
+ & +massDistributionSpheroidStellar_%massEnclosedBySphere(radius) &
+ & +massDistributionDiskStellar_ %massEnclosedBySphere(radius)
case (destinationMergerUnmoved %ID)
- cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
- & +self_%galacticStructure_%massEnclosed(node_,radius,componentType=componentTypeSpheroid,massType=massTypeStellar)
+ cole2000HalfMassRadiusRoot=+cole2000HalfMassRadiusRoot &
+ & +massDistributionSpheroidStellar_%massEnclosedBySphere(radius)
end select
return
end function cole2000HalfMassRadiusRoot
diff --git a/source/satellites.merging.progenitor_properties.simple.F90 b/source/satellites.merging.progenitor_properties.simple.F90
index ae015abbad..cbc03ddba2 100644
--- a/source/satellites.merging.progenitor_properties.simple.F90
+++ b/source/satellites.merging.progenitor_properties.simple.F90
@@ -22,7 +22,6 @@
!!}
use :: Satellite_Merging_Mass_Movements, only : mergerMassMovementsClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -34,7 +33,6 @@
A merger progenitor properties class which uses a simple calculation.
!!}
private
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
class(mergerMassMovementsClass), pointer :: mergerMassMovements_ => null()
contains
final :: simpleDestructor
@@ -63,7 +61,6 @@ function simpleConstructorParameters(parameters) result(self)
type (mergerProgenitorPropertiesSimple) :: self
type (inputParameters ), intent(inout) :: parameters
class(mergerMassMovementsClass ), pointer :: mergerMassMovements_
- class(galacticStructureClass ), pointer :: galacticStructure_
! Ensure that required methods are supported.
if ( &
@@ -104,27 +101,24 @@ function simpleConstructorParameters(parameters) result(self)
& )
!![
-
!!]
- self=mergerProgenitorPropertiesSimple(mergerMassMovements_,galacticStructure_)
+ self=mergerProgenitorPropertiesSimple(mergerMassMovements_)
!![
-
!!]
return
end function simpleConstructorParameters
- function simpleConstructorInternal(mergerMassMovements_,galacticStructure_) result(self)
+ function simpleConstructorInternal(mergerMassMovements_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily simple} merger progenitor properties class.
!!}
implicit none
type (mergerProgenitorPropertiesSimple) :: self
class(mergerMassMovementsClass ), intent(in ), target :: mergerMassMovements_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -139,7 +133,6 @@ subroutine simpleDestructor(self)
!![
-
!!]
return
end subroutine simpleDestructor
@@ -152,6 +145,7 @@ subroutine simpleGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSphe
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Satellite_Merging_Mass_Movements, only : destinationMergerDisk, destinationMergerSpheroid, destinationMergerUnmoved, enumerationDestinationMergerType
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (mergerProgenitorPropertiesSimple), intent(inout), target :: self
type (treeNode ), intent(inout), target :: nodeSatellite , nodeHost
@@ -160,6 +154,7 @@ subroutine simpleGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSphe
& massSpheroidHostPreMerger, massGasSpheroidRemnant , &
& massSpheroidRemnant , massSatellite , &
& radiusSatellite , massSpheroidSatellite
+ class (massDistributionClass ), pointer :: massDistributionHost , massDistributionSatellite
class (nodeComponentDisk ), pointer :: diskHost , diskSatelite
class (nodeComponentSpheroid ), pointer :: spheroidHost , spheroidSatellite
type (enumerationDestinationMergerType) :: destinationGasSatellite , destinationGasHost , &
@@ -174,8 +169,10 @@ subroutine simpleGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSphe
diskSatelite => nodeSatellite%disk ()
spheroidSatellite => nodeSatellite%spheroid()
! Find the baryonic masses of the two galaxies.
- massSatellite=self%galacticStructure_%massEnclosed(nodeSatellite,massType=massTypeGalactic)
- massHost =self%galacticStructure_%massEnclosed(nodeHost ,massType=massTypeGalactic)
+ massDistributionSatellite => nodeSatellite %massDistribution(massType=massTypeGalactic)
+ massDistributionHost => nodeHost %massDistribution(massType=massTypeGalactic)
+ massSatellite = massDistributionSatellite%massTotal ( )
+ massHost = massDistributionHost %massTotal ( )
! Find the masses of material that will end up in the spheroid component of the remnant.
select case (destinationGasHost%ID)
case (destinationMergerSpheroid%ID)
@@ -250,5 +247,10 @@ subroutine simpleGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSphe
factorAngularMomentum=1.0d0
! Compute the mass of the host spheroid before the merger.
massSpheroidHostPreMerger=spheroidHost%massStellar()+spheroidHost%massGas()
+ ! Clean up.
+ !![
+
+
+ !!]
return
end subroutine simpleGet
diff --git a/source/satellites.merging.progenitor_properties.standard.F90 b/source/satellites.merging.progenitor_properties.standard.F90
index f8f120a22e..00450a7b62 100644
--- a/source/satellites.merging.progenitor_properties.standard.F90
+++ b/source/satellites.merging.progenitor_properties.standard.F90
@@ -22,7 +22,6 @@
!!}
use :: Satellite_Merging_Mass_Movements, only : mergerMassMovementsClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -57,7 +56,6 @@
A merger progenitor properties class which uses a standard calculation.
!!}
private
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
class(mergerMassMovementsClass), pointer :: mergerMassMovements_ => null()
contains
final :: standardDestructor
@@ -86,7 +84,6 @@ function standardConstructorParameters(parameters) result(self)
type (mergerProgenitorPropertiesStandard) :: self
type (inputParameters ), intent(inout) :: parameters
class(mergerMassMovementsClass ), pointer :: mergerMassMovements_
- class(galacticStructureClass ), pointer :: galacticStructure_
if ( &
& .not. &
@@ -130,27 +127,24 @@ function standardConstructorParameters(parameters) result(self)
& )
!![
-
!!]
- self=mergerProgenitorPropertiesStandard(mergerMassMovements_,galacticStructure_)
+ self=mergerProgenitorPropertiesStandard(mergerMassMovements_)
!![
-
!!]
return
end function standardConstructorParameters
- function standardConstructorInternal(mergerMassMovements_,galacticStructure_) result(self)
+ function standardConstructorInternal(mergerMassMovements_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily standard} merger progenitor properties class.
!!}
implicit none
type (mergerProgenitorPropertiesStandard) :: self
class(mergerMassMovementsClass ), intent(in ), target :: mergerMassMovements_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -165,7 +159,6 @@ subroutine standardDestructor(self)
!![
-
!!]
return
end subroutine standardDestructor
@@ -179,6 +172,7 @@ subroutine standardGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid , treeNode
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Satellite_Merging_Mass_Movements, only : destinationMergerDisk , destinationMergerSpheroid, destinationMergerUnmoved, enumerationDestinationMergerType
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (mergerProgenitorPropertiesStandard), intent(inout), target :: self
type (treeNode ), intent(inout), target :: nodeSatellite , nodeHost
@@ -187,6 +181,7 @@ subroutine standardGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
& massSpheroidHostPreMerger , massGasSpheroidRemnant , &
& massSpheroidRemnant , massSatellite , &
& radiusSatellite , massSpheroidSatellite
+ class (massDistributionClass ), pointer :: massDistributionHost , massDistributionSatellite
class (nodeComponentDisk ), pointer :: diskHost , diskSatellite
class (nodeComponentSpheroid ), pointer :: spheroidHost , spheroidSatellite
double precision , parameter :: massComponentMinimum=1.0d-30
@@ -207,8 +202,10 @@ subroutine standardGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
diskSatellite => nodeSatellite%disk ()
spheroidSatellite => nodeSatellite%spheroid()
! Find the baryonic masses of the two galaxies.
- massSatellite=self%galacticStructure_%massEnclosed(nodeSatellite,massType=massTypeGalactic)
- massHost =self%galacticStructure_%massEnclosed(nodeHost ,massType=massTypeGalactic)
+ massDistributionSatellite => nodeSatellite %massDistribution(massType=massTypeGalactic)
+ massDistributionHost => nodeHost %massDistribution(massType=massTypeGalactic)
+ massSatellite = massDistributionSatellite%massTotal ( )
+ massHost = massDistributionHost %massTotal ( )
! Compute dark matter factors. These are the specific angular momenta of components divided by sqrt(G M r) where M is the
! component mass and r its half-mass radius. We use a weighted average of these factors to infer the specific angular momentum
! of the remnant from its mass and radius.
@@ -346,5 +343,10 @@ subroutine standardGet(self,nodeSatellite,nodeHost,massSatellite,massHost,massSp
! Compute the mass of the host spheroid before the merger.
massSpheroidHostPreMerger=spheroidHost%massStellar()+spheroidHost%massGas()
if (radiusHost <= 0.0d0) massSpheroidHostPreMerger=0.0d0
+ ! Clean up.
+ !![
+
+
+ !!]
return
end subroutine standardGet
diff --git a/source/satellites.merging.remnant_sizes.Cole2000.F90 b/source/satellites.merging.remnant_sizes.Cole2000.F90
index 2ceddda736..d1e18d46ae 100644
--- a/source/satellites.merging.remnant_sizes.Cole2000.F90
+++ b/source/satellites.merging.remnant_sizes.Cole2000.F90
@@ -23,7 +23,6 @@
use :: Kind_Numbers , only : kind_int8
use :: Satellite_Merging_Progenitor_Properties, only : mergerProgenitorPropertiesClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -73,7 +72,6 @@
A merger remnant size class which uses the \cite{cole_hierarchical_2000} algorithm.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (mergerProgenitorPropertiesClass), pointer :: mergerProgenitorProperties_ => null()
double precision :: energyOrbital
integer (kind=kind_int8 ) :: lastUniqueID
@@ -105,7 +103,6 @@ function cole2000ConstructorParameters(parameters) result(self)
type (mergerRemnantSizeCole2000 ) :: self
type (inputParameters ), intent(inout) :: parameters
class (mergerProgenitorPropertiesClass), pointer :: mergerProgenitorProperties_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: energyOrbital
!![
@@ -116,18 +113,16 @@ function cole2000ConstructorParameters(parameters) result(self)
-
!!]
- self=mergerRemnantSizeCole2000(energyOrbital,mergerProgenitorProperties_,galacticStructure_)
+ self=mergerRemnantSizeCole2000(energyOrbital,mergerProgenitorProperties_)
!![
-
!!]
return
end function cole2000ConstructorParameters
- function cole2000ConstructorInternal(energyOrbital,mergerProgenitorProperties_,galacticStructure_) result(self)
+ function cole2000ConstructorInternal(energyOrbital,mergerProgenitorProperties_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily cole2000} merger remnant size class.
!!}
@@ -135,9 +130,8 @@ function cole2000ConstructorInternal(energyOrbital,mergerProgenitorProperties_,g
type (mergerRemnantSizeCole2000 ) :: self
double precision , intent(in ) :: energyOrbital
class (mergerProgenitorPropertiesClass), intent(in ), target :: mergerProgenitorProperties_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
self%propertiesCalculated =.false.
@@ -170,7 +164,6 @@ subroutine cole2000Destructor(self)
!![
-
!!]
if (calculationResetEvent%isAttached(self,cole2000CalculationReset)) call calculationResetEvent%detach(self,cole2000CalculationReset)
if (satelliteMergerEvent %isAttached(self,cole2000GetHook )) call satelliteMergerEvent %detach(self,cole2000GetHook )
@@ -226,6 +219,7 @@ subroutine cole2000Get(self,node,radius,velocityCircular,angularMomentumSpecific
use :: Display , only : displayMessage
use :: Galactic_Structure_Options , only : massTypeDark
use :: Error , only : Error_Report
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Comparison , only : Values_Agree
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: String_Handling , only : operator(//)
@@ -235,6 +229,7 @@ subroutine cole2000Get(self,node,radius,velocityCircular,angularMomentumSpecific
double precision , intent( out) :: radius , velocityCircular , &
& angularMomentumSpecific
type (treeNode ), pointer :: nodeHost
+ class (massDistributionClass ), pointer :: massDistributionSatellite , massDistributionHost
double precision , parameter :: formFactorEnergyBinding =0.5d+0
double precision , parameter :: toleranceMassAbsolute =1.0d+0
double precision , parameter :: toleranceMassRelative =1.0d-9
@@ -347,8 +342,14 @@ subroutine cole2000Get(self,node,radius,velocityCircular,angularMomentumSpecific
! Check if host has finite mass.
if (massSpheroidSatellite+massSpheroidHost > 0.0d0) then
! Compute masses of dark matter within the host and satellite radii.
- massDarkMatterHost =self%galacticStructure_%massEnclosed(nodeHost,radiusHost ,massType=massTypeDark)
- massDarkMatterSatellite=self%galacticStructure_%massEnclosed(node ,radiusSatellite,massType=massTypeDark)
+ massDistributionHost => nodeHost%massDistribution(massType=massTypeDark)
+ massDistributionSatellite => node %massDistribution(massType=massTypeDark)
+ massDarkMatterHost = massDistributionHost %massEnclosedBySphere(radiusHost )
+ massDarkMatterSatellite = massDistributionSatellite%massEnclosedBySphere(radiusSatellite)
+ !![
+
+
+ !!]
! Combine baryonic and dark matter masses.
massSpheroidHostTotal =+massSpheroidHost +2.0d0*massDarkMatterHost
massSpheroidTotalSatellite=+massSpheroidSatellite+2.0d0*massDarkMatterSatellite
diff --git a/source/satellites.merging.timescale.Boylan-Kolchin2008.F90 b/source/satellites.merging.timescale.Boylan-Kolchin2008.F90
index 5e97e4b82b..0fd0601127 100644
--- a/source/satellites.merging.timescale.Boylan-Kolchin2008.F90
+++ b/source/satellites.merging.timescale.Boylan-Kolchin2008.F90
@@ -23,7 +23,6 @@
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -40,7 +39,6 @@
private
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: timescaleMultiplier
contains
final :: boylanKolchin2008Destructor
@@ -68,7 +66,6 @@ function boylanKolchin2008ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: timescaleMultiplier
!![
@@ -80,19 +77,17 @@ function boylanKolchin2008ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteMergingTimescalesBoylanKolchin2008(timescaleMultiplier,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteMergingTimescalesBoylanKolchin2008(timescaleMultiplier,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
!!]
return
end function boylanKolchin2008ConstructorParameters
- function boylanKolchin2008ConstructorInternal(timescaleMultiplier,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function boylanKolchin2008ConstructorInternal(timescaleMultiplier,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Default constructor for the {\normalfont \ttfamily boylanKolchin2008} satellite merging timescale class.
!!}
@@ -101,9 +96,8 @@ function boylanKolchin2008ConstructorInternal(timescaleMultiplier,darkMatterHalo
double precision , intent(in ) :: timescaleMultiplier
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -119,7 +113,6 @@ subroutine boylanKolchin2008Destructor(self)
!![
-
!!]
return
end subroutine boylanKolchin2008Destructor
@@ -128,16 +121,18 @@ double precision function boylanKolchin2008TimeUntilMerging(self,node,orbit)
!!{
Return the timescale for merging satellites using the \cite{boylan-kolchin_dynamical_2008} method.
!!}
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes, only : nodeComponentBasic , treeNode
- use :: Kepler_Orbits , only : keplerOrbit
- use :: Satellite_Orbits, only : Satellite_Orbit_Equivalent_Circular_Orbit_Radius, errorCodeNoEquivalentOrbit, errorCodeOrbitUnbound, errorCodeSuccess
+ use :: Error , only : Error_Report
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions, only : massDistributionClass
+ use :: Kepler_Orbits , only : keplerOrbit
+ use :: Satellite_Orbits , only : Satellite_Orbit_Equivalent_Circular_Orbit_Radius, errorCodeNoEquivalentOrbit, errorCodeOrbitUnbound, errorCodeSuccess
implicit none
class (satelliteMergingTimescalesBoylanKolchin2008), intent(inout) :: self
type (treeNode ), intent(inout) :: node
type (keplerOrbit ), intent(inout) :: orbit
type (treeNode ), pointer :: nodeHost
class (nodeComponentBasic ), pointer :: basicHost , basic
+ class (massDistributionClass ), pointer :: massDistribution_
logical , parameter :: acceptUnboundOrbits =.false.
double precision , parameter :: expArgumentMaximum =100.0d0
double precision , parameter :: A =0.216d0, b =1.3d0, & ! Fitting parameters from eqn. (6) of Boylan-Kolchin et al.
@@ -158,7 +153,7 @@ double precision function boylanKolchin2008TimeUntilMerging(self,node,orbit)
velocityScale=self%darkMatterHaloScale_%velocityVirial(nodeHost)
radialScale =self%darkMatterHaloScale_%radiusVirial (nodeHost)
! Get the equivalent circular orbit.
- equivalentCircularOrbitRadius=Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,self%darkMatterHaloScale_,self%darkMatterProfileDMO_,self%galacticStructure_,errorCode)
+ equivalentCircularOrbitRadius=Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,self%darkMatterHaloScale_,errorCode)
! Check error codes.
select case (errorCode)
case (errorCodeOrbitUnbound )
@@ -172,10 +167,13 @@ double precision function boylanKolchin2008TimeUntilMerging(self,node,orbit)
return
case (errorCodeSuccess )
! Compute orbital circularity.
- orbitalCircularity &
- & =orbit%angularMomentum() &
- & /equivalentCircularOrbitRadius &
- & /self%darkMatterProfileDMO_%circularVelocity(nodeHost,equivalentCircularOrbitRadius)
+ massDistribution_ => self %darkMatterProfileDMO_%get (nodeHost )
+ orbitalCircularity = +orbit %angularMomentum( ) &
+ & /massDistribution_ %rotationCurve (equivalentCircularOrbitRadius) &
+ & /equivalentCircularOrbitRadius
+ !![
+
+ !!]
case default
orbitalCircularity=0.0d0
call Error_Report('unrecognized error code'//{introspection:location})
diff --git a/source/satellites.merging.timescale.Jiang2008.F90 b/source/satellites.merging.timescale.Jiang2008.F90
index 301d533b3b..20a3542953 100644
--- a/source/satellites.merging.timescale.Jiang2008.F90
+++ b/source/satellites.merging.timescale.Jiang2008.F90
@@ -23,7 +23,6 @@
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -46,7 +45,6 @@
private
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: timescaleMultiplier
! Scatter (in log(T_merge)) to add to the merger times.
double precision :: scatter
@@ -77,7 +75,6 @@ function jiang2008ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: scatter , timescaleMultiplier
if (.not.defaultBasicComponent%massIsGettable()) call Error_Report('this method requires that the "mass" property of the basic component be gettable'//{introspection:location})
@@ -96,19 +93,17 @@ function jiang2008ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteMergingTimescalesJiang2008(timescaleMultiplier,scatter,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_)
+ self=satelliteMergingTimescalesJiang2008(timescaleMultiplier,scatter,darkMatterHaloScale_,darkMatterProfileDMO_)
!![
-
!!]
return
end function jiang2008ConstructorParameters
- function jiang2008ConstructorInternal(timescaleMultiplier,scatter,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function jiang2008ConstructorInternal(timescaleMultiplier,scatter,darkMatterHaloScale_,darkMatterProfileDMO_) result(self)
!!{
Constructor for the \cite{jiang_fitting_2008} merging timescale class.
!!}
@@ -117,9 +112,8 @@ function jiang2008ConstructorInternal(timescaleMultiplier,scatter,darkMatterHalo
double precision , intent(in ) :: timescaleMultiplier , scatter
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -135,7 +129,6 @@ subroutine jiang2008Destructor(self)
!![
-
!!]
return
end subroutine jiang2008Destructor
@@ -144,15 +137,17 @@ double precision function jiang2008TimeUntilMerging(self,node,orbit)
!!{
Return the timescale for merging satellites using the \cite{jiang_fitting_2008} method.
!!}
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes, only : nodeComponentBasic , treeNode
- use :: Satellite_Orbits, only : Satellite_Orbit_Equivalent_Circular_Orbit_Radius, errorCodeNoEquivalentOrbit, errorCodeOrbitUnbound, errorCodeSuccess
+ use :: Error , only : Error_Report
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions, only : massDistributionClass
+ use :: Satellite_Orbits , only : Satellite_Orbit_Equivalent_Circular_Orbit_Radius, errorCodeNoEquivalentOrbit, errorCodeOrbitUnbound, errorCodeSuccess
implicit none
class (satelliteMergingTimescalesJiang2008), intent(inout) :: self
type (treeNode ), intent(inout) :: node
type (keplerOrbit ), intent(inout) :: orbit
type (treeNode ), pointer :: nodeHost
class (nodeComponentBasic ), pointer :: basicHost , basic
+ class (massDistributionClass ), pointer :: massDistribution_
logical , parameter :: acceptUnboundOrbits =.false.
double precision , parameter :: C =0.43d0 , a =0.94d0, & ! Fitting parameters from Jiang's paper.
@@ -169,7 +164,7 @@ double precision function jiang2008TimeUntilMerging(self,node,orbit)
nodeHost => node%parent%firstChild
end if
! Get the equivalent circular orbit.
- equivalentCircularOrbitRadius=Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,self%darkMatterHaloScale_,self%darkMatterProfileDMO_,self%galacticStructure_,errorCode)
+ equivalentCircularOrbitRadius=Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,self%darkMatterHaloScale_,errorCode)
! Check error codes.
select case (errorCode)
case (errorCodeOrbitUnbound )
@@ -187,9 +182,13 @@ double precision function jiang2008TimeUntilMerging(self,node,orbit)
velocityScale=self%darkMatterHaloScale_%velocityVirial(nodeHost)
radialScale =self%darkMatterHaloScale_%radiusVirial (nodeHost)
! Compute orbital circularity.
- orbitalCircularity= orbit%angularMomentum() &
- & /equivalentCircularOrbitRadius &
- & /self%darkMatterProfileDMO_%circularVelocity(nodeHost,equivalentCircularOrbitRadius)
+ massDistribution_ => self %darkMatterProfileDMO_%get (nodeHost )
+ orbitalCircularity = +orbit %angularMomentum( ) &
+ & /massDistribution_ %rotationCurve (equivalentCircularOrbitRadius) &
+ & /equivalentCircularOrbitRadius
+ !![
+
+ !!]
! Compute mass ratio (mass in host [not including satellite if the node is already a satellite] divided by mass in satellite).
basic => node %basic()
basicHost => nodeHost %basic()
diff --git a/source/satellites.merging.virial_orbits.Benson2005.F90 b/source/satellites.merging.virial_orbits.Benson2005.F90
index 9b9dadb32c..1be98dbf86 100644
--- a/source/satellites.merging.virial_orbits.Benson2005.F90
+++ b/source/satellites.merging.virial_orbits.Benson2005.F90
@@ -21,11 +21,10 @@
An implementation of virial orbits using the \cite{benson_orbital_2005} orbital parameter distribution.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass, virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass, virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
!![
@@ -49,7 +48,6 @@
!!}
private
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class(virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
class(cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -87,29 +85,26 @@ function benson2005ConstructorParameters(parameters) result(self)
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(virialDensityContrastClass), pointer :: virialDensityContrast_
!![
-
!!]
- self=virialOrbitBenson2005(darkMatterHaloScale_,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitBenson2005(darkMatterHaloScale_,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_)
!![
-
!!]
return
end function benson2005ConstructorParameters
- function benson2005ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function benson2005ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily benson2005} virial orbits class.
!!}
@@ -120,9 +115,8 @@ function benson2005ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,
class(cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(virialDensityContrastClass), intent(in ), target :: virialDensityContrast_
- class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
!![
-
+
!!]
allocate(self%virialDensityContrastDefinition_)
@@ -144,7 +138,6 @@ subroutine benson2005Destructor(self)
-
!!]
return
@@ -190,7 +183,6 @@ function benson2005Orbit(self,node,host,acceptUnboundOrbits)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -198,7 +190,6 @@ function benson2005Orbit(self,node,host,acceptUnboundOrbits)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
! Select an orbit.
@@ -275,7 +266,7 @@ double precision function benson2005VelocityTangentialMagnitudeMean(self,node,ho
!$GLC attributes unused :: node
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
benson2005VelocityTangentialMagnitudeMean = +velocityTangentialMean &
& *velocityHost
return
@@ -312,7 +303,7 @@ double precision function benson2005AngularMomentumMagnitudeMean(self,node,host)
basic => node%basic()
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
benson2005AngularMomentumMagnitudeMean = +self%velocityTangentialMagnitudeMean(node,host) &
& *radiusHost &
& /( & ! Account for reduced mass.
@@ -356,7 +347,7 @@ double precision function benson2005VelocityTotalRootMeanSquared(self,node,host)
!$GLC attributes unused :: node
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
benson2005VelocityTotalRootMeanSquared = +velocityTotalRootMeanSquared &
& *velocityHost
return
@@ -378,7 +369,7 @@ double precision function benson2005EnergyMean(self,node,host)
basic => node%basic()
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrastDefinition_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
benson2005EnergyMean = +0.5d0 &
& *self%velocityTotalRootMeanSquared(node,host)**2 &
& /( & ! Account for reduced mass.
diff --git a/source/satellites.merging.virial_orbits.Jiang2014.F90 b/source/satellites.merging.virial_orbits.Jiang2014.F90
index 503339a4fe..77155aaedf 100644
--- a/source/satellites.merging.virial_orbits.Jiang2014.F90
+++ b/source/satellites.merging.virial_orbits.Jiang2014.F90
@@ -21,13 +21,12 @@
An implementation of virial orbits using the \cite{jiang_orbital_2014} orbital parameter distribution.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Root_Finder , only : rootFinder
- use :: Tables , only : table1DLinearLinear
- use :: Virial_Density_Contrast , only : virialDensityContrastClass, virialDensityContrastFixed
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Root_Finder , only : rootFinder
+ use :: Tables , only : table1DLinearLinear
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass, virialDensityContrastFixed
!![
@@ -53,7 +52,6 @@
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
type (virialDensityContrastFixed), pointer :: virialDensityContrastDefinition_ => null()
double precision , dimension(3,3) :: B , gamma , &
@@ -111,7 +109,6 @@ function jiang2014ConstructorParameters(parameters) result(self)
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass), pointer :: virialDensityContrast_
double precision , dimension(3) :: bRatioLow , bRatioIntermediate , bRatioHigh , &
& gammaRatioLow , gammaRatioIntermediate, gammaRatioHigh, &
@@ -194,22 +191,20 @@ function jiang2014ConstructorParameters(parameters) result(self)
-
!!]
- self=virialOrbitJiang2014(bRatioLow,bRatioIntermediate,bRatioHigh,gammaRatioLow,gammaRatioIntermediate,gammaRatioHigh,sigmaRatioLow,sigmaRatioIntermediate,sigmaRatioHigh,muRatioLow,muRatioIntermediate,muRatioHigh,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitJiang2014(bRatioLow,bRatioIntermediate,bRatioHigh,gammaRatioLow,gammaRatioIntermediate,gammaRatioHigh,sigmaRatioLow,sigmaRatioIntermediate,sigmaRatioHigh,muRatioLow,muRatioIntermediate,muRatioHigh,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_)
!![
-
!!]
return
end function jiang2014ConstructorParameters
- function jiang2014ConstructorInternal(bRatioLow,bRatioIntermediate,bRatioHigh,gammaRatioLow,gammaRatioIntermediate,gammaRatioHigh,sigmaRatioLow,sigmaRatioIntermediate,sigmaRatioHigh,muRatioLow,muRatioIntermediate,muRatioHigh,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function jiang2014ConstructorInternal(bRatioLow,bRatioIntermediate,bRatioHigh,gammaRatioLow,gammaRatioIntermediate,gammaRatioHigh,sigmaRatioLow,sigmaRatioIntermediate,sigmaRatioHigh,muRatioLow,muRatioIntermediate,muRatioHigh,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily jiang2014} virial orbits class.
!!}
@@ -233,7 +228,6 @@ function jiang2014ConstructorInternal(bRatioLow,bRatioIntermediate,bRatioHigh,ga
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
integer , parameter :: tableCount =1000
integer :: i , j , k , &
& attempt
@@ -248,7 +242,7 @@ function jiang2014ConstructorInternal(bRatioLow,bRatioIntermediate,bRatioHigh,ga
type (hdf5Object ) :: file
type (lockDescriptor ) :: fileLock
!![
-
+
!!]
! Assign parameters of the distribution.
@@ -455,7 +449,6 @@ subroutine jiang2014Destructor(self)
-
!!]
@@ -503,7 +496,6 @@ function jiang2014Orbit(self,node,host,acceptUnboundOrbits)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -511,7 +503,6 @@ function jiang2014Orbit(self,node,host,acceptUnboundOrbits)
& virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -645,7 +636,6 @@ double precision function jiang2014VelocityTangentialMagnitudeMean(self,node,hos
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite = Dark_Matter_Profile_Mass_Definition( &
@@ -653,7 +643,6 @@ double precision function jiang2014VelocityTangentialMagnitudeMean(self,node,hos
& virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -703,7 +692,6 @@ double precision function jiang2014AngularMomentumMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
jiang2014AngularMomentumMagnitudeMean = +self%velocityTangentialMagnitudeMean(node,host) &
@@ -759,7 +747,6 @@ double precision function jiang2014VelocityTotalRootMeanSquared(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite = Dark_Matter_Profile_Mass_Definition( &
@@ -767,7 +754,6 @@ double precision function jiang2014VelocityTotalRootMeanSquared(self,node,host)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -802,7 +788,6 @@ double precision function jiang2014EnergyMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
jiang2014EnergyMean = +0.5d0 &
diff --git a/source/satellites.merging.virial_orbits.Li2020.F90 b/source/satellites.merging.virial_orbits.Li2020.F90
index 93a2ef12f6..534f77e063 100644
--- a/source/satellites.merging.virial_orbits.Li2020.F90
+++ b/source/satellites.merging.virial_orbits.Li2020.F90
@@ -26,7 +26,6 @@
use :: Cosmological_Density_Field, only : cosmologicalMassVarianceClass , criticalOverdensityClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Virial_Density_Contrast , only : virialDensityContrastBryanNorman1998, virialDensityContrastClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
!![
@@ -49,7 +48,6 @@
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
type (virialDensityContrastBryanNorman1998), pointer :: virialDensityContrastDefinition_ => null()
double precision :: mu1 , mu2 , &
@@ -100,7 +98,6 @@ function li2020ConstructorParameters(parameters) result(self)
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (criticalOverdensityClass ), pointer :: criticalOverdensity_
class (cosmologicalMassVarianceClass), pointer :: cosmologicalMassVariance_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
double precision :: mu1 , mu2 , &
& a0 , a1 , &
@@ -191,10 +188,9 @@ function li2020ConstructorParameters(parameters) result(self)
-
!!]
- self=virialOrbitLi2020(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateOrbits,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitLi2020(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateOrbits,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_)
!![
@@ -202,13 +198,12 @@ function li2020ConstructorParameters(parameters) result(self)
-
!!]
return
end function li2020ConstructorParameters
- function li2020ConstructorInternal(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateOrbits,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function li2020ConstructorInternal(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateOrbits,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,criticalOverdensity_,cosmologicalMassVariance_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily li2020} virial orbits class.
!!}
@@ -220,7 +215,6 @@ function li2020ConstructorInternal(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateO
class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
class (cosmologicalMassVarianceClass), intent(in ), target :: cosmologicalMassVariance_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
double precision , intent(in ) :: mu1 , mu2 , &
& a0 , a1 , &
& a2 , a3 , &
@@ -228,7 +222,7 @@ function li2020ConstructorInternal(mu1,mu2,sigma1,a0,a1,a2,a3,b1,b2,c,propagateO
& c , sigma1
logical , intent(in ) :: propagateOrbits
!![
-
+
!!]
! Create virial density contrast definition.
@@ -260,7 +254,6 @@ subroutine li2020Destructor(self)
-
!!]
@@ -307,7 +300,6 @@ function li2020Orbit(self,node,host,acceptUnboundOrbits)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -315,7 +307,6 @@ function li2020Orbit(self,node,host,acceptUnboundOrbits)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -436,7 +427,6 @@ function li2020VelocityDistributionFunction(self,node,host,velocityRadial,veloci
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -444,7 +434,6 @@ function li2020VelocityDistributionFunction(self,node,host,velocityRadial,veloci
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -530,7 +519,6 @@ double precision function li2020VelocityTangentialMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -538,7 +526,6 @@ double precision function li2020VelocityTangentialMagnitudeMean(self,node,host)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -630,7 +617,6 @@ double precision function li2020AngularMomentumMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
if (massHost > 0.0d0) then
@@ -689,7 +675,6 @@ double precision function li2020VelocityTotalRootMeanSquared(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -697,7 +682,6 @@ double precision function li2020VelocityTotalRootMeanSquared(self,node,host)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -732,7 +716,6 @@ double precision function li2020EnergyMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
li2020EnergyMean = +0.5d0 &
diff --git a/source/satellites.merging.virial_orbits.Wetzel2010.F90 b/source/satellites.merging.virial_orbits.Wetzel2010.F90
index 49f1f1ee76..8eafed5c31 100644
--- a/source/satellites.merging.virial_orbits.Wetzel2010.F90
+++ b/source/satellites.merging.virial_orbits.Wetzel2010.F90
@@ -65,7 +65,6 @@ use this value rather than $10$ since the GSL $_2F_1$ hypergeometric function fa
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
type (virialDensityContrastFriendsOfFriends), pointer :: virialDensityContrastDefinition_ => null()
contains
@@ -119,7 +118,6 @@ function wetzel2010ConstructorParameters(parameters) result(self)
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(criticalOverdensityClass ), pointer :: criticalOverdensity_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(virialDensityContrastClass), pointer :: virialDensityContrast_
!![
@@ -127,10 +125,9 @@ function wetzel2010ConstructorParameters(parameters) result(self)
-
!!]
- self=virialOrbitWetzel2010(darkMatterHaloScale_,cosmologyFunctions_,criticalOverdensity_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitWetzel2010(darkMatterHaloScale_,cosmologyFunctions_,criticalOverdensity_,cosmologyParameters_,virialDensityContrast_)
!![
@@ -138,13 +135,12 @@ function wetzel2010ConstructorParameters(parameters) result(self)
-
!!]
return
end function wetzel2010ConstructorParameters
- function wetzel2010ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,criticalOverdensity_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function wetzel2010ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,criticalOverdensity_,cosmologyParameters_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily wetzel2010} virial orbits class.
!!}
@@ -155,14 +151,13 @@ function wetzel2010ConstructorInternal(darkMatterHaloScale_,cosmologyFunctions_,
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (virialDensityContrastClass), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative =1.0d-2
integer :: iRadius
double precision :: x , xGamma2 , &
& probabilityCumulative , probabilityCumulativeNormalization
!![
-
+
!!]
! Initialize root finder.
@@ -219,7 +214,6 @@ subroutine wetzel2010Destructor(self)
-
!!]
@@ -268,7 +262,6 @@ function wetzel2010Orbit(self,node,host,acceptUnboundOrbits)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -276,7 +269,6 @@ function wetzel2010Orbit(self,node,host,acceptUnboundOrbits)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
@@ -427,7 +419,6 @@ double precision function wetzel2010AngularMomentumMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
wetzel2010AngularMomentumMagnitudeMean = +self%velocityTangentialMagnitudeMean(node,host) &
@@ -494,7 +485,6 @@ double precision function wetzel2010EnergyMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
wetzel2010EnergyMean = +0.5d0 &
diff --git a/source/satellites.merging.virial_orbits.fixed.F90 b/source/satellites.merging.virial_orbits.fixed.F90
index 1c2078864f..aa9d6b7dc1 100644
--- a/source/satellites.merging.virial_orbits.fixed.F90
+++ b/source/satellites.merging.virial_orbits.fixed.F90
@@ -21,11 +21,10 @@
An implementation of virial orbits which assumes fixed orbital parameters.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -43,7 +42,6 @@
double precision :: velocityRadial , velocityTangential
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (virialDensityContrastClass), pointer :: virialDensityContrast_ => null(), virialDensityContrastDefinition_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
contains
@@ -78,7 +76,6 @@ function fixedConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (virialDensityContrastClass), pointer :: virialDensityContrast_, virialDensityContrastDefinition_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: velocityRadial , velocityTangential
@@ -98,25 +95,23 @@ function fixedConstructorParameters(parameters) result(self)
-
!!]
- self=virialOrbitFixed(velocityRadial,velocityTangential,virialDensityContrastDefinition_,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitFixed(velocityRadial,velocityTangential,virialDensityContrastDefinition_,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_)
!![
-
!!]
return
end function fixedConstructorParameters
- function fixedConstructorInternal(velocityRadial,velocityTangential,virialDensityContrastDefinition_,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function fixedConstructorInternal(velocityRadial,velocityTangential,virialDensityContrastDefinition_,darkMatterHaloScale_,cosmologyParameters_,cosmologyFunctions_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily fixed} virial orbits class.
!!}
@@ -128,10 +123,9 @@ function fixedConstructorInternal(velocityRadial,velocityTangential,virialDensit
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
double precision , intent(in ) :: velocityRadial , velocityTangential
!![
-
+
!!]
return
@@ -147,7 +141,6 @@ subroutine fixedDestructor(self)
!![
-
@@ -189,7 +182,6 @@ function fixedOrbit(self,node,host,acceptUnboundOrbits)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite=Dark_Matter_Profile_Mass_Definition( &
@@ -197,7 +189,6 @@ function fixedOrbit(self,node,host,acceptUnboundOrbits)
& self%virialDensityContrastDefinition_%densityContrast( basic%mass(), basic%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
! Set basic properties of the orbit - do not allow the satellite mass to exceed the host mass.
@@ -267,7 +258,6 @@ double precision function fixedVelocityTangentialMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
fixedVelocityTangentialMagnitudeMean = +self%velocityTangential &
@@ -313,7 +303,6 @@ double precision function fixedAngularMomentumMagnitudeMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
fixedAngularMomentumMagnitudeMean = +self%velocityTangentialMagnitudeMean(node,host) &
@@ -364,7 +353,6 @@ double precision function fixedVelocityTotalRootMeanSquared(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
fixedVelocityTotalRootMeanSquared = +sqrt( &
@@ -398,7 +386,6 @@ double precision function fixedEnergyMean(self,node,host)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
fixedEnergyMean = +0.5d0 &
diff --git a/source/satellites.merging.virial_orbits.loss_cone.F90 b/source/satellites.merging.virial_orbits.loss_cone.F90
index 108d168163..3a3f4952b4 100644
--- a/source/satellites.merging.virial_orbits.loss_cone.F90
+++ b/source/satellites.merging.virial_orbits.loss_cone.F90
@@ -26,7 +26,6 @@
use :: Cosmological_Velocity_Field , only : cosmologicalVelocityFieldClass
use :: Cosmological_Density_Field , only : cosmologicalMassVarianceClass , cosmologicalMassVariancePeakBackgroundSplit, criticalOverdensityClass, criticalOverdensityPeakBackgroundSplit, &
& haloEnvironmentNormal
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
use :: Halo_Mass_Functions , only : haloMassFunctionShethTormen
use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBiasClass
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
@@ -54,7 +53,6 @@
class (cosmologicalVelocityFieldClass ), pointer :: cosmologicalVelocityField_ => null()
class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null()
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (linearGrowthClass ), pointer :: linearGrowth_ => null()
@@ -149,7 +147,6 @@ function lossConeConstructorParameters(parameters) result(self)
class (criticalOverdensityClass ), pointer :: criticalOverdensity_
class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (linearGrowthClass ), pointer :: linearGrowth_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
class (correlationFunctionTwoPointClass ), pointer :: correlationFunctionTwoPoint_
@@ -219,12 +216,11 @@ The normalization parameter $A$ of the \cite{sheth_ellipsoidal_2001
-
!!]
- self=virialOrbitLossCone(velocityMinimum,velocityMaximum,countVelocitiesPerUnit,countMassesPerDecade,includeInFlightGrowth,haloMassFunctionA,haloMassFunctionP,haloMassFunctionNormalization,velocityDispersionMultiplier,cosmologyFunctions_,cosmologyParameters_,cosmologicalVelocityField_,linearGrowth_,darkMatterHaloBias_,darkMatterHaloScale_,virialDensityContrast_,correlationFunctionTwoPoint_,cosmologicalMassVariance_,criticalOverdensity_,mergerTreeBranchingProbability_,darkMatterProfileDMO_)
+ self=virialOrbitLossCone(velocityMinimum,velocityMaximum,countVelocitiesPerUnit,countMassesPerDecade,includeInFlightGrowth,haloMassFunctionA,haloMassFunctionP,haloMassFunctionNormalization,velocityDispersionMultiplier,cosmologyFunctions_,cosmologyParameters_,cosmologicalVelocityField_,linearGrowth_,darkMatterHaloBias_,darkMatterHaloScale_,virialDensityContrast_,correlationFunctionTwoPoint_,cosmologicalMassVariance_,criticalOverdensity_,mergerTreeBranchingProbability_)
!![
@@ -235,7 +231,6 @@ The normalization parameter $A$ of the \cite{sheth_ellipsoidal_2001
-
@@ -243,7 +238,7 @@ The normalization parameter $A$ of the \cite{sheth_ellipsoidal_2001
return
end function lossConeConstructorParameters
- function lossConeConstructorInternal(velocityMinimum,velocityMaximum,countVelocitiesPerUnit,countMassesPerDecade,includeInFlightGrowth,haloMassFunctionA,haloMassFunctionP,haloMassFunctionNormalization,velocityDispersionMultiplier,cosmologyFunctions_,cosmologyParameters_,cosmologicalVelocityField_,linearGrowth_,darkMatterHaloBias_,darkMatterHaloScale_,virialDensityContrast_,correlationFunctionTwoPoint_,cosmologicalMassVariance_,criticalOverdensity_,mergerTreeBranchingProbability_,darkMatterProfileDMO_) result(self)
+ function lossConeConstructorInternal(velocityMinimum,velocityMaximum,countVelocitiesPerUnit,countMassesPerDecade,includeInFlightGrowth,haloMassFunctionA,haloMassFunctionP,haloMassFunctionNormalization,velocityDispersionMultiplier,cosmologyFunctions_,cosmologyParameters_,cosmologicalVelocityField_,linearGrowth_,darkMatterHaloBias_,darkMatterHaloScale_,virialDensityContrast_,correlationFunctionTwoPoint_,cosmologicalMassVariance_,criticalOverdensity_,mergerTreeBranchingProbability_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily lossCone} virial orbits class.
!!}
@@ -259,7 +254,6 @@ function lossConeConstructorInternal(velocityMinimum,velocityMaximum,countVeloci
class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (linearGrowthClass ), intent(in ), target :: linearGrowth_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
class (correlationFunctionTwoPointClass ), intent(in ), target :: correlationFunctionTwoPoint_
@@ -271,7 +265,7 @@ function lossConeConstructorInternal(velocityMinimum,velocityMaximum,countVeloci
logical , intent(in ) :: includeInFlightGrowth
integer :: countVelocities
!![
-
+
!!]
! Set an initial mass range, along with an unphysical initial time (so that retabulation will be forced on the first call).
@@ -314,7 +308,6 @@ subroutine lossConeDestructor(self)
-
!!]
return
end subroutine lossConeDestructor
@@ -493,7 +486,6 @@ subroutine lossConeInterpolants(self,node,host,massSatellite,massHost,velocityHo
& self%virialDensityContrast_%densityContrast(basicSatellite%mass(),basicSatellite%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massHost = Dark_Matter_Profile_Mass_Definition( &
@@ -503,7 +495,6 @@ subroutine lossConeInterpolants(self,node,host,massSatellite,massHost,velocityHo
& velocity =velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massSatellite = min(massSatellite,massHost)
@@ -588,7 +579,7 @@ double precision function lossConeAngularMomentumMagnitudeMean(self,node,host)
basic => node%basic()
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrast_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrast_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
lossConeAngularMomentumMagnitudeMean = +self%velocityTangentialMagnitudeMean(node,host) &
& *radiusHost &
& /( & ! Account for reduced mass.
@@ -661,7 +652,7 @@ double precision function lossConeEnergyMean(self,node,host)
basic => node%basic()
basicHost => host%basic()
- massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrast_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%darkMatterProfileDMO_,self%virialDensityContrast_)
+ massHost = Dark_Matter_Profile_Mass_Definition(host,self%virialDensityContrast_%densityContrast(basicHost%mass(),basicHost%timeLastIsolated()),radiusHost,velocityHost,self%cosmologyParameters_,self%cosmologyFunctions_,self%virialDensityContrast_)
lossConeEnergyMean = +0.5d0 &
& *self%velocityTotalRootMeanSquared(node,host)**2 &
& /( & ! Account for reduced mass.
@@ -746,7 +737,6 @@ subroutine lossConeTabulate(self,nodeSatelliteTarget,nodeHostTarget)
& self%virialDensityContrast_%densityContrast(basicSatellite%mass(),basicSatellite%timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
massHost = Dark_Matter_Profile_Mass_Definition( &
@@ -754,7 +744,6 @@ subroutine lossConeTabulate(self,nodeSatelliteTarget,nodeHostTarget)
& self%virialDensityContrast_%densityContrast(basicHost %mass(),basicHost %timeLastIsolated()), &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
if ( &
diff --git a/source/satellites.merging.virial_orbits.mass_reduced.F90 b/source/satellites.merging.virial_orbits.mass_reduced.F90
index b4ab5aa75b..b9f882e7fe 100644
--- a/source/satellites.merging.virial_orbits.mass_reduced.F90
+++ b/source/satellites.merging.virial_orbits.mass_reduced.F90
@@ -22,11 +22,9 @@
the primary halo below its ``\gls{dmou}'' value.
!!}
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Virial_Density_Contrast , only : virialDensityContrastClass
+ use :: Cosmology_Parameters , only : cosmologyParametersClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Virial_Density_Contrast, only : virialDensityContrastClass
!![
@@ -43,8 +41,6 @@
!!}
private
class(virialOrbitClass ), pointer :: virialOrbit_ => null()
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class(virialDensityContrastClass), pointer :: virialDensityContrast_ => null()
class(cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
@@ -75,47 +71,39 @@ function massReducedConstructorParameters(parameters) result(self)
type (virialOrbitMassReduced ) :: self
type (inputParameters ), intent(inout) :: parameters
class(virialOrbitClass ), pointer :: virialOrbit_
- class(galacticStructureClass ), pointer :: galacticStructure_
class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class(cosmologyParametersClass ), pointer :: cosmologyParameters_
- class(darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class(virialDensityContrastClass), pointer :: virialDensityContrast_
!![
-
-
!!]
- self=virialOrbitMassReduced(virialOrbit_,galacticStructure_,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_)
+ self=virialOrbitMassReduced(virialOrbit_,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_)
!![
-
-
!!]
return
end function massReducedConstructorParameters
- function massReducedConstructorInternal(virialOrbit_,galacticStructure_,cosmologyFunctions_,cosmologyParameters_,darkMatterProfileDMO_,virialDensityContrast_) result(self)
+ function massReducedConstructorInternal(virialOrbit_,cosmologyFunctions_,cosmologyParameters_,virialDensityContrast_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily massReduced} virial orbits class.
!!}
implicit none
type (virialOrbitMassReduced ) :: self
class(virialOrbitClass ), intent(in ), target :: virialOrbit_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
class(cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class(virialDensityContrastClass), intent(in ), target :: virialDensityContrast_
- class(darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
!![
-
+
!!]
return
@@ -130,11 +118,9 @@ subroutine massReducedDestructor(self)
!![
-
-
!!]
return
end subroutine massReducedDestructor
@@ -147,6 +133,7 @@ function massReducedOrbit(self,node,host,acceptUnboundOrbits) result(orbit)
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBasic
use :: Kepler_Orbits , only : keplerOrbitPhi , keplerOrbitRadius, keplerOrbitTheta, keplerOrbitVelocityTangential
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll
use :: Virial_Density_Contrast , only : virialDensityContrastClass
@@ -158,6 +145,7 @@ function massReducedOrbit(self,node,host,acceptUnboundOrbits) result(orbit)
integer , parameter :: iterationMaximum =1000
class (nodeComponentBasic ), pointer :: basicHost
class (virialDensityContrastClass), pointer :: densityContrastDefinition_
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: velocityHost , massHost , &
& radiusHost , massHostDMO , &
& massSatellite , velocityRadialSquared
@@ -176,14 +164,17 @@ function massReducedOrbit(self,node,host,acceptUnboundOrbits) result(orbit)
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
!!]
! Find the mass of the host including baryons.
- massHost=self%galacticStructure_%massEnclosed(host,radiusHost,massType=massTypeAll,componentType=componentTypeAll)
+ massDistribution_ => host %massDistribution (massType=massTypeAll,componentType=componentTypeAll)
+ massHost = massDistribution_%massEnclosedBySphere( radiusHost )
+ !![
+
+ !!]
! Iterate until an acceptable orbit is found.
acceptOrbit=.false.
iteration =0
@@ -246,6 +237,7 @@ double precision function massReducedVelocityTotalRootMeanSquared(self,node,host
!!}
use :: Dark_Matter_Profile_Mass_Definitions, only : Dark_Matter_Profile_Mass_Definition
use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll
use :: Virial_Density_Contrast , only : virialDensityContrastClass
@@ -254,6 +246,7 @@ double precision function massReducedVelocityTotalRootMeanSquared(self,node,host
type (treeNode ), intent(inout) :: node , host
class (nodeComponentBasic ), pointer :: basicHost
class (virialDensityContrastClass), pointer :: densityContrastDefinition_
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: massHost , radiusHost , &
& velocityHost , massHostDMO
!$GLC attributes unused :: node
@@ -269,13 +262,16 @@ double precision function massReducedVelocityTotalRootMeanSquared(self,node,host
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
!!]
- massHost = +self%galacticStructure_%massEnclosed(host,radiusHost,massType=massTypeAll,componentType=componentTypeAll)
+ massDistribution_ => host %massDistribution (massType=massTypeAll,componentType=componentTypeAll)
+ massHost = massDistribution_%massEnclosedBySphere( radiusHost )
+ !![
+
+ !!]
velocityRootMeanSquared = +sqrt( &
& +self%virialOrbit_%velocityTotalRootMeanSquared(node,host)**2 &
& +2.0d0 &
@@ -297,12 +293,14 @@ double precision function massReducedEnergyMean(self,node,host) result(energyMea
use :: Galacticus_Nodes , only : nodeComponentBasic
use :: Numerical_Constants_Astronomical , only : gravitationalConstantGalacticus
use :: Galactic_Structure_Options , only : componentTypeAll , massTypeAll
+ use :: Mass_Distributions , only : massDistributionClass
use :: Virial_Density_Contrast , only : virialDensityContrastClass
implicit none
class (virialOrbitMassReduced ), intent(inout) :: self
type (treeNode ), intent(inout) :: node , host
class (nodeComponentBasic ), pointer :: basicHost
class (virialDensityContrastClass), pointer :: densityContrastDefinition_
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: massHost , radiusHost , &
& velocityHost , massHostDMO
@@ -317,19 +315,22 @@ double precision function massReducedEnergyMean(self,node,host) result(energyMea
& velocityHost , &
& cosmologyParameters_ =self%cosmologyParameters_ , &
& cosmologyFunctions_ =self%cosmologyFunctions_ , &
- & darkMatterProfileDMO_ =self%darkMatterProfileDMO_ , &
& virialDensityContrast_=self%virialDensityContrast_ &
& )
!![
!!]
- massHost = +self%galacticStructure_%massEnclosed(host,radiusHost,massType=massTypeAll,componentType=componentTypeAll)
- energyMean = +self%virialOrbit_%energyMean(node,host) &
- & +gravitationalConstantGalacticus &
- & *( &
- & +massHost &
- & -massHostDMO &
- & ) &
- & /radiusHost
+ massDistribution_ => host %massDistribution (massType=massTypeAll,componentType=componentTypeAll)
+ massHost = massDistribution_%massEnclosedBySphere( radiusHost )
+ !![
+
+ !!]
+ energyMean=+self%virialOrbit_%energyMean(node,host) &
+ & +gravitationalConstantGalacticus &
+ & *( &
+ & +massHost &
+ & -massHostDMO &
+ & ) &
+ & /radiusHost
return
end function massReducedEnergyMean
diff --git a/source/satellites.merging.virial_orbits.spin_correlated.F90 b/source/satellites.merging.virial_orbits.spin_correlated.F90
index fd7b6a2cbc..a8aea9f50d 100644
--- a/source/satellites.merging.virial_orbits.spin_correlated.F90
+++ b/source/satellites.merging.virial_orbits.spin_correlated.F90
@@ -22,7 +22,7 @@
host halo.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMO, darkMatterProfileDMOClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -35,8 +35,8 @@
!!}
private
double precision :: alpha
- class (virialOrbitClass ), pointer :: virialOrbit_ => null()
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class (virialOrbitClass ), pointer :: virialOrbit_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
contains
final :: spinCorrelatedDestructor
procedure :: orbit => spinCorrelatedOrbit
@@ -69,7 +69,7 @@ function spinCorrelatedConstructorParameters(parameters) result(self)
type (virialOrbitSpinCorrelated) :: self
type (inputParameters ), intent(inout) :: parameters
class (virialOrbitClass ), pointer :: virialOrbit_
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: alpha
!![
@@ -80,18 +80,18 @@ function spinCorrelatedConstructorParameters(parameters) result(self)
The parameter $\alpha$ which expresses the strength of the correlation between satellite orbital angular momentum and the spin of the host halo.
-
+
!!]
- self=virialOrbitSpinCorrelated(alpha,virialOrbit_,darkMatterProfileDMO_)
+ self=virialOrbitSpinCorrelated(alpha,virialOrbit_,darkMatterHaloScale_)
!![
-
-
+
+
!!]
return
end function spinCorrelatedConstructorParameters
- function spinCorrelatedConstructorInternal(alpha,virialOrbit_,darkMatterProfileDMO_) result(self)
+ function spinCorrelatedConstructorInternal(alpha,virialOrbit_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily spinCorrelated} virial orbits class.
!!}
@@ -101,9 +101,9 @@ function spinCorrelatedConstructorInternal(alpha,virialOrbit_,darkMatterProfileD
type (virialOrbitSpinCorrelated) :: self
double precision , intent(in ) :: alpha
class (virialOrbitClass ), intent(in ), target :: virialOrbit_
- class (darkMatterProfileDMOClass), intent(in ), target :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
!![
-
+
!!]
if (.not.defaultSpinComponent%angularMomentumVectorIsGettable()) &
@@ -127,8 +127,8 @@ subroutine spinCorrelatedDestructor(self)
type(virialOrbitSpinCorrelated), intent(inout) :: self
!![
-
-
+
+
!!]
return
end subroutine spinCorrelatedDestructor
@@ -174,7 +174,7 @@ function spinCorrelatedOrbit(self,node,host,acceptUnboundOrbits)
! Compute the cosine of the angle between the angular momentum of this orbit and the spin of the host halo.
spinHost => host %spin ()
spinVector = +spinHost%angularMomentumVector() &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterProfileDMO_)
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterHaloScale_)
coordinates = spinCorrelatedOrbit%position ()
position = coordinates
coordinates = spinCorrelatedOrbit%velocity ()
@@ -234,10 +234,10 @@ function spinCorrelatedVelocityTangentialVectorMean(self,node,host)
class (nodeComponentSpin ), pointer :: spinHost
spinHost => host %spin ( )
- spinCorrelatedVelocityTangentialVectorMean = +self %alpha &
- & *self %velocityTangentialMagnitudeMean(node,host) &
- & *spinHost%angularMomentumVector ( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterProfileDMO_) &
+ spinCorrelatedVelocityTangentialVectorMean = +self %alpha &
+ & *self %velocityTangentialMagnitudeMean(node,host) &
+ & *spinHost%angularMomentumVector ( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterHaloScale_) &
& /3.0d0
return
end function spinCorrelatedVelocityTangentialVectorMean
@@ -267,10 +267,10 @@ function spinCorrelatedAngularMomentumVectorMean(self,node,host)
class (nodeComponentSpin ), pointer :: spinHost
spinHost => host %spin ( )
- spinCorrelatedAngularMomentumVectorMean = +self %alpha &
- & *self %angularMomentumMagnitudeMean(node,host) &
- & *spinHost%angularMomentumVector ( ) &
- & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterProfileDMO_) &
+ spinCorrelatedAngularMomentumVectorMean = +self %alpha &
+ & *self %angularMomentumMagnitudeMean(node,host) &
+ & *spinHost%angularMomentumVector ( ) &
+ & /Dark_Matter_Halo_Angular_Momentum_Scale(host,self%darkMatterHaloScale_) &
& /3.0d0
return
end function spinCorrelatedAngularMomentumVectorMean
diff --git a/source/satellites.orbits.F90 b/source/satellites.orbits.F90
index 0808e66e81..aa05d37c1f 100644
--- a/source/satellites.orbits.F90
+++ b/source/satellites.orbits.F90
@@ -25,22 +25,22 @@ module Satellite_Orbits
!!{
Implements calculations related to satellite orbits.
!!}
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
- use :: Galacticus_Nodes , only : treeNode
- use :: Kind_Numbers , only : kind_int8
+ use :: Galacticus_Nodes , only : treeNode
+ use :: Kind_Numbers , only : kind_int8
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
private
public :: Satellite_Orbit_Equivalent_Circular_Orbit_Radius, Satellite_Orbit_Extremum_Phase_Space_Coordinates
! Orbital energy and angular momentum - used for finding radius of equivalent circular orbit.
- double precision :: orbitalAngularMomentumInternal , orbitalEnergyInternal
+ double precision :: orbitalAngularMomentumInternal , orbitalEnergyInternal
!$omp threadprivate(orbitalEnergyInternal,orbitalAngularMomentumInternal)
- ! Node used in root finding calculations.
+
+ ! Objects used in root finding calculations.
type (treeNode ), pointer :: activeNode
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO__
- class (galacticStructureClass ), pointer :: galacticStructure__
- !$omp threadprivate(activeNode,darkMatterProfileDMO__,galacticStructure__)
+ class (massDistributionClass ), pointer :: massDistribution__
+ double precision :: radiusVirial__ , massVirial__
+ !$omp threadprivate(activeNode,radiusVirial__,massVirial__,massDistribution__)
! Enumeration used to indicate type of extremum.
integer , parameter, public :: extremumPericenter =-1
@@ -66,10 +66,11 @@ module Satellite_Orbits
contains
- double precision function Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,darkMatterHaloScale_,darkMatterProfileDMO_,galacticStructure_,errorCode)
+ double precision function Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeHost,orbit,darkMatterHaloScale_,errorCode)
!!{
Solves for the equivalent circular orbit radius for {\normalfont \ttfamily orbit} in {\normalfont \ttfamily nodeHost}.
!!}
+ use :: Galacticus_Nodes , only : nodeComponentBasic
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
use :: Kepler_Orbits , only : keplerOrbit
use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
@@ -78,22 +79,28 @@ double precision function Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeH
type (keplerOrbit ), intent(inout) :: orbit
integer , intent( out), optional :: errorCode
class (darkMatterHaloScaleClass ), intent(inout) :: darkMatterHaloScale_
- class (darkMatterProfileDMOClass), intent(inout), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(inout), target :: galacticStructure_
- double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6
+ class (nodeComponentBasic ) , pointer :: basicHost
+ double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-6, &
+ & factorRadiusLarge =1.0d6
type (rootFinder ) :: finder
type (keplerOrbit ) :: orbitCurrent
+ double precision :: potential
- ! Convert the orbit to the potential of the current halo in which the satellite finds itself.
- orbitCurrent=Satellite_Orbit_Convert_To_Current_Potential(orbit,nodeHost,galacticStructure_)
! Assign the active node.
- activeNode => nodeHost
- darkMatterProfileDMO__ => darkMatterProfileDMO_
- galacticStructure__ => galacticStructure_
+ activeNode => nodeHost
+ ! Get the mass distribution.
+ massDistribution__ => nodeHost%massDistribution()
+ ! Convert the orbit to the potential of the current halo in which the satellite finds itself.
+ orbitCurrent=Satellite_Orbit_Convert_To_Current_Potential(orbit,nodeHost,darkMatterHaloScale_)
+ ! Get virial properties.
+ basicHost => nodeHost %basic ( )
+ radiusVirial__ = darkMatterHaloScale_ %radiusVirial(nodeHost)
+ massVirial__ = basicHost %mass ( )
! Store the orbital energy.
orbitalEnergyInternal=orbit%energy()
! Test for conditions that an equivalent circular orbit exists.
- if (orbitalEnergyInternal >= 0.0d0) then
+ potential=Satellite_Orbit_Potential(factorRadiusLarge*radiusVirial__,radiusVirial__,massVirial__)
+ if (orbitalEnergyInternal >= potential) then
! Orbit is unbound, return unphysical value.
Satellite_Orbit_Equivalent_Circular_Orbit_Radius=-1.0d0
if (present(errorCode)) errorCode=errorCodeOrbitUnbound
@@ -103,23 +110,26 @@ double precision function Satellite_Orbit_Equivalent_Circular_Orbit_Radius(nodeH
Satellite_Orbit_Equivalent_Circular_Orbit_Radius=-1.0d0
if (present(errorCode)) errorCode=errorCodeNoEquivalentOrbit
else
- finder=rootFinder( &
- & rootFunction =Equivalent_Circular_Orbit_Solver, &
- & toleranceAbsolute =toleranceAbsolute , &
- & toleranceRelative =toleranceRelative , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive , &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- Satellite_Orbit_Equivalent_Circular_Orbit_Radius=finder%find(rootGuess=darkMatterHaloScale_%radiusVirial(nodeHost))
+ finder =rootFinder( &
+ & rootFunction =Equivalent_Circular_Orbit_Solver, &
+ & toleranceAbsolute =toleranceAbsolute , &
+ & toleranceRelative =toleranceRelative , &
+ & rangeExpandUpward =2.0d0 , &
+ & rangeExpandDownward =0.5d0 , &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectNegative , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectPositive , &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
+ Satellite_Orbit_Equivalent_Circular_Orbit_Radius=finder%find(rootGuess=radiusVirial__)
if (present(errorCode)) errorCode=errorCodeSuccess
end if
+ !![
+
+ !!]
return
end function Satellite_Orbit_Equivalent_Circular_Orbit_Radius
- double precision function Equivalent_Circular_Orbit_Solver(radius)
+ double precision function Equivalent_Circular_Orbit_Solver(radius) result(radiusCircular)
!!{
Root function used in finding equivalent circular orbits.
!!}
@@ -132,26 +142,27 @@ Root function used in finding equivalent circular orbits.
type (enumerationStructureErrorCodeType) :: status
! Get potential.
- potential=galacticStructure__%potential(activeNode,radius,status=status)
+ potential=Satellite_Orbit_Potential(radius,radiusVirial__,massVirial__,status)
select case (status%ID)
case (structureErrorCodeSuccess %ID)
- Equivalent_Circular_Orbit_Solver=potential+0.5d0*darkMatterProfileDMO__%circularVelocity(activeNode,radius)**2-orbitalEnergyInternal
+ radiusCircular=+potential+0.5d0*massDistribution__%rotationCurve(radius)**2-orbitalEnergyInternal
case (structureErrorCodeInfinite%ID)
! The gravitational potential is negative infinity at this radius (most likely zero radius). Since all we care about in
! this root-finding function is the sign of the function, return a large negative value.
- Equivalent_Circular_Orbit_Solver=-potentialInfinite
+ radiusCircular=-potentialInfinite
case default
- Equivalent_Circular_Orbit_Solver=0.0d0
+ radiusCircular=+0.0d0
call Error_Report('dark matter potential evaluation failed'//{introspection:location})
end select
return
end function Equivalent_Circular_Orbit_Solver
- subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumType,radius,velocity,galacticStructure_)
+ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumType,radius,velocity,darkMatterHaloScale_)
!!{
Solves for the pericentric radius and velocity of {\normalfont \ttfamily orbit} in {\normalfont \ttfamily nodeHost}.
!!}
use :: Galactic_Structure_Options , only : structureErrorCodeInfinite, structureErrorCodeSuccess ,enumerationStructureErrorCodeType, radiusLarge
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
use :: Kepler_Orbits , only : keplerOrbit
@@ -163,7 +174,7 @@ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extre
type (keplerOrbit ), intent(inout) :: orbit
integer , intent(in ) :: extremumType
double precision , intent( out) :: radius , velocity
- class (galacticStructureClass ), intent(inout), target :: galacticStructure_
+ class (darkMatterHaloScaleClass ), intent(inout) :: darkMatterHaloScale_
class (nodeComponentBasic ), pointer :: basicHost
double precision , parameter :: toleranceAbsolute=0.0d0 , toleranceRelative=1.0d-6
type (rootFinder ), save :: finder
@@ -173,8 +184,10 @@ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extre
type (enumerationStructureErrorCodeType) :: status
double precision :: potential , energyKinetic
+ ! Get the mass distribution.
+ massDistribution__ => nodeHost%massDistribution()
! Convert the orbit to the potential of the current halo in which the satellite finds itself.
- orbitCurrent=Satellite_Orbit_Convert_To_Current_Potential(orbit,nodeHost,galacticStructure_)
+ orbitCurrent=Satellite_Orbit_Convert_To_Current_Potential(orbit,nodeHost,darkMatterHaloScale_)
! Extract the orbital energy and angular momentum.
orbitalEnergyInternal =orbitCurrent%energy ()
orbitalAngularMomentumInternal=orbitCurrent%angularMomentum()
@@ -197,7 +210,8 @@ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extre
& ) then
! Set a pointer to the host node.
activeNode => nodeHost
- galacticStructure__ => galacticStructure_
+ radiusVirial__ = darkMatterHaloScale_%radiusVirial(nodeHost)
+ massVirial__ = basicHost %mass ( )
! Record previous orbital properties.
lastUniqueID =nodeHost %uniqueID()
timePrevious =basicHost%time ()
@@ -252,7 +266,7 @@ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extre
velocity=orbitalAngularMomentumInternal/radius
else
! Orbit is radial - use energy to find velocity.
- potential=galacticStructure_%potential(activeNode,radius,status=status)
+ potential=Satellite_Orbit_Potential(radius,darkMatterHaloScale_%radiusVirial(activeNode),basicHost%mass(),status)
select case (status%ID)
case (structureErrorCodeSuccess %ID)
energyKinetic=max(orbitalEnergyInternal-potential,0.0d0)
@@ -288,6 +302,9 @@ subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extre
velocity= apocenterVelocity
end select
end if
+ !![
+
+ !!]
return
end subroutine Satellite_Orbit_Extremum_Phase_Space_Coordinates
@@ -299,12 +316,12 @@ Root function used in finding orbital extremum radius.
double precision, intent(in ) :: radius
double precision :: potential
- potential=galacticStructure__%potential(activeNode,radius)
+ potential=Satellite_Orbit_Potential(radius,radiusVirial__,massvirial__)
Extremum_Solver=potential+0.5d0*(orbitalAngularMomentumInternal/radius)**2-orbitalEnergyInternal
return
end function Extremum_Solver
- function Satellite_Orbit_Convert_To_Current_Potential(orbit,currentHost,galacticStructure_) result(orbitCurrent)
+ function Satellite_Orbit_Convert_To_Current_Potential(orbit,currentHost,darkMatterHaloScale_) result(orbitCurrent)
!!{
Takes a virial orbit and adjusts the energy to account for the change in the definition of potential between the original
halo in which the orbit was defined and the current halo. Since the potential at the virial radius of halos is always
@@ -315,26 +332,51 @@ function Satellite_Orbit_Convert_To_Current_Potential(orbit,currentHost,galactic
where subscript $0$ refers to the original halo in which the orbit was defined and $\Phi(r)$ is the potential of the
current halo.
!!}
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
use :: Kepler_Orbits , only : keplerOrbit
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
- type (keplerOrbit ) :: orbitCurrent
- type (keplerOrbit ), intent(inout) :: orbit
- type (treeNode ), intent(inout) :: currentHost
- class (galacticStructureClass), intent(inout), target :: galacticStructure_
- double precision :: potentialHost , radiusVirialOriginal, &
- & velocityVirialOriginal
+ type (keplerOrbit ) :: orbitCurrent
+ type (keplerOrbit ), intent(inout) :: orbit
+ type (treeNode ), intent(inout) :: currentHost
+ class (darkMatterHaloScaleClass), intent(inout) :: darkMatterHaloScale_
+ double precision :: potentialHost , radiusVirialOriginal, &
+ & velocityVirialOriginal, radiusVirial
! Compute the properties of the initial orbit, and the current potential.
- radiusVirialOriginal =gravitationalConstantGalacticus*orbit%massHost()/orbit%velocityScale()**2
- velocityVirialOriginal= orbit%velocityScale()
- potentialHost =galacticStructure_%potential(currentHost,radiusVirialOriginal)
+ radiusVirialOriginal =gravitationalConstantGalacticus*orbit%massHost()/orbit%velocityScale()**2
+ velocityVirialOriginal = orbit%velocityScale()
+ radiusVirial =darkMatterHaloScale_%radiusVirial (currentHost)
+ potentialHost =Satellite_Orbit_Potential(radiusVirialOriginal,radiusVirial,orbit%massHost())
! Create a new orbit with an adjusted energy.
orbitCurrent=orbit
call orbitCurrent%energySet(orbit%energy()+velocityVirialOriginal**2+potentialHost)
return
end function Satellite_Orbit_Convert_To_Current_Potential
+ double precision function Satellite_Orbit_Potential(radius,radiusVirial,massVirial,status) result(potential)
+ !!{
+ Evaluate the gravitational potential under the convention that the potential at the virial radius is always
+ $\Phi(r_\mathrm{vir}) = - V_\mathrm{vir}^2$, as is assumed for {\normalfont \ttfamily keplerOrbit} objects.
+ !!}
+ use :: Galactic_Structure_Options , only : enumerationStructureErrorCodeType
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
+ implicit none
+ double precision , intent(in ) :: radius , radiusVirial , &
+ & massVirial
+ type (enumerationStructureErrorCodeType), intent( out), optional :: status
+ type (coordinateCartesian ) :: coordinates, coordinatesVirial
+
+ coordinates =[radius ,0.0d0,0.0d0]
+ coordinatesVirial=[radiusVirial,0.0d0,0.0d0]
+ potential =+massDistribution__%potentialDifference(coordinates,coordinatesVirial,status) &
+ & -gravitationalConstantGalacticus &
+ & * massVirial &
+ & /radiusVirial
+ return
+ end function Satellite_Orbit_Potential
+
subroutine Satellite_Orbit_Reset(node)
!!{
Reset the satellite orbit calculations.
diff --git a/source/satellites.orphans.distributions.trace_dark_matter.F90 b/source/satellites.orphans.distributions.trace_dark_matter.F90
index 028c6e3576..d501a587fc 100644
--- a/source/satellites.orphans.distributions.trace_dark_matter.F90
+++ b/source/satellites.orphans.distributions.trace_dark_matter.F90
@@ -23,7 +23,6 @@
!!}
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -35,7 +34,6 @@
An orphan satellite distribution which assumes an isotropic, random distribution with orphans tracing the radial distribution of dark matter.
!!}
private
- class(galacticStructureClass ), pointer :: galacticStructure_ => null()
class(darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
contains
final :: traceDarkMatterDestructor
@@ -64,32 +62,28 @@ function traceDarkMatterConstructorParameters(parameters) result(self)
type (satelliteOrphanDistributionTraceDarkMatter) :: self
type (inputParameters ), intent(inout) :: parameters
class(darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class(galacticStructureClass ), pointer :: galacticStructure_
! Check and read parameters.
!![
-
!!]
- self=satelliteOrphanDistributionTraceDarkMatter(darkMatterHaloScale_,galacticStructure_)
+ self=satelliteOrphanDistributionTraceDarkMatter(darkMatterHaloScale_)
!![
-
!!]
return
end function traceDarkMatterConstructorParameters
- function traceDarkMatterConstructorInternal(darkMatterHaloScale_,galacticStructure_) result(self)
+ function traceDarkMatterConstructorInternal(darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily traceDarkMatter} orphan satellite distribution class.
!!}
implicit none
type (satelliteOrphanDistributionTraceDarkMatter) :: self
class(darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class(galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
call self%initialize()
@@ -105,7 +99,6 @@ subroutine traceDarkMatterDestructor(self)
!![
-
!!]
return
end subroutine traceDarkMatterDestructor
@@ -128,21 +121,23 @@ double precision function traceDarkMatterInverseCMFRadial(self,node,fraction)
!!{
Return the radial coordinate within which the given {\normalfont \ttfamily fraction} of orphan satellites are found.
!!}
- use :: Galactic_Structure_Options, only : componentTypeAll, massTypeDark
+ use :: Galactic_Structure_Options, only : componentTypeAll , massTypeDark
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (satelliteOrphanDistributionTraceDarkMatter), intent(inout) :: self
type (treeNode ), intent(inout) :: node
double precision , intent(in ) :: fraction
type (treeNode ), pointer :: nodeHost
- !$GLC attributes unused :: self
-
- nodeHost => node%parent
- traceDarkMatterInverseCMFRadial = self%galacticStructure_%radiusEnclosingMass( &
- & nodeHost , &
- & massFractional=fraction , &
- & componentType =componentTypeAll, &
- & massType =massTypeDark &
- & )
+ class (massDistributionClass ), pointer :: massDistribution_
+ double precision :: massEnclosed
+
+ nodeHost => node %parent
+ massDistribution_ => nodeHost %massDistribution (componentType=componentTypeAll ,massType=massTypeDark)
+ massEnclosed = massDistribution_%massEnclosedBySphere(radius =+ self%extent (nodeHost) )
+ traceDarkMatterInverseCMFRadial = massDistribution_%radiusEnclosingMass (mass =+fraction* massEnclosed )
+ !![
+
+ !!]
return
end function traceDarkMatterInverseCMFRadial
diff --git a/source/satellites.tidal_fields.spherical_symmetry.F90 b/source/satellites.tidal_fields.spherical_symmetry.F90
index 0a176ed281..b70df73ba8 100644
--- a/source/satellites.tidal_fields.spherical_symmetry.F90
+++ b/source/satellites.tidal_fields.spherical_symmetry.F90
@@ -21,7 +21,7 @@
Contains a module which implements a model of the tidal field acting on a satellite assuming spherical symmetry in the host.
!!}
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
!![
@@ -43,7 +43,7 @@
Implementation of a satellite tidal friction class which assumes spherical symmetry.
!!}
private
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
double precision :: factorBoost
contains
final :: sphericalSymmetryDestructor
@@ -68,7 +68,7 @@ function sphericalSymmetryConstructorParameters(parameters) result(self)
implicit none
type (satelliteTidalFieldSphericalSymmetry) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
double precision :: factorBoost
!![
@@ -78,26 +78,26 @@ function sphericalSymmetryConstructorParameters(parameters) result(self)
The factor by which to boost satellite tidal fields in the {\normalfont \ttfamily sphericalSymmetry} tidal field class.
-
+
!!]
- self=satelliteTidalFieldSphericalSymmetry(factorBoost,galacticStructure_)
+ self=satelliteTidalFieldSphericalSymmetry(factorBoost,darkMatterHaloScale_)
!![
-
+
!!]
return
end function sphericalSymmetryConstructorParameters
- function sphericalSymmetryConstructorInternal(factorBoost,galacticStructure_) result(self)
+ function sphericalSymmetryConstructorInternal(factorBoost,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily sphericalSymmetry} satellite tidal field class.
!!}
implicit none
type (satelliteTidalFieldSphericalSymmetry) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
double precision , intent(in ) :: factorBoost
!![
-
+
!!]
return
@@ -111,7 +111,7 @@ subroutine sphericalSymmetryDestructor(self)
type(satelliteTidalFieldSphericalSymmetry), intent(inout) :: self
!![
-
+
!!]
return
end subroutine sphericalSymmetryDestructor
@@ -120,9 +120,10 @@ double precision function sphericalSymmetryTidalTensorRadial(self,node)
!!{
Return the radial part of the tidal tensor for satellite halos assuming spherical symmetry of the host.
!!}
- use :: Galactic_Structure_Options , only : coordinateSystemCylindrical
+ use :: Coordinates , only : coordinateCylindrical , assignment(=)
use :: Galacticus_Nodes , only : nodeComponentSatellite , treeNode
use :: Kepler_Orbits , only : keplerOrbit
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Satellite_Orbits , only : Satellite_Orbit_Extremum_Phase_Space_Coordinates, extremumPericenter
@@ -131,9 +132,11 @@ double precision function sphericalSymmetryTidalTensorRadial(self,node)
type (treeNode ), intent(inout) :: node
type (treeNode ), pointer :: nodeHost
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_
type (keplerOrbit ) :: orbit
- double precision :: densityHost , enclosedMassHost, &
- & radiusOrbital, velocityOrbital
+ double precision :: densityHost , enclosedMassHost, &
+ & radiusOrbital , velocityOrbital
+ type (coordinateCylindrical ) :: coordinatesOrbital
! For isolated halos, always return zero tidal field.
if (node%isSatellite()) then
@@ -144,17 +147,15 @@ double precision function sphericalSymmetryTidalTensorRadial(self,node)
! Get the orbit for this node.
orbit = satellite%virialOrbit()
! Get the orbital radius and velocity at pericenter.
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(nodeHost,orbit,extremumPericenter,radiusOrbital,velocityOrbital,self%darkMatterHaloScale_)
! Find the mass and density of the host halo at pericenter.
- densityHost =self%galacticStructure_%density ( &
- & nodeHost , &
- & [radiusOrbital,0.0d0,0.0d0] , &
- & coordinateSystem=coordinateSystemCylindrical &
- & )
- enclosedMassHost=self%galacticStructure_%massEnclosed( &
- & nodeHost , &
- & radiusOrbital &
- & )
+ coordinatesOrbital = [radiusOrbital,0.0d0,0.0d0]
+ massDistribution_ => nodeHost %massDistribution ( )
+ densityHost = massDistribution_%density (coordinatesOrbital)
+ enclosedMassHost = massDistribution_%massEnclosedBySphere( radiusOrbital)
+ !![
+
+ !!]
! Compute the tidal field.
sphericalSymmetryTidalTensorRadial=+ gravitationalConstantGalacticus*enclosedMassHost/ radiusOrbital **3 &
& -4.0d0*Pi*gravitationalConstantGalacticus*densityHost &
diff --git a/source/satellites.tidal_heating.rate.Gnedin1999.F90 b/source/satellites.tidal_heating.rate.Gnedin1999.F90
index b1bafd8743..57891fca53 100644
--- a/source/satellites.tidal_heating.rate.Gnedin1999.F90
+++ b/source/satellites.tidal_heating.rate.Gnedin1999.F90
@@ -25,7 +25,6 @@
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -52,7 +51,6 @@
private
class (cosmologyParametersClass), pointer :: cosmologyParameters_ => null()
class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: epsilon , gamma
contains
final :: gnedin1999Destructor
@@ -79,7 +77,6 @@ function gnedin1999ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: epsilon , gamma
!![
@@ -97,19 +94,17 @@ function gnedin1999ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteTidalHeatingRateGnedin1999(epsilon,gamma,cosmologyParameters_,darkMatterHaloScale_,galacticStructure_)
+ self=satelliteTidalHeatingRateGnedin1999(epsilon,gamma,cosmologyParameters_,darkMatterHaloScale_)
!![
-
!!]
return
end function gnedin1999ConstructorParameters
- function gnedin1999ConstructorInternal(epsilon,gamma,cosmologyParameters_,darkMatterHaloScale_,galacticStructure_) result(self)
+ function gnedin1999ConstructorInternal(epsilon,gamma,cosmologyParameters_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily gnedin1999} satellite tidal heating rate class.
!!}
@@ -117,10 +112,9 @@ function gnedin1999ConstructorInternal(epsilon,gamma,cosmologyParameters_,darkMa
type (satelliteTidalHeatingRateGnedin1999) :: self
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in) :: epsilon , gamma
!![
-
+
!!]
return
@@ -136,7 +130,6 @@ subroutine gnedin1999Destructor(self)
!![
-
!!]
return
end subroutine gnedin1999Destructor
@@ -151,15 +144,19 @@ double precision function gnedin1999HeatingRate(self,node)
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions , only : massDistributionClass
use :: Tensors , only : assignment(=) , max , operator(*) , tensorRank2Dimension3Symmetric
use :: Vectors , only : Vector_Magnitude
+ use :: Coordinates , only : coordinateCartesian , assignment(=)
implicit none
class (satelliteTidalHeatingRateGnedin1999), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentSatellite ), pointer :: satellite
type (treeNode ), pointer :: nodeHost
class (nodeComponentBasic ), pointer :: basic
- double precision , dimension(3) :: position , velocity
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionHost_
+ double precision , dimension(3) :: velocity
+ type (coordinateCartesian ) :: position
double precision :: massSatellite , velocityCircularSatellite, &
& radius , speed , &
& timescaleShock , heatingRateNormalized , &
@@ -174,7 +171,7 @@ double precision function gnedin1999HeatingRate(self,node)
position = satellite%position ( )
velocity = satellite%velocity ( )
tidalTensorPathIntegrated = satellite%tidalTensorPathIntegrated( )
- radius = Vector_Magnitude (position)
+ radius = position %rSpherical ( )
speed = Vector_Magnitude (velocity)
! Find the universal dark matter fraction.
fractionDarkMatter = +( &
@@ -183,32 +180,25 @@ double precision function gnedin1999HeatingRate(self,node)
& ) &
& / self%cosmologyParameters_%OmegaMatter()
! Find the gravitational tidal tensor.
- tidalTensor = self%galacticStructure_%tidalTensor(nodeHost,position)
+ massDistributionHost_ => nodeHost%massDistribution()
+ tidalTensor = massDistributionHost_%tidalTensor(position)
+ !![
+
+ !!]
! Find the orbital frequency at the half mass radius of the satellite.
+ massDistribution_ => node%massDistribution(componentTypeAll,massTypeDark)
basic => node%basic()
- massHalfSatellite = +0.50d0 &
- & *min( &
- & + fractionDarkMatter &
- & * massSatellite , &
- & +self%galacticStructure_%massEnclosed ( &
- & node , &
- & radius =self%darkMatterHaloScale_%radiusVirial(node), &
- & componentType=componentTypeAll , &
- & massType =massTypeDark &
- & ) &
+ massHalfSatellite = +0.50d0 &
+ & *min( &
+ & + fractionDarkMatter &
+ & * massSatellite , &
+ & +massDistribution_%massEnclosedBySphere(radius=self%darkMatterHaloScale_%radiusVirial (node)) &
& )
- radiusHalfMassSatellite = self%galacticStructure_%radiusEnclosingMass( &
- & node , &
- & mass =massHalfSatellite, &
- & componentType =componentTypeAll , &
- & massType =massTypeDark &
- & )
- velocityCircularSatellite= self%galacticStructure_%velocityRotation ( &
- & node , &
- & radiusHalfMassSatellite , &
- & componentType =componentTypeAll , &
- & massType =massTypeDark &
- & )
+ radiusHalfMassSatellite = massDistribution_%radiusEnclosingMass (mass = massHalfSatellite )
+ velocityCircularSatellite= massDistribution_%rotationCurve (radius= radiusHalfMassSatellite )
+ !![
+
+ !!]
if (radiusHalfMassSatellite > 0.0d0) then
! Compute the orbital frequency.
orbitalFrequencySatellite = +velocityCircularSatellite &
diff --git a/source/satellites.tidal_stripping.radius.King1962.F90 b/source/satellites.tidal_stripping.radius.King1962.F90
index e55beebb4e..1c312be46a 100644
--- a/source/satellites.tidal_stripping.radius.King1962.F90
+++ b/source/satellites.tidal_stripping.radius.King1962.F90
@@ -25,9 +25,7 @@
use :: Cosmology_Parameters , only : cosmologyParametersClass
use :: Dark_Matter_Halo_Scales, only : darkMatterHaloScaleClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Kind_Numbers , only : kind_int8
- use :: Root_Finder , only : rootFinder
!![
@@ -48,24 +46,14 @@
Implementation of a satellite tidal radius class which follows the method of \cite{king_structure_1962}.
!!}
private
- class (cosmologyParametersClass), pointer :: cosmologyParameters_ => null()
- class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- double precision :: radiusTidalPrevious , expandMultiplier , &
- & fractionDarkMatter , efficiencyCentrifugal
- integer (kind_int8 ) :: lastUniqueID
- type (rootFinder ) :: finder
+ class (cosmologyParametersClass), pointer :: cosmologyParameters_ => null()
+ class (darkMatterHaloScaleClass), pointer :: darkMatterHaloScale_ => null()
+ double precision :: efficiencyCentrifugal , expandMultiplier, &
+ & fractionDarkMatter
logical :: applyPreInfall
contains
- !![
-
-
-
- !!]
- final :: king1962Destructor
- procedure :: autoHook => king1962AutoHook
- procedure :: calculationReset => king1962CalculationReset
- procedure :: radius => king1962Radius
+ final :: king1962Destructor
+ procedure :: radius => king1962Radius
end type satelliteTidalStrippingRadiusKing1962
interface satelliteTidalStrippingRadiusKing1962
@@ -76,12 +64,6 @@
module procedure king1962ConstructorInternal
end interface satelliteTidalStrippingRadiusKing1962
- ! Module-scope objects used for root finding.
- class (satelliteTidalStrippingRadiusKing1962), pointer :: self_
- type (treeNode ), pointer :: node_
- double precision :: tidalPull
- !$omp threadprivate(node_,tidalPull,self_)
-
contains
function king1962ConstructorParameters(parameters) result(self)
@@ -94,7 +76,6 @@ function king1962ConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: efficiencyCentrifugal
logical :: applyPreInfall
@@ -113,19 +94,17 @@ function king1962ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteTidalStrippingRadiusKing1962(efficiencyCentrifugal,applyPreInfall,cosmologyParameters_,darkMatterHaloScale_,galacticStructure_)
+ self=satelliteTidalStrippingRadiusKing1962(efficiencyCentrifugal,applyPreInfall,cosmologyParameters_,darkMatterHaloScale_)
!![
-
!!]
return
end function king1962ConstructorParameters
- function king1962ConstructorInternal(efficiencyCentrifugal,applyPreInfall,cosmologyParameters_,darkMatterHaloScale_,galacticStructure_) result(self)
+ function king1962ConstructorInternal(efficiencyCentrifugal,applyPreInfall,cosmologyParameters_,darkMatterHaloScale_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily king1962} satellite tidal stripping class.
!!}
@@ -133,12 +112,11 @@ function king1962ConstructorInternal(efficiencyCentrifugal,applyPreInfall,cosmol
type (satelliteTidalStrippingRadiusKing1962) :: self
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: efficiencyCentrifugal
logical , intent(in ) :: applyPreInfall
double precision , parameter :: toleranceAbsolute =0.0d0, toleranceRelative=1.0d-3
!![
-
+
!!]
self%fractionDarkMatter=+( &
@@ -146,41 +124,20 @@ function king1962ConstructorInternal(efficiencyCentrifugal,applyPreInfall,cosmol
& -self%cosmologyParameters_%OmegaBaryon() &
& ) &
& / self%cosmologyParameters_%OmegaMatter()
- self%expandMultiplier=2.0d0
- self%finder =rootFinder( &
- & rootFunction =king1962TidalRadiusSolver, &
- & toleranceAbsolute=toleranceAbsolute , &
- & toleranceRelative=toleranceRelative &
- & )
return
end function king1962ConstructorInternal
- subroutine king1962AutoHook(self)
- !!{
- Attach to the calculation reset event.
- !!}
- use :: Events_Hooks, only : calculationResetEvent, openMPThreadBindingAllLevels
- implicit none
- class(satelliteTidalStrippingRadiusKing1962), intent(inout) :: self
-
- call calculationResetEvent%attach(self,king1962CalculationReset,openMPThreadBindingAllLevels,label='satelliteTidalStrippingRadiusKing1962')
- return
- end subroutine king1962AutoHook
-
subroutine king1962Destructor(self)
!!{
Destructor for the {\normalfont \ttfamily king1962} satellite tidal stripping class.
!!}
- use :: Events_Hooks, only : calculationResetEvent
implicit none
type(satelliteTidalStrippingRadiusKing1962), intent(inout) :: self
!![
-
!!]
- if (calculationResetEvent%isAttached(self,king1962CalculationReset)) call calculationResetEvent%detach(self,king1962CalculationReset)
return
end subroutine king1962Destructor
@@ -211,14 +168,14 @@ double precision function king1962Radius(self,node)
largest positive eigenvalue, not the largest absolute eigenvalue as we're interested in stretching tidal fields, not
compressive ones.)
!!}
- use :: Error , only : Error_Report , errorStatusSuccess
- use :: Galactic_Structure_Options , only : coordinateSystemCartesian, massTypeDark
+ use :: Coordinates , only : assignment(=) , coordinateCartesian
+ use :: Galactic_Structure_Options , only : massTypeDark
use :: Galacticus_Nodes , only : nodeComponentSatellite , nodeComponentBasic , treeNode
use :: Linear_Algebra , only : assignment(=) , matrix , vector
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical, only : gigaYear , gravitationalConstantGalacticus, megaParsec
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : kilo
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative , rangeExpandSignExpectPositive
use :: Tensors , only : assignment(=) , tensorRank2Dimension3Symmetric
use :: Vectors , only : Vector_Magnitude , Vector_Product
implicit none
@@ -227,18 +184,21 @@ double precision function king1962Radius(self,node)
type (treeNode ), pointer :: nodeHost
class (nodeComponentBasic ), pointer :: basic , basicHost
class (nodeComponentSatellite ), pointer :: satellite
- double precision , dimension(3 ) :: position , velocity , &
+ class (massDistributionClass ), pointer :: massDistribution_ , massDistributionDark
+ double precision , dimension(3 ) :: position , velocity , &
& tidalTensorEigenValueComponents
double precision , dimension(3,3) :: tidalTensorComponents
double precision , parameter :: radiusZero =0.0d+0
double precision , parameter :: radiusTidalTinyFraction =1.0d-6
- integer :: status
- double precision :: massSatellite , frequencyAngular , &
- & radius , tidalFieldRadial , &
- & radiusLimitDownward
+ double precision :: massSatellite , frequencyAngular , &
+ & radius , tidalFieldRadial , &
+ & radiusGuess , densityTidal , &
+ & tidalPull , tidalTensorEigenValueMaximum, &
+ & radiusDownwardLimit
type (tensorRank2Dimension3Symmetric ) :: tidalTensor
type (matrix ) :: tidalTensorMatrix , tidalTensorEigenVectors
type (vector ) :: tidalTensorEigenValues
+ type (coordinateCartesian ) :: coordinates
! Find the host node.
if (node%isOnMainBranch().or.(.not.self%applyPreInfall.and..not.node%isSatellite())) then
@@ -285,115 +245,90 @@ double precision function king1962Radius(self,node)
!
! -2GM(r)r⁻³ - 4πGρ(r)
if (associated(nodeHost)) then
- tidalTensor = self%galacticStructure_%tidalTensor(nodeHost,position)
- tidalTensorComponents = tidalTensor
- tidalTensorMatrix = tidalTensorComponents
- call tidalTensorMatrix%eigenSystem(tidalTensorEigenVectors,tidalTensorEigenValues)
- tidalTensorEigenValueComponents = tidalTensorEigenValues
- tidalFieldRadial =-maxval(tidalTensorEigenValueComponents) &
- & *( &
- & +kilo &
- & *gigaYear &
- & /megaParsec &
- & )**2
+ massDistribution_ => nodeHost%massDistribution()
+ if (massDistribution_%isSphericallySymmetric()) then
+ ! For spherically-symmetric mass distributions we can avoid the expense of solving for the eigenvalues. We simply
+ ! evaluate the tidal tensor at [r,0,0] (since the distribution is spherically-symmetric we can evaluate at any
+ ! position on the sphere), and take the 0,0 element of the tensor which will be the largest (and only) positive
+ ! eigenvector.
+ coordinates =[radius,0.0d0,0.0d0]
+ tidalTensor =massDistribution_%tidalTensor(coordinates)
+ tidalTensorEigenValueMaximum =tidalTensor%element(0,0)
+ else
+ coordinates =position
+ tidalTensor =massDistribution_%tidalTensor(coordinates)
+ tidalTensorComponents =tidalTensor
+ tidalTensorMatrix =tidalTensorComponents
+ call tidalTensorMatrix%eigenSystem(tidalTensorEigenVectors,tidalTensorEigenValues)
+ tidalTensorEigenValueComponents=tidalTensorEigenValues
+ tidalTensorEigenValueMaximum =maxval(tidalTensorEigenValueComponents)
+ end if
+ tidalFieldRadial=-tidalTensorEigenValueMaximum &
+ & *( &
+ & +kilo &
+ & *gigaYear &
+ & /megaParsec &
+ & )**2
+ !![
+
+ !!]
else
tidalFieldRadial =+0.0d0
end if
! If the tidal force is stretching (not compressing), compute the tidal radius.
- tidalPull=self%efficiencyCentrifugal*frequencyAngular**2-tidalFieldRadial
- if ( &
- & tidalPull > 0.0d0 &
- & .and. &
- & massSatellite > 0.0d0 &
- & .and. &
- & self%galacticStructure_%massEnclosed(node,radiusZero) >= 0.0d0 &
+ massDistribution_ => node%massDistribution()
+ tidalPull = self%efficiencyCentrifugal*frequencyAngular**2-tidalFieldRadial
+ if ( &
+ & tidalPull > 0.0d0 &
+ & .and. &
+ & massSatellite > 0.0d0 &
+ & .and. &
+ & massDistribution_%massEnclosedBySphere(radiusZero) >= 0.0d0 &
& ) then
- ! Check if node differs from previous one for which we performed calculations.
- if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
- ! Initial estimate of the tidal radius.
- if (self%radiusTidalPrevious <= 0.0d0) then
- self%radiusTidalPrevious=+sqrt( &
- & +gravitationalConstantGalacticus &
- & *massSatellite &
- & /self%darkMatterHaloScale_%radiusVirial(node) &
- & /tidalPull &
- & *(kilo*gigaYear/megaParsec)**2 &
- & )
- self%expandMultiplier =+2.0d0
- end if
- ! Find the tidal radius in the dark matter profile.
- radiusLimitDownward=+radiusTidalTinyFraction &
- & *self%radiusTidalPrevious
- call self%finder%rangeExpand( &
- & rangeExpandUpward =+1.0d0*self%expandMultiplier , &
- & rangeExpandDownward =+1.0d0/self%expandMultiplier , &
- & rangeExpandDownwardSignExpect= rangeExpandSignExpectNegative, &
- & rangeExpandUpwardSignExpect = rangeExpandSignExpectPositive, &
- & rangeDownwardLimit = radiusLimitDownward , &
- & rangeExpandType = rangeExpandMultiplicative &
- & )
- self_ => self
- node_ => node
- ! Find the tidal radius, using the previous result as an initial guess.
- self%radiusTidalPrevious=self%finder%find(rootGuess=self%radiusTidalPrevious,status=status)
- if (status == errorStatusSuccess) then
- self%expandMultiplier =1.2d0
- else if (king1962TidalRadiusSolver(radiusLimitDownward) > 0.0d0) then
- ! Complete stripping.
- self%radiusTidalPrevious=0.0d0
+ ! Find the tidal density.
+ densityTidal=+tidalPull &
+ & /(kilo*gigaYear/megaParsec) **2 &
+ & /gravitationalConstantGalacticus &
+ & *3.0d0 &
+ & /4.0d0 &
+ & /Pi
+ ! Solve for the radius enclosing this density.
+ radiusGuess = +sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *massSatellite &
+ & /self%darkMatterHaloScale_%radiusVirial(node) &
+ & /tidalPull &
+ & *(kilo*gigaYear/megaParsec)**2 &
+ & )
+ radiusDownwardLimit=radiusTidalTinyFraction*radiusGuess
+ if ( &
+ & + 3.0 &
+ & /4.0d0 &
+ & /Pi &
+ & *massDistribution_%massEnclosedBySphere(radiusDownwardLimit) &
+ & / radiusDownwardLimit **3 &
+ & > &
+ & densityTidal &
+ & ) then
+ king1962Radius = massDistribution_%radiusEnclosingDensity(densityTidal,radiusGuess)
else
- ! Find the tidal radius, using the previous result as an initial guess.
- call Error_Report('unable to find tidal radius'//{introspection:location})
+ king1962Radius = 0.0d0
end if
- king1962Radius=self%radiusTidalPrevious
else
! If the bound mass of the satellite exceeds the original mass (which can happen during failed ODE steps), simply return
! the virial radius. Otherwise, solve for the radius enclosing the current bound mass.
- if (massSatellite > self%galacticStructure_%massEnclosed(node,radius=self%darkMatterHaloScale_%radiusVirial(node),massType=massTypeDark)) then
- king1962Radius=self%darkMatterHaloScale_%radiusVirial (node )
+ massDistributionDark => node%massDistribution(massType=massTypeDark)
+ if (massSatellite > massDistributionDark%massEnclosedBySphere(self%darkMatterHaloScale_%radiusVirial(node))) then
+ king1962Radius=self %darkMatterHaloScale_%radiusVirial (node )
else
- king1962Radius=self%galacticStructure_ %radiusEnclosingMass(node,massSatellite*self%fractionDarkMatter,massType=massTypeDark)
+ king1962Radius=massDistributionDark %radiusEnclosingMass(massSatellite*self%fractionDarkMatter)
end if
+ !![
+
+ !!]
end if
+ !![
+
+ !!]
return
end function king1962Radius
-
- double precision function king1962TidalRadiusSolver(radius)
- !!{
- Root function used to find the tidal radius within a subhalo.
- !!}
- use :: Numerical_Constants_Astronomical, only : gigaYear, gravitationalConstantGalacticus, megaParsec
- use :: Numerical_Constants_Prefixes , only : kilo
- implicit none
- double precision, intent(in ) :: radius
- double precision :: enclosedMass
-
- ! Get the satellite component.
- enclosedMass =+self_%galacticStructure_%massEnclosed(node_,radius)
- king1962TidalRadiusSolver=+tidalPull &
- & -gravitationalConstantGalacticus &
- & *enclosedMass &
- & /radius **3 &
- & *( &
- & +kilo &
- & *gigaYear &
- & /megaParsec &
- & ) **2
- return
- end function king1962TidalRadiusSolver
-
- subroutine king1962CalculationReset(self,node,uniqueID)
- !!{
- Reset the stored tidal radii.
- !!}
- use :: Kind_Numbers, only : kind_int8
- implicit none
- class (satelliteTidalStrippingRadiusKing1962), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- integer(kind_int8 ), intent(in ) :: uniqueID
- !$GLC attributes unused :: node
-
- self%radiusTidalPrevious=-1.0d0
- self%lastUniqueID =uniqueID
- return
- end subroutine king1962CalculationReset
diff --git a/source/satellites.tidal_stripping.rate.Zentner2005.F90 b/source/satellites.tidal_stripping.rate.Zentner2005.F90
index adc2eeb404..f12a46e880 100644
--- a/source/satellites.tidal_stripping.rate.Zentner2005.F90
+++ b/source/satellites.tidal_stripping.rate.Zentner2005.F90
@@ -24,7 +24,6 @@
!!}
use :: Satellite_Tidal_Stripping_Radii, only : satelliteTidalStrippingRadiusClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -61,7 +60,6 @@
!!}
private
class (satelliteTidalStrippingRadiusClass), pointer :: satelliteTidalStrippingRadius_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: efficiency
logical :: useDynamicalTimeScale
contains
@@ -88,7 +86,6 @@ function zentner2005ConstructorParameters(parameters) result(self)
type (satelliteTidalStrippingZentner2005) :: self
type (inputParameters ), intent(inout) :: parameters
class (satelliteTidalStrippingRadiusClass), pointer :: satelliteTidalStrippingRadius_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: efficiency
logical :: useDynamicalTimeScale
@@ -106,29 +103,26 @@ function zentner2005ConstructorParameters(parameters) result(self)
-
!!]
- self=satelliteTidalStrippingZentner2005(efficiency,useDynamicalTimeScale,satelliteTidalStrippingRadius_,galacticStructure_)
+ self=satelliteTidalStrippingZentner2005(efficiency,useDynamicalTimeScale,satelliteTidalStrippingRadius_)
!![
-
!!]
return
end function zentner2005ConstructorParameters
- function zentner2005ConstructorInternal(efficiency,useDynamicalTimeScale,satelliteTidalStrippingRadius_,galacticStructure_) result(self)
+ function zentner2005ConstructorInternal(efficiency,useDynamicalTimeScale,satelliteTidalStrippingRadius_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily zentner2005} satellite tidal stripping class.
!!}
implicit none
type (satelliteTidalStrippingZentner2005) :: self
class (satelliteTidalStrippingRadiusClass), intent(in ), target :: satelliteTidalStrippingRadius_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: efficiency
logical , intent(in ) :: useDynamicalTimeScale
!![
-
+
!!]
return
@@ -143,7 +137,6 @@ subroutine zentner2005Destructor(self)
!![
-
!!]
return
end subroutine zentner2005Destructor
@@ -153,6 +146,7 @@ double precision function zentner2005MassLossRate(self,node)
Return a mass loss rate for satellites due to tidal stripping using the formulation of \cite{zentner_physics_2005}.
!!}
use :: Galacticus_Nodes , only : nodeComponentSatellite, treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec , gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Prefixes , only : kilo
@@ -161,6 +155,7 @@ double precision function zentner2005MassLossRate(self,node)
class (satelliteTidalStrippingZentner2005), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentSatellite ), pointer :: satellite
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , dimension(3 ) :: position , velocity
double precision :: massSatellite , frequencyAngular, &
& periodOrbital , radius , &
@@ -187,14 +182,18 @@ double precision function zentner2005MassLossRate(self,node)
& /megaParsec
! Find the orbital period. We use the larger of the angular and radial frequencies to avoid numerical problems for purely
! radial or purely circular orbits.
+ massDistribution_ => node%massDistribution()
periodOrbital = +2.0d0 &
& *Pi &
& /max( &
& frequencyAngular, &
& frequencyRadial &
& )
- radiusTidal = self%satelliteTidalStrippingRadius_%radius (node )
- massEnclosedTidalRadius=max(0.0d0,self%galacticStructure_ %massEnclosed(node,radiusTidal))
+ radiusTidal = self %satelliteTidalStrippingRadius_%radius (node )
+ massEnclosedTidalRadius=max(0.0d0,massDistribution_ %massEnclosedBySphere(radiusTidal))
+ !![
+
+ !!]
! Check whether to use the dynamical time scale or the orbital time scale for mass loss rate.
if (self%useDynamicalTimeScale .and. massEnclosedTidalRadius > 0.0d0) then
timeScaleMassLoss=+2.0d0 &
diff --git a/source/star_formation.active_masses.surface_density_threshold.F90 b/source/star_formation.active_masses.surface_density_threshold.F90
index a243f12203..6cc029bc56 100644
--- a/source/star_formation.active_masses.surface_density_threshold.F90
+++ b/source/star_formation.active_masses.surface_density_threshold.F90
@@ -21,7 +21,6 @@
Implementation of an active mass for star formation class in which the mass of the ISM above a surface density threshold is active.
!!}
use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Math_Exponentiation , only : fastExponentiator
!![
@@ -35,7 +34,6 @@
!!}
private
class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: surfaceDensityThreshold , surfaceDensityNormalization, &
& exponentVelocity
type (fastExponentiator ) :: velocityExponentiator
@@ -64,7 +62,6 @@ function surfaceDensityThresholdConstructorParameters(parameters) result(self)
type (starFormationActiveMassSurfaceDensityThreshold) :: self
type (inputParameters ), intent(inout) :: parameters
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: surfaceDensityThreshold, exponentVelocity
!![
@@ -81,18 +78,16 @@ function surfaceDensityThresholdConstructorParameters(parameters) result(self)
-
!!]
- self=starFormationActiveMassSurfaceDensityThreshold(surfaceDensityThreshold,exponentVelocity,darkMatterProfileDMO_,galacticStructure_)
+ self=starFormationActiveMassSurfaceDensityThreshold(surfaceDensityThreshold,exponentVelocity,darkMatterProfileDMO_)
!![
-
!!]
return
end function surfaceDensityThresholdConstructorParameters
- function surfaceDensityThresholdConstructorInternal(surfaceDensityThreshold,exponentVelocity,darkMatterProfileDMO_,galacticStructure_) result(self)
+ function surfaceDensityThresholdConstructorInternal(surfaceDensityThreshold,exponentVelocity,darkMatterProfileDMO_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily surfaceDensityThreshold} active mass for star formation class.
!!}
@@ -100,10 +95,9 @@ function surfaceDensityThresholdConstructorInternal(surfaceDensityThreshold,expo
type (starFormationActiveMassSurfaceDensityThreshold) :: self
double precision , intent(in ) :: surfaceDensityThreshold , exponentVelocity
class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , parameter :: velocityNormalization =200.0d0
!![
-
+
!!]
! Initialize exponentiators.
@@ -122,7 +116,6 @@ subroutine surfaceDensityThresholdDestructor(self)
!![
-
!!]
return
end subroutine surfaceDensityThresholdDestructor
@@ -132,30 +125,42 @@ double precision function surfaceDensityThresholdMassActive(self,component)
Returns the mass (in $\mathrm{M}_\odot$) of gas actively undergoing star formation in the given {\normalfont \ttfamily
component} as the mass of gas in the ISM above a given surface density threshold
!!}
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
use :: Error , only : Error_Report
use :: Galacticus_Nodes , only : nodeComponentDisk
- use :: Galactic_Structure_Options, only : componentTypeDisk, coordinateSystemCartesian, coordinateSystemCylindrical, massTypeGaseous, &
- & weightByMass , weightIndexNull
+ use :: Galactic_Structure_Options, only : componentTypeDisk , massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (starFormationActiveMassSurfaceDensityThreshold), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: surfaceDensityThreshold, radiusBounding, &
& densitySurfaceCentral
+ type (coordinateCylindrical ) :: coordinates
select type (component)
class is (nodeComponentDisk)
! Compute the surface density threshold for this node.
- surfaceDensityThreshold=+self%surfaceDensityNormalization &
- & *self%velocityExponentiator %exponentiate(self%darkMatterProfileDMO_ %circularVelocityMaximum(component%hostNode))
+ massDistribution_ => self%darkMatterProfileDMO_ % get (component %hostNode )
+ surfaceDensityThreshold = +self%surfaceDensityNormalization &
+ & *self%velocityExponentiator %exponentiate(massDistribution_%velocityRotationCurveMaximum())
+ !![
+
+ !!]
! We assume a monotonically decreasing surface density. So, if the central density is below threshold then the active mass
! is zero.
- densitySurfaceCentral =self%galacticStructure_%surfaceDensity (component%hostNode,[0.0d0,0.0d0,0.0d0] ,coordinateSystemCylindrical,componentTypeDisk,massTypeGaseous,weightByMass,weightIndexNull)
+ coordinates=[0.0d0,0.0d0,0.0d0]
+ massDistribution_ => component%hostNode%massDistribution (componentType=componentTypeDisk ,massType=massTypeGaseous)
+ densitySurfaceCentral = massDistribution_ %surfaceDensity ( coordinates )
if (densitySurfaceCentral < surfaceDensityThreshold) then
- surfaceDensityThresholdMassActive=0.0d0
+ surfaceDensityThresholdMassActive =0.0d0
else
- radiusBounding =self%galacticStructure_%radiusEnclosingSurfaceDensity(component%hostNode,surfaceDensityThreshold ,componentTypeDisk,massTypeGaseous,weightByMass,weightIndexNull)
- surfaceDensityThresholdMassActive=self%galacticStructure_%massEnclosed (component%hostNode,radiusBounding ,componentTypeDisk,massTypeGaseous,weightByMass,weightIndexNull)
+ radiusBounding = massDistribution_%radiusEnclosingSurfaceDensity( surfaceDensityThreshold )
+ surfaceDensityThresholdMassActive = massDistribution_%massEnclosedBySphere ( radiusBounding )
end if
+ !![
+
+ !!]
class default
surfaceDensityThresholdMassActive=0.0d0
call Error_Report('unsupported class'//{introspection:location})
diff --git a/source/star_formation.rate_surface_density.disks.Blitz2006.F90 b/source/star_formation.rate_surface_density.disks.Blitz2006.F90
index 4eb8268568..9f94dabae6 100644
--- a/source/star_formation.rate_surface_density.disks.Blitz2006.F90
+++ b/source/star_formation.rate_surface_density.disks.Blitz2006.F90
@@ -22,7 +22,6 @@
!!}
use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure , only : galacticStructureClass
use :: Root_Finder , only : rootFinder
use :: Math_Exponentiation, only : fastExponentiator
@@ -61,10 +60,9 @@
Implementation of the \cite{blitz_role_2006} star formation rate surface density law for galactic disks.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
integer (kind_int8 ) :: lastUniqueID
logical :: factorsComputed , assumeMonotonicSurfaceDensity , &
- & assumeExponentialDisk , useTabulation
+ & isExponentialDisk , useTabulation
double precision :: heightToRadialScaleDisk , pressureCharacteristic , &
& pressureExponent , starFormationFrequencyNormalization , &
& surfaceDensityCritical , surfaceDensityExponent , &
@@ -92,7 +90,6 @@
-
@@ -105,7 +102,6 @@
procedure :: unchanged => blitz2006Unchanged
procedure :: intervals => blitz2006Intervals
procedure :: pressureRatio => blitz2006PressureRatio
- procedure :: pressureRatioExponential => blitz2006PressureRatioExponential
procedure :: integralFullyMolecular => blitz2006IntegralFullyMolecular
procedure :: integralPartiallyMolecular => blitz2006IntegralPartiallyMolecular
end type starFormationRateSurfaceDensityDisksBlitz2006
@@ -132,13 +128,11 @@ function blitz2006ConstructorParameters(parameters) result(self)
implicit none
type (starFormationRateSurfaceDensityDisksBlitz2006) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: velocityDispersionDiskGas , heightToRadialScaleDisk, &
& surfaceDensityCritical , surfaceDensityExponent , &
& starFormationFrequencyNormalization, pressureCharacteristic , &
& pressureExponent
- logical :: assumeMonotonicSurfaceDensity , assumeExponentialDisk , &
- & useTabulation
+ logical :: assumeMonotonicSurfaceDensity , useTabulation
!![
@@ -196,29 +190,21 @@ function blitz2006ConstructorParameters(parameters) result(self)
If true, assume that the surface density in disks is always monotonically decreasing.
-
- assumeExponentialDisk
- .false.
- If true, assume that the surface density in disks follows an exponential profile, $\exp(-r/r_\mathrm{d})$.
-
-
useTabulation
.false.
If true, then use tabulated solutions to the integrated star formation rate.
-
!!]
- self=starFormationRateSurfaceDensityDisksBlitz2006(velocityDispersionDiskGas,heightToRadialScaleDisk,surfaceDensityCritical,surfaceDensityExponent,starFormationFrequencyNormalization,pressureCharacteristic,pressureExponent,assumeMonotonicSurfaceDensity,assumeExponentialDisk,useTabulation,galacticStructure_)
+ self=starFormationRateSurfaceDensityDisksBlitz2006(velocityDispersionDiskGas,heightToRadialScaleDisk,surfaceDensityCritical,surfaceDensityExponent,starFormationFrequencyNormalization,pressureCharacteristic,pressureExponent,assumeMonotonicSurfaceDensity,useTabulation)
!![
-
!!]
return
end function blitz2006ConstructorParameters
- function blitz2006ConstructorInternal(velocityDispersionDiskGas,heightToRadialScaleDisk,surfaceDensityCritical,surfaceDensityExponent,starFormationFrequencyNormalization,pressureCharacteristic,pressureExponent,assumeMonotonicSurfaceDensity,assumeExponentialDisk,useTabulation,galacticStructure_) result(self)
+ function blitz2006ConstructorInternal(velocityDispersionDiskGas,heightToRadialScaleDisk,surfaceDensityCritical,surfaceDensityExponent,starFormationFrequencyNormalization,pressureCharacteristic,pressureExponent,assumeMonotonicSurfaceDensity,useTabulation) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily blitz2006} star formation surface density rate from disks class.
!!}
@@ -230,18 +216,16 @@ function blitz2006ConstructorInternal(velocityDispersionDiskGas,heightToRadialSc
use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
use :: Hashes_Cryptographic , only : Hash_MD5
implicit none
- type (starFormationRateSurfaceDensityDisksBlitz2006) :: self
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
- double precision , intent(in ) :: velocityDispersionDiskGas , heightToRadialScaleDisk, &
- & surfaceDensityCritical , surfaceDensityExponent , &
- & starFormationFrequencyNormalization, pressureCharacteristic , &
- & pressureExponent
- logical , intent(in ) :: assumeMonotonicSurfaceDensity , assumeExponentialDisk , &
- & useTabulation
- type (varying_string ) :: descriptorString
- character (len=17 ) :: parameterLabel
+ type (starFormationRateSurfaceDensityDisksBlitz2006) :: self
+ double precision , intent(in ) :: velocityDispersionDiskGas , heightToRadialScaleDisk, &
+ & surfaceDensityCritical , surfaceDensityExponent , &
+ & starFormationFrequencyNormalization, pressureCharacteristic , &
+ & pressureExponent
+ logical , intent(in ) :: assumeMonotonicSurfaceDensity , useTabulation
+ type (varying_string ) :: descriptorString
+ character (len=17 ) :: parameterLabel
!![
-
+
!!]
self%lastUniqueID =-1_kind_int8
@@ -255,29 +239,16 @@ function blitz2006ConstructorInternal(velocityDispersionDiskGas,heightToRadialSc
! Build fast exponentiator.
self%pressureRatioExponentiator =fastExponentiator(0.0d0,1.0d0,pressureExponent,1000.0d0,.false.)
! Build root finder.
- if (self%assumeExponentialDisk) then
- self%finder=rootFinder( &
- & rootFunction =blitz2006CriticalDensityExponentialRoot, &
- & toleranceAbsolute =0.0d+0 , &
- & toleranceRelative =1.0d-4 , &
- & rangeExpandUpward =2.0d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive , &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- else
- self%finder=rootFinder( &
- & rootFunction =blitz2006CriticalDensityRoot , &
- & toleranceAbsolute =0.0d+0 , &
- & toleranceRelative =1.0d-4 , &
- & rangeExpandUpward =2.0d+0 , &
- & rangeExpandDownward =0.5d+0 , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative , &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive , &
- & rangeExpandType =rangeExpandMultiplicative &
- & )
- end if
+ self%finder=rootFinder( &
+ & rootFunction =blitz2006CriticalDensityRoot , &
+ & toleranceAbsolute =0.0d+0 , &
+ & toleranceRelative =1.0d-4 , &
+ & rangeExpandUpward =2.0d+0 , &
+ & rangeExpandDownward =0.5d+0 , &
+ & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
+ & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
+ & rangeExpandType =rangeExpandMultiplicative &
+ & )
! Initialize memoized values.
self%massGasPrevious =-huge(0.0d0)
self%massStellarPrevious =-huge(0.0d0)
@@ -328,9 +299,6 @@ subroutine blitz2006Destructor(self)
type(starFormationRateSurfaceDensityDisksBlitz2006), intent(inout) :: self
if (calculationResetEvent%isAttached(self,blitz2006CalculationReset)) call calculationResetEvent%detach(self,blitz2006CalculationReset)
- !![
-
- !!]
return
end subroutine blitz2006Destructor
@@ -427,12 +395,16 @@ subroutine blitz2006ComputeFactors(self,node)
!!}
use :: Abundances_Structure , only : abundances
use :: Galacticus_Nodes , only : nodeComponentDisk
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeGaseous , massTypeStellar
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionCylindricalScaler, massDistributionExponentialDisk
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
class(starFormationRateSurfaceDensityDisksBlitz2006), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class(nodeComponentDisk ), pointer :: disk
+ class(massDistributionClass ), pointer :: massDistributionGaseous, massDistributionStellar, &
+ & massDistribution_
type (abundances ), save :: abundancesFuel
!$omp threadprivate(abundancesFuel)
@@ -448,8 +420,48 @@ subroutine blitz2006ComputeFactors(self,node)
abundancesFuel=disk%abundancesGas()
call abundancesFuel%massToMassFraction(self%massGas)
self%hydrogenMassFraction=abundancesFuel%hydrogenMassFraction()
+ ! Determine if we have an exponential disk.
+ massDistributionGaseous => node%massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ massDistributionStellar => node%massDistribution(componentType=componentTypeDisk,massType=massTypeStellar)
+ self%isExponentialDisk = .true.
+ select type (massDistributionGaseous)
+ class is (massDistributionExponentialDisk)
+ ! The disk is exponential - no change needed.
+ class is (massDistributionCylindricalScaler )
+ ! Check the unscale distribution.
+ massDistribution_ => massDistributionGaseous%unscaled()
+ select type (massDistribution_)
+ class is (massDistributionExponentialDisk)
+ ! The disk is exponential - no change needed.
+ class default
+ self%isExponentialDisk=.false.
+ end select
+ class default
+ ! Not an exponential distribution.
+ self%isExponentialDisk=.false.
+ end select
+ select type (massDistributionStellar)
+ class is (massDistributionExponentialDisk)
+ ! The disk is exponential - no change needed.
+ class is (massDistributionCylindricalScaler )
+ ! Check the unscale distribution.
+ massDistribution_ => massDistributionStellar%unscaled()
+ select type (massDistribution_)
+ class is (massDistributionExponentialDisk)
+ ! The disk is exponential - no change needed.
+ class default
+ self%isExponentialDisk=.false.
+ end select
+ class default
+ ! Not an exponential distribution.
+ self%isExponentialDisk=.false.
+ end select
+ !![
+
+
+ !!]
! Properties required for exponential disks.
- if (self%assumeExponentialDisk .and. self%massStellar >= 0.0d0 .and. self%radiusDisk > 0.0d0) then
+ if (self%isExponentialDisk .and. self%massStellar >= 0.0d0 .and. self%radiusDisk > 0.0d0) then
self%pressureRatioCoefficient =+gravitationalConstantGalacticus &
& /8.0d0 &
& /Pi &
@@ -486,6 +498,8 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
!!{
Returns intervals to use for integrating the \cite{krumholz_star_2009} star formation rate over a galactic disk.
!!}
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeGaseous , massTypeStellar
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
@@ -495,15 +509,25 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
double precision , intent(in ) :: radiusInner , radiusOuter
logical , intent(inout), allocatable, dimension( :) :: intervalIsAnalytic
double precision , intent(inout), allocatable, dimension( :) :: integralsAnalytic
+ class (massDistributionClass ), pointer :: massDistributionGaseous , massDistributionStellar
double precision , parameter :: factorBoostStellarCoefficientTiny=1.0d-6
- double precision :: coefficientNormalization , coefficientFactorBoost, &
- & coefficientFactorBoostStellar , coefficientMolecular , &
- & rootValueInner , rootValueOuter , &
+ double precision :: coefficientNormalization , coefficientFactorBoost , &
+ & coefficientFactorBoostStellar , coefficientMolecular , &
+ & rootValueInner , rootValueOuter , &
& radiusAnalytic , sqrtTerm
- logical :: thresholdCondition
+ logical :: thresholdCondition , assumeMonotonicSurfaceDensity
! Check if we can assume a monotonic surface density.
- if (self%assumeMonotonicSurfaceDensity) then
+ massDistributionGaseous => node %massDistribution (componentType=componentTypeDisk,massType=massTypeGaseous)
+ massDistributionStellar => node %massDistribution (componentType=componentTypeDisk,massType=massTypeStellar)
+ assumeMonotonicSurfaceDensity = massDistributionGaseous%assumeMonotonicDecreasingSurfaceDensity( ) &
+ & .and. &
+ & massDistributionStellar%assumeMonotonicDecreasingSurfaceDensity( )
+ !![
+
+
+ !!]
+ if (assumeMonotonicSurfaceDensity) then
! Set the critical radius to a very negative value so that pressure ratio is always computed.
self%radiusCritical=-huge(0.0d0)
! Compute factors.
@@ -516,20 +540,24 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
self_ => self
node_ => node
! Test if the inner radius is below the pressure threshold.
- if (self%assumeExponentialDisk) then
+ if (self%isExponentialDisk) then
! For exponential disks this condition has a simple analytic form.
- rootValueInner =-huge(0.0d0)
- thresholdCondition=1.0d0/self%pressureRatioCoefficient-self%factorBoostStellarCoefficient >= 1.0d0
+ rootValueInner =-huge(0.0d0)
+ if (self%pressureRatioCoefficient > 0.0d0 .and. -exponent(self%pressureRatioCoefficient) < maxExponent(0.0d0)) then
+ thresholdCondition=1.0d0/self%pressureRatioCoefficient-self%factorBoostStellarCoefficient >= 1.0d0
+ else
+ thresholdCondition=.true.
+ end if
else
! For generic disks test this numerically.
- rootValueInner =blitz2006CriticalDensityRoot(radiusInner)
- thresholdCondition=rootValueInner <= 0.0d0
+ rootValueInner =blitz2006CriticalDensityRoot(radiusInner)
+ thresholdCondition =rootValueInner <= 0.0d0
end if
if (thresholdCondition) then
! The entire disk is below the pressure threshold so use a single interval.
allocate(blitz2006Intervals(2,1))
allocate(intervalIsAnalytic( 1))
- if (self%assumeExponentialDisk.and.self%useTabulation) then
+ if (self%isExponentialDisk.and.self%useTabulation) then
call computeCoefficients()
allocate(integralsAnalytic(1))
intervalIsAnalytic=.true.
@@ -541,14 +569,14 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
self%radiusCritical=-huge(0.0d0)
else
! Compute coefficients needed for analytic solutions.
- if (self%assumeExponentialDisk.and.self%useTabulation) call computeCoefficients()
+ if (self%isExponentialDisk.and.self%useTabulation) call computeCoefficients()
! Test the surface density at the outer radius.
rootValueOuter=blitz2006CriticalDensityRoot(radiusOuter)
if (rootValueOuter >= 0.0d0) then
! Entire disk is above the pressure threshold so use a single interval.
allocate(blitz2006Intervals(2,1))
allocate(intervalIsAnalytic( 1))
- if (self%assumeExponentialDisk.and.self%useTabulation) then
+ if (self%isExponentialDisk.and.self%useTabulation) then
allocate(integralsAnalytic ( 1))
intervalIsAnalytic =.true.
integralsAnalytic =self%integralFullyMolecular(coefficientNormalization,coefficientFactorBoost,radiusInner,radiusOuter)
@@ -560,7 +588,7 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
else
! The disk transitions the pressure threshold - attempt to locate the radius at which this happens and use two
! intervals split at this point.
- if (self%assumeExponentialDisk) then
+ if (self%isExponentialDisk) then
! For exponential disks we have an analytic solution for the transition radius.
if (self%factorBoostStellarCoefficient <= factorBoostStellarCoefficientTiny) then
radiusAnalytic=+0.5d0*log(self%pressureRatioCoefficient)
@@ -609,7 +637,7 @@ function blitz2006Intervals(self,node,radiusInner,radiusOuter,intervalIsAnalytic
self%radiusCriticalPrevious=self%radiusCritical
allocate(blitz2006Intervals(2,2))
allocate(intervalIsAnalytic( 2))
- if (self%assumeExponentialDisk.and.self%useTabulation) then
+ if (self%isExponentialDisk.and.self%useTabulation) then
allocate(integralsAnalytic ( 2))
intervalIsAnalytic =.true.
integralsAnalytic =[ &
@@ -667,8 +695,7 @@ subroutine computeCoefficients()
& )
return
end subroutine computeCoefficients
-
-
+
end function blitz2006Intervals
double precision function blitz2006IntegralFullyMolecular(self,coefficientNormalization,coefficientFactorBoost,radiusInner,radiusOuter)
@@ -1136,17 +1163,6 @@ Root function used in finding the radius in a disk where the pressure ratio exce
return
end function blitz2006CriticalDensityRoot
- double precision function blitz2006CriticalDensityExponentialRoot(radius)
- !!{
- Root function used in finding the radius in a disk where the pressure ratio exceeds the critical ratio.
- !!}
- implicit none
- double precision, intent(in ) :: radius
-
- blitz2006CriticalDensityExponentialRoot=self_%pressureRatioExponential(node_,radius)-1.0d0
- return
- end function blitz2006CriticalDensityExponentialRoot
-
double precision function blitz2006PressureRatio(self,node,radius,surfaceDensityGas) result(pressureRatio)
!!{
Root function used in finding the radius in a disk where the pressure ratio exceeds the critical ratio.
@@ -1154,16 +1170,25 @@ Root function used in finding the radius in a disk where the pressure ratio exce
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Galactic_Structure_Options , only : componentTypeDisk , coordinateSystemCylindrical, massTypeGaseous, massTypeStellar
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates, only : coordinateCylindrical, assignment(=)
implicit none
class (starFormationRateSurfaceDensityDisksBlitz2006), intent(inout) :: self
type (treeNode ), intent(inout) :: node
double precision , intent(in ) :: radius
double precision , intent( out), optional :: surfaceDensityGas
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateCylindrical ) :: coordinates
double precision :: surfaceDensityGas_, surfaceDensityStellar, &
& factorBoostStellar
! Get gas surface density.
- surfaceDensityGas_=self%galacticStructure_%surfaceDensity(node,[radius,0.0d0,0.0d0],coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeDisk,massType=massTypeGaseous)
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ surfaceDensityGas_ = massDistribution_%surfaceDensity ( coordinates )
+ !![
+
+ !!]
if (present(surfaceDensityGas)) surfaceDensityGas=surfaceDensityGas_
! If the radius is less than the critical radius the pressure radius is above 1 by definition, so simply pin it to that value.
if (radius <= self%radiusCritical) then
@@ -1179,7 +1204,11 @@ Root function used in finding the radius in a disk where the pressure ratio exce
& /self%pressureCharacteristic
if (pressureRatio > 0.0d0 .and. pressureRatio < 1.0d0) then
! Compute the stellar boost factor.
- surfaceDensityStellar=+self%galacticStructure_%surfaceDensity(node,[radius,0.0d0,0.0d0],coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeDisk,massType=massTypeStellar)
+ massDistribution_ => node %massDistribution(componentType=componentTypeDisk,massType=massTypeStellar)
+ surfaceDensityStellar = +massDistribution_%surfaceDensity ( coordinates )
+ !![
+
+ !!]
factorBoostStellar =+1.0d0 &
& +self%velocityDispersionDiskGas &
& /surfaceDensityGas_ &
@@ -1196,24 +1225,3 @@ Root function used in finding the radius in a disk where the pressure ratio exce
end if
return
end function blitz2006PressureRatio
-
- double precision function blitz2006PressureRatioExponential(self,node,radius) result(pressureRatio)
- !!{
- Root function used in finding the radius in a disk where the pressure ratio exceeds the critical ratio.
- !!}
- implicit none
- class (starFormationRateSurfaceDensityDisksBlitz2006), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- double precision :: factorBoostStellar
-
- ! Compute the pressure ratio that Blitz & Rosolowsky (2006) use to compute the molecular fraction.
- pressureRatio =+self%pressureRatioCoefficient &
- & *exp(-2.0d0*radius/self%radiusDisk)
- factorBoostStellar=+1.0d0 &
- & +self%factorBoostStellarCoefficient &
- & *exp(+0.5d0*radius/self%radiusDisk)
- pressureRatio =+pressureRatio &
- & *factorBoostStellar
- return
- end function blitz2006PressureRatioExponential
diff --git a/source/star_formation.rate_surface_density.disks.Kennicutt-Schmidt.F90 b/source/star_formation.rate_surface_density.disks.Kennicutt-Schmidt.F90
index b46428a068..880f3c2c4f 100644
--- a/source/star_formation.rate_surface_density.disks.Kennicutt-Schmidt.F90
+++ b/source/star_formation.rate_surface_density.disks.Kennicutt-Schmidt.F90
@@ -21,8 +21,7 @@
Implementation of a the Kennicutt-Schmidt star formation rate surface density for galactic disks.
!!}
- use :: Kind_Numbers , only : kind_int8
- use :: Galactic_Structure, only : galacticStructureClass
+ use :: Kind_Numbers, only : kind_int8
!![
@@ -57,14 +56,13 @@
Implementation of a Kennicutt-Schmidt star formation rate surface density for galactic disks.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
- double precision :: normalization , exponent , &
- & exponentTruncated , velocityDispersionDiskGas, &
- & toomreParameterCritical
- logical :: truncate
- integer (kind_int8 ) :: lastUniqueID
- logical :: factorsComputed
- double precision :: surfaceDensityCriticalFactor , hydrogenMassFraction
+ double precision :: normalization , exponent , &
+ & exponentTruncated , velocityDispersionDiskGas, &
+ & toomreParameterCritical
+ logical :: truncate
+ integer (kind_int8) :: lastUniqueID
+ logical :: factorsComputed
+ double precision :: surfaceDensityCriticalFactor, hydrogenMassFraction
contains
!![
@@ -95,7 +93,6 @@ function kennicuttSchmidtConstructorParameters(parameters) result(self)
implicit none
type (starFormationRateSurfaceDensityDisksKennicuttSchmidt) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: normalization , exponent , &
& exponentTruncated , velocityDispersionDiskGas, &
& toomreParameterCritical
@@ -142,30 +139,27 @@ function kennicuttSchmidtConstructorParameters(parameters) result(self)
The critical Toomre parameter for star formation in disks.
-
!!]
- self=starFormationRateSurfaceDensityDisksKennicuttSchmidt(normalization,exponent,truncate,exponentTruncated,velocityDispersionDiskGas,toomreParameterCritical,galacticStructure_)
+ self=starFormationRateSurfaceDensityDisksKennicuttSchmidt(normalization,exponent,truncate,exponentTruncated,velocityDispersionDiskGas,toomreParameterCritical)
!![
-
!!]
return
end function kennicuttSchmidtConstructorParameters
- function kennicuttSchmidtConstructorInternal(normalization,exponent,truncate,exponentTruncated,velocityDispersionDiskGas,toomreParameterCritical,galacticStructure_) result(self)
+ function kennicuttSchmidtConstructorInternal(normalization,exponent,truncate,exponentTruncated,velocityDispersionDiskGas,toomreParameterCritical) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily kennicuttSchmidt} star formation surface density rate from disks class.
!!}
use :: Numerical_Constants_Prefixes, only : mega
implicit none
- type (starFormationRateSurfaceDensityDisksKennicuttSchmidt) :: self
- double precision , intent(in ) :: normalization , exponent , &
- & exponentTruncated , velocityDispersionDiskGas, &
- & toomreParameterCritical
- logical , intent(in ) :: truncate
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
+ type (starFormationRateSurfaceDensityDisksKennicuttSchmidt) :: self
+ double precision , intent(in ) :: normalization , exponent , &
+ & exponentTruncated , velocityDispersionDiskGas, &
+ & toomreParameterCritical
+ logical , intent(in ) :: truncate
!![
-
+
!!]
self%lastUniqueID =-1_kind_int8
@@ -197,9 +191,6 @@ subroutine kennicuttSchmidtDestructor(self)
type(starFormationRateSurfaceDensityDisksKennicuttSchmidt), intent(inout) :: self
if (calculationResetEvent%isAttached(self,kennicuttSchmidtCalculationReset)) call calculationResetEvent%detach(self,kennicuttSchmidtCalculationReset)
- !![
-
- !!]
return
end subroutine kennicuttSchmidtDestructor
@@ -236,8 +227,10 @@ double precision function kennicuttSchmidtRate(self,node,radius)
assumed to have a flat rotation curve such that $\kappa = \sqrt{2} V/R$.
!!}
use :: Abundances_Structure , only : abundances
- use :: Galactic_Structure_Options , only : componentTypeDisk , coordinateSystemCylindrical, massTypeGaseous
- use :: Galacticus_Nodes , only : nodeComponentDisk , treeNode
+ use :: Coordinates , only : coordinateCylindrical , assignment(=)
+ use :: Galactic_Structure_Options , only : componentTypeDisk , massTypeGaseous
+ use :: Galacticus_Nodes , only : nodeComponentDisk
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Math , only : Pi
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
implicit none
@@ -245,10 +238,12 @@ double precision function kennicuttSchmidtRate(self,node,radius)
type (treeNode ), intent(inout) :: node
double precision , intent(in ) :: radius
class (nodeComponentDisk ), pointer :: disk
+ class (massDistributionClass ), pointer :: massDistribution_
type (abundances ), save :: abundancesFuel
!$omp threadprivate(abundancesFuel)
double precision :: surfaceDensityCritical, massGas, &
& surfaceDensityGas
+ type (coordinateCylindrical ) :: coordinates
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -272,7 +267,12 @@ double precision function kennicuttSchmidtRate(self,node,radius)
self%factorsComputed=.true.
end if
! Get gas surface density.
- surfaceDensityGas=self%galacticStructure_%surfaceDensity(node,[radius,0.0d0,0.0d0],coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeDisk,massType=massTypeGaseous)
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ surfaceDensityGas = massDistribution_%surfaceDensity ( coordinates )
+ !![
+
+ !!]
! Compute the star formation rate surface density.
kennicuttSchmidtRate=+self%normalization &
& *( &
diff --git a/source/star_formation.rate_surface_density.disks.Krumholz2009.F90 b/source/star_formation.rate_surface_density.disks.Krumholz2009.F90
index 7d897ad6bc..778c03e4ed 100644
--- a/source/star_formation.rate_surface_density.disks.Krumholz2009.F90
+++ b/source/star_formation.rate_surface_density.disks.Krumholz2009.F90
@@ -26,7 +26,6 @@
use :: Math_Exponentiation , only : fastExponentiator
use :: Tables , only : table1DLinearLinear
use :: Root_Finder , only : rootFinder
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -69,22 +68,21 @@
Implementation of the \cite{krumholz_star_2009} star formation rate surface density law for galactic disks.
!!}
private
- class (galacticStructureClass) , pointer :: galacticStructure_ => null()
- integer (kind_int8 ) :: lastUniqueID
- logical :: factorsComputed
- double precision :: massGasPrevious , radiusPrevious , &
- & radiusCriticalPrevious , radiusMaximumPrevious
- type (abundances ) :: abundancesFuelPrevious
- double precision :: chi , radiusDisk , &
- & massGas , hydrogenMassFraction , &
- & metallicityRelativeToSolar , sNormalization , &
- & sigmaMolecularComplexNormalization , clumpingFactorMolecularComplex, &
- & frequencyStarFormation
- logical :: assumeMonotonicSurfaceDensity , molecularFractionFast
- type (rootFinder ) :: finderCritical , finderMolecules
- type (fastExponentiator ) :: surfaceDensityExponentiator
- type (table1DLinearLinear ) :: molecularFractionTable
- procedure (double precision ), nopass, pointer :: molecularFractionFunction
+ integer (kind_int8 ) :: lastUniqueID
+ logical :: factorsComputed
+ double precision :: massGasPrevious , radiusPrevious , &
+ & radiusCriticalPrevious , radiusMaximumPrevious
+ type (abundances ) :: abundancesFuelPrevious
+ double precision :: chi , radiusDisk , &
+ & massGas , hydrogenMassFraction , &
+ & metallicityRelativeToSolar , sNormalization , &
+ & sigmaMolecularComplexNormalization, clumpingFactorMolecularComplex, &
+ & frequencyStarFormation
+ logical :: assumeMonotonicSurfaceDensity , molecularFractionFast
+ type (rootFinder ) :: finderCritical , finderMolecules
+ type (fastExponentiator ) :: surfaceDensityExponentiator
+ type (table1DLinearLinear) :: molecularFractionTable
+ procedure (double precision ), nopass, pointer :: molecularFractionFunction
contains
!![
@@ -131,9 +129,8 @@ function krumholz2009ConstructorParameters(parameters) result(self)
implicit none
type (starFormationRateSurfaceDensityDisksKrumholz2009) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
- double precision :: frequencyStarFormation , clumpingFactorMolecularComplex
- logical :: molecularFractionFast , assumeMonotonicSurfaceDensity
+ double precision :: frequencyStarFormation, clumpingFactorMolecularComplex
+ logical :: molecularFractionFast , assumeMonotonicSurfaceDensity
!![
@@ -161,17 +158,15 @@ function krumholz2009ConstructorParameters(parameters) result(self)
If true, assume that the surface density in disks is always monotonically decreasing.
-
!!]
- self=starFormationRateSurfaceDensityDisksKrumholz2009(frequencyStarFormation,clumpingFactorMolecularComplex,molecularFractionFast,assumeMonotonicSurfaceDensity,galacticStructure_)
+ self=starFormationRateSurfaceDensityDisksKrumholz2009(frequencyStarFormation,clumpingFactorMolecularComplex,molecularFractionFast,assumeMonotonicSurfaceDensity)
!![
-
!!]
return
end function krumholz2009ConstructorParameters
- function krumholz2009ConstructorInternal(frequencyStarFormation,clumpingFactorMolecularComplex,molecularFractionFast,assumeMonotonicSurfaceDensity,galacticStructure_) result(self)
+ function krumholz2009ConstructorInternal(frequencyStarFormation,clumpingFactorMolecularComplex,molecularFractionFast,assumeMonotonicSurfaceDensity) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily krumholz2009} star formation surface density rate from disks class.
!!}
@@ -179,14 +174,13 @@ function krumholz2009ConstructorInternal(frequencyStarFormation,clumpingFactorMo
use :: Table_Labels , only : extrapolationTypeFix
use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive
implicit none
- type (starFormationRateSurfaceDensityDisksKrumholz2009) :: self
- double precision , intent(in ) :: frequencyStarFormation , clumpingFactorMolecularComplex
- logical , intent(in ) :: molecularFractionFast , assumeMonotonicSurfaceDensity
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
- integer , parameter :: sCount =1000
- integer :: i
+ type (starFormationRateSurfaceDensityDisksKrumholz2009) :: self
+ double precision , intent(in ) :: frequencyStarFormation , clumpingFactorMolecularComplex
+ logical , intent(in ) :: molecularFractionFast , assumeMonotonicSurfaceDensity
+ integer , parameter :: sCount =1000
+ integer :: i
!![
-
+
!!]
self%lastUniqueID =-1_kind_int8
@@ -257,9 +251,6 @@ subroutine krumholz2009Destructor(self)
if (calculationResetEvent%isAttached(self,krumholz2009CalculationReset)) &
& call calculationResetEvent%detach (self,krumholz2009CalculationReset)
call self %molecularFractionTable%destroy( )
- !![
-
- !!]
return
end subroutine krumholz2009Destructor
@@ -398,22 +389,25 @@ subroutine krumholz2009SurfaceDensityFactors(self,node,radius,surfaceDensityGas,
!!{
Compute surface density and related quantities needed for the \cite{krumholz_star_2009} star formation rate model.
!!}
- use :: Galactic_Structure_Options, only : componentTypeDisk, coordinateSystemCylindrical, massTypeGaseous
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeDisk , massTypeGaseous
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (starFormationRateSurfaceDensityDisksKrumholz2009), intent(inout) :: self
type (treeNode ), intent(inout) :: node
double precision , intent(in ) :: radius
double precision , intent( out) :: surfaceDensityGas , surfaceDensityGasDimensionless
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: surfaceDensityTransition=85.0d12 ! M☉/Mpc²
+ type (coordinateCylindrical ) :: coordinates
! Get gas surface density.
- surfaceDensityGas=self%galacticStructure_%surfaceDensity( &
- & node , &
- & [radius ,0.0d0,0.0d0], &
- & coordinateSystem= coordinateSystemCylindrical , &
- & componentType = componentTypeDisk , &
- & massType = massTypeGaseous &
- & )
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistribution_ => node %massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ surfaceDensityGas = massDistribution_%surfaceDensity ( coordinates )
+ !![
+
+ !!]
! Compute the cloud density factor.
surfaceDensityGasDimensionless=+self%hydrogenMassFraction &
& * surfaceDensityGas &
diff --git a/source/star_formation.rate_surface_density.disks.extended_Schmidt.F90 b/source/star_formation.rate_surface_density.disks.extended_Schmidt.F90
index 8f249f273d..beb9169ccb 100644
--- a/source/star_formation.rate_surface_density.disks.extended_Schmidt.F90
+++ b/source/star_formation.rate_surface_density.disks.extended_Schmidt.F90
@@ -22,8 +22,6 @@
!!{
Implementation of the extended Schmidt star formation rate surface density law of \cite{shi_extended_2011} for galactic disks.
!!}
-
- use :: Galactic_Structure, only : galacticStructureClass
!![
@@ -43,11 +41,10 @@
Implementation of the extended Schmidt star formation rate surface density law of \cite{shi_extended_2011} for galactic disks.
!!}
private
- class (galacticStructureClass), pointer :: galacticStructure_ => null()
- integer (kind_int8 ) :: lastUniqueID
- logical :: factorsComputed
- double precision :: normalization , exponentGas , &
- & exponentStars , hydrogenMassFraction
+ integer (kind_int8) :: lastUniqueID
+ logical :: factorsComputed
+ double precision :: normalization , exponentGas , &
+ & exponentStars , hydrogenMassFraction
contains
!![
@@ -78,8 +75,7 @@ function extendedSchmidtConstructorParameters(parameters) result(self)
implicit none
type (starFormationRateSurfaceDensityDisksExtendedSchmidt) :: self
type (inputParameters ), intent(inout) :: parameters
- class (galacticStructureClass ), pointer :: galacticStructure_
- double precision :: normalization , exponentGas, &
+ double precision :: normalization, exponentGas, &
& exponentStars
!![
@@ -104,28 +100,25 @@ function extendedSchmidtConstructorParameters(parameters) result(self)
The exponent of stellar surface density in the extended Schmidt star formation law.
-
!!]
- self=starFormationRateSurfaceDensityDisksExtendedSchmidt(normalization,exponentGas,exponentStars,galacticStructure_)
+ self=starFormationRateSurfaceDensityDisksExtendedSchmidt(normalization,exponentGas,exponentStars)
!![
-
!!]
return
end function extendedSchmidtConstructorParameters
- function extendedSchmidtConstructorInternal(normalization,exponentGas,exponentStars,galacticStructure_) result(self)
+ function extendedSchmidtConstructorInternal(normalization,exponentGas,exponentStars) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily extendedSchmidt} star formation surface density rate from disks class.
!!}
use :: Numerical_Constants_Prefixes, only : giga, mega
implicit none
- type (starFormationRateSurfaceDensityDisksExtendedSchmidt) :: self
- double precision , intent(in ) :: normalization , exponentGas, &
- & exponentStars
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
+ type (starFormationRateSurfaceDensityDisksExtendedSchmidt) :: self
+ double precision , intent(in ) :: normalization, exponentGas, &
+ & exponentStars
!![
-
+
!!]
self%lastUniqueID =-1_kind_int8
@@ -159,9 +152,6 @@ subroutine extendedSchmidtDestructor(self)
type(starFormationRateSurfaceDensityDisksExtendedSchmidt), intent(inout) :: self
if (calculationResetEvent%isAttached(self,extendedSchmidtCalculationReset)) call calculationResetEvent%detach(self,extendedSchmidtCalculationReset)
- !![
-
- !!]
return
end subroutine extendedSchmidtDestructor
@@ -189,21 +179,25 @@ double precision function extendedSchmidtRate(self,node,radius)
\dot{\Sigma}_\star = A \left(x_\mathrm{H} {\Sigma_\mathrm{gas}\over M_\odot \hbox{pc}^{-2}}\right)
^{N_1} \left({\Sigma_{\star}\over M_\odot \hbox{pc}^{-2}}\right)^{N_2},
\end{equation}
- where $A=${\normalfont \ttfamily [normalization]} and $N_1=${\normalfont \ttfamily
- [exponentGas]}. $N_2=${\normalfont \ttfamily [exponentStars]}.
+ where $A=${\normalfont \ttfamily [normalization]}, $N_1=${\normalfont \ttfamily
+ [exponentGas]}, and $N_2=${\normalfont \ttfamily [exponentStars]}.
!!}
use :: Abundances_Structure , only : abundances
- use :: Galactic_Structure_Options, only : componentTypeDisk, coordinateSystemCylindrical, massTypeGaseous, massTypeStellar
- use :: Galacticus_Nodes , only : nodeComponentDisk, treeNode
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
+ use :: Galactic_Structure_Options, only : componentTypeDisk , massTypeGaseous, massTypeStellar
+ use :: Galacticus_Nodes , only : nodeComponentDisk
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
class (starFormationRateSurfaceDensityDisksExtendedSchmidt), intent(inout) :: self
type (treeNode ), intent(inout) :: node
double precision , intent(in ) :: radius
class (nodeComponentDisk ), pointer :: disk
+ class (massDistributionClass ), pointer :: massDistributionGaseous, massDistributionStellar
type (abundances ), save :: abundancesFuel
!$omp threadprivate(abundancesFuel)
- double precision :: massGas , surfaceDensityGas, &
+ double precision :: massGas , surfaceDensityGas, &
& surfaceDensityStellar
+ type (coordinateCylindrical ) :: coordinates
! Check if node differs from previous one for which we performed calculations.
if (node%uniqueID() /= self%lastUniqueID) call self%calculationReset(node,node%uniqueID())
@@ -225,8 +219,15 @@ double precision function extendedSchmidtRate(self,node,radius)
return
end if
! Get stellar and gas surface densities.
- surfaceDensityGas =self%galacticStructure_%surfaceDensity(node,[radius,0.0d0,0.0d0],coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeDisk,massType=massTypeGaseous)
- surfaceDensityStellar=self%galacticStructure_%surfaceDensity(node,[radius,0.0d0,0.0d0],coordinateSystem=coordinateSystemCylindrical,componentType=componentTypeDisk,massType=massTypeStellar)
+ coordinates = [radius,0.0d0,0.0d0]
+ massDistributionGaseous => node %massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ massDistributionStellar => node %massDistribution(componentType=componentTypeDisk,massType=massTypeStellar)
+ surfaceDensityGas = massDistributionGaseous%surfaceDensity ( coordinates )
+ surfaceDensityStellar = massDistributionStellar%surfaceDensity ( coordinates )
+ !![
+
+
+ !!]
! Compute the star formation rate surface density.
extendedSchmidtRate=+ self%normalization & ! Normalization of the star formation rate.
& *((self%hydrogenMassFraction*surfaceDensityGas )**self%exponentGas ) &
diff --git a/source/star_formation.timescales.velocity_maximum_scaling.F90 b/source/star_formation.timescales.velocity_maximum_scaling.F90
index dc79bed4b5..aed72ed6b0 100644
--- a/source/star_formation.timescales.velocity_maximum_scaling.F90
+++ b/source/star_formation.timescales.velocity_maximum_scaling.F90
@@ -78,11 +78,11 @@ function velocityMaxScalingConstructorParameters(parameters) result(self)
use :: Input_Parameters, only : inputParameter, inputParameters
implicit none
type (starFormationTimescaleVelocityMaxScaling) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- double precision :: timescale , exponentVelocity, &
- & exponentRedshift
+ type (inputParameters ), intent(inout) :: parameters
+ class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
+ double precision :: timescale , exponentVelocity, &
+ & exponentRedshift
! Get parameters of for the timescale calculation.
!![
@@ -195,11 +195,13 @@ double precision function velocityMaxScalingTimescale(self,component)
!!{
Returns the timescale (in Gyr) for star formation in the {\normalfont \ttfamily component} in the velocity maximum scaling timescale model.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions, only : massDistributionClass
+ implicit none
class (starFormationTimescaleVelocityMaxScaling), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: expansionFactor, velocityMaximum
! Check if node differs from previous one for which we performed calculations.
@@ -207,9 +209,13 @@ double precision function velocityMaxScalingTimescale(self,component)
! Compute the timescale if necessary.
if (.not.self%timescaleComputed) then
! Get virial velocity and expansion factor.
- basic => component%hostNode%basic ( )
- velocityMaximum = self %darkMatterProfileDMO_%circularVelocityMaximum(component%hostNode )
- expansionFactor = self %cosmologyFunctions_ %expansionFactor (basic %time ())
+ massDistribution_ => self %darkMatterProfileDMO_%get (component%hostNode )
+ basic => component%hostNode%basic ( )
+ velocityMaximum = massDistribution_ %velocityRotationCurveMaximum( )
+ expansionFactor = self %cosmologyFunctions_ %expansionFactor (basic %time ())
+ !![
+
+ !!]
! Compute the velocity factor.
if (velocityMaximum /= self%velocityMaximumPrevious) then
self%velocityMaximumPrevious=velocityMaximum
diff --git a/source/statistics.Nbody.halos.mass_errors.SO_halo_finder.F90 b/source/statistics.Nbody.halos.mass_errors.SO_halo_finder.F90
index bd21b29d7a..f36a97dcc7 100644
--- a/source/statistics.Nbody.halos.mass_errors.SO_halo_finder.F90
+++ b/source/statistics.Nbody.halos.mass_errors.SO_halo_finder.F90
@@ -120,13 +120,17 @@ double precision function soHaloFinderErrorFractional(self,node)
!!{
Return the fractional error on the mass of an N-body halo in the power-law error model.
!!}
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Math, only : Pi
implicit none
class (nbodyHaloMassErrorSOHaloFinder), intent(inout) :: self
type (treeNode ), intent(inout) :: node
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
double precision , parameter :: errorConstant =0.014d0
+ type (coordinateSpherical ) :: coordinates
double precision :: radiusHalo , densityOuterRadius, &
& densityRatioInternalToSurface , particleCount , &
& errorFractionalFixedSphere
@@ -141,9 +145,14 @@ double precision function soHaloFinderErrorFractional(self,node)
errorFractionalFixedSphere = +1.0d0 &
& /sqrt(particleCount)
! Get the outer radius of the halo.
- radiusHalo = +self%darkMatterHaloScale_ %radiusVirial(node )
+ radiusHalo = +self %darkMatterHaloScale_ %radiusVirial(node )
! Get the density at the edge of the halo.
- densityOuterRadius = +self%darkMatterProfileDMO_%density (node,radiusHalo)
+ massDistribution_ => self %darkMatterProfileDMO_%get (node )
+ coordinates = [radiusHalo,0.0d0,0.0d0]
+ densityOuterRadius = +massDistribution_ %density (coordinates)
+ !![
+
+ !!]
! Find the ratio of the mean interior density in the halo to the density at the halo outer radius.
densityRatioInternalToSurface = +3.0d0 &
& *basic%mass() &
diff --git a/source/stellar_feedback.outflows.Creasey2012.F90 b/source/stellar_feedback.outflows.Creasey2012.F90
index eb1446068a..9c6cf9d0ae 100644
--- a/source/stellar_feedback.outflows.Creasey2012.F90
+++ b/source/stellar_feedback.outflows.Creasey2012.F90
@@ -22,7 +22,6 @@
!!}
use :: Star_Formation_Rate_Surface_Density_Disks, only : starFormationRateSurfaceDensityDisksClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -46,7 +45,6 @@
!!}
private
class (starFormationRateSurfaceDensityDisksClass), pointer :: starFormationRateSurfaceDensityDisks_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: nu , mu, &
& beta0
contains
@@ -73,7 +71,6 @@ function creasey2012ConstructorParameters(parameters) result(self)
type (stellarFeedbackOutflowsCreasey2012 ) :: self
type (inputParameters ), intent(inout) :: parameters
class (starFormationRateSurfaceDensityDisksClass), pointer :: starFormationRateSurfaceDensityDisks_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: mu , nu, &
& beta0
@@ -100,29 +97,26 @@ function creasey2012ConstructorParameters(parameters) result(self)
The parameter $\beta_0$ appearing in the \cite{creasey_how_2012} model for supernovae feedback.
-
!!]
- self=stellarFeedbackOutflowsCreasey2012(mu,nu,beta0,starFormationRateSurfaceDensityDisks_,galacticStructure_)
+ self=stellarFeedbackOutflowsCreasey2012(mu,nu,beta0,starFormationRateSurfaceDensityDisks_)
!![
-
!!]
return
end function creasey2012ConstructorParameters
- function creasey2012ConstructorInternal(mu,nu,beta0,starFormationRateSurfaceDensityDisks_,galacticStructure_) result(self)
+ function creasey2012ConstructorInternal(mu,nu,beta0,starFormationRateSurfaceDensityDisks_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily creasey2012} stellar feedback class.
!!}
implicit none
type (stellarFeedbackOutflowsCreasey2012 ) :: self
class (starFormationRateSurfaceDensityDisksClass), intent(in ), target :: starFormationRateSurfaceDensityDisks_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
double precision , intent(in ) :: mu , nu, &
& beta0
!![
-
+
!!]
return
@@ -137,7 +131,6 @@ subroutine creasey2012Destructor(self)
!![
-
!!]
return
end subroutine creasey2012Destructor
@@ -153,15 +146,18 @@ subroutine creasey2012OutflowRate(self,component,rateStarFormation,rateEnergyInp
fraction, $\dot{\Sigma}_\star(r)$ is the surface density of star formation rate, $\beta_0=${\normalfont \ttfamily [beta0]},
$\mu=${\normalfont \ttfamily [mu]}, and $\nu=${\normalfont \ttfamily [nu]}.
!!}
- use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid
- use :: Numerical_Constants_Math, only : Pi
- use :: Numerical_Integration , only : integrator
- use :: Stellar_Feedback , only : feedbackEnergyInputAtInfinityCanonical
+ use :: Galactic_Structure_Options, only : componentTypeDisk , coordinateSystemCylindrical, massTypeGaseous, massTypeStellar
+ use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Integration , only : integrator
+ use :: Stellar_Feedback , only : feedbackEnergyInputAtInfinityCanonical
implicit none
class (stellarFeedbackOutflowsCreasey2012), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
double precision , intent(in ) :: rateEnergyInput , rateStarFormation
double precision , intent( out) :: rateOutflowEjective , rateOutflowExpulsive
+ class (massDistributionClass ), pointer :: massDistributionGaseous , massDistributionStellar
double precision , parameter :: radiusInnerDimensionless=0.0d0, radiusOuterDimensionless=10.0d0
double precision :: radiusScale , massGas , &
& radiusInner , radiusOuter , &
@@ -190,15 +186,21 @@ subroutine creasey2012OutflowRate(self,component,rateStarFormation,rateEnergyInp
radiusInner=radiusScale*radiusInnerDimensionless
radiusOuter=radiusScale*radiusOuterDimensionless
! Compute the outflow rate.
- integrator_ =integrator(outflowRateIntegrand,toleranceRelative=1.0d-3)
- rateOutflowEjective =+2.0d0 &
- & *Pi &
- & *self%beta0 &
- & *integrator_%integrate(radiusInner,radiusOuter) &
- & /rateStarFormation &
- & *rateEnergyInput &
- & /feedbackEnergyInputAtInfinityCanonical
- rateOutflowExpulsive=+0.0d0
+ massDistributionGaseous => component%hostNode%massDistribution(componentType=componentTypeDisk,massType=massTypeGaseous)
+ massDistributionStellar => component%hostNode%massDistribution(componentType=componentTypeDisk,massType=massTypeStellar)
+ integrator_ = integrator(outflowRateIntegrand,toleranceRelative=1.0d-3)
+ rateOutflowEjective = +2.0d0 &
+ & *Pi &
+ & *self%beta0 &
+ & *integrator_%integrate(radiusInner,radiusOuter) &
+ & /rateStarFormation &
+ & *rateEnergyInput &
+ & /feedbackEnergyInputAtInfinityCanonical
+ rateOutflowExpulsive = +0.0d0
+ !![
+
+
+ !!]
return
contains
@@ -207,29 +209,19 @@ double precision function outflowRateIntegrand(radius)
!!{
Integrand function for the ``Creasey et al. (2012)'' supernovae feedback calculation.
!!}
- use :: Galactic_Structure_Options , only : componentTypeDisk, coordinateSystemCylindrical, massTypeGaseous, massTypeStellar
+ use :: Coordinates , only : coordinateCylindrical, assignment(=)
use :: Numerical_Constants_Prefixes, only : mega
implicit none
- double precision, intent(in ) :: radius
- double precision :: fractionGas , densitySurfaceRateStarFormation, &
- & densitySurfaceGas, densitySurfaceStellar
+ double precision , intent(in ) :: radius
+ double precision :: fractionGas , densitySurfaceRateStarFormation, &
+ & densitySurfaceGas, densitySurfaceStellar
+ type (coordinateCylindrical) :: coordinates
+ coordinates=[radius,0.0d0,0.0d0]
! Get gas surface density.
- densitySurfaceGas =self%galacticStructure_%surfaceDensity( &
- & component%hostNode , &
- & [radius ,0.0d0,0.0d0], &
- & coordinateSystem= coordinateSystemCylindrical , &
- & componentType = componentTypeDisk , &
- & massType = massTypeGaseous &
- & )
+ densitySurfaceGas =massDistributionGaseous%surfaceDensity(coordinates)
! Get stellar surface density.
- densitySurfaceStellar=self%galacticStructure_%surfaceDensity( &
- & component%hostNode , &
- & [radius ,0.0d0,0.0d0], &
- & coordinateSystem= coordinateSystemCylindrical , &
- & componentType = componentTypeDisk , &
- & massType = massTypeStellar &
- & )
+ densitySurfaceStellar=massDistributionStellar%surfaceDensity(coordinates)
! Compute the gas fraction.
fractionGas=+ densitySurfaceGas &
& /( &
diff --git a/source/stellar_feedback.outflows.velocity_maximum_scaling.F90 b/source/stellar_feedback.outflows.velocity_maximum_scaling.F90
index e473c1e7af..1369a4d8ea 100644
--- a/source/stellar_feedback.outflows.velocity_maximum_scaling.F90
+++ b/source/stellar_feedback.outflows.velocity_maximum_scaling.F90
@@ -151,21 +151,27 @@ subroutine vlctyMxSclngOutflowRate(self,component,rateStarFormation,rateEnergyIn
!!{
Returns the outflow rate (in $M_\odot$ Gyr$^{-1}$) for star formation in the given {\normalfont \ttfamily component}.
!!}
- use :: Galacticus_Nodes, only : nodeComponentBasic
+ use :: Galacticus_Nodes , only : nodeComponentBasic
+ use :: Mass_Distributions, only : massDistributionClass
implicit none
class (stellarFeedbackOutflowsVlctyMxSclng), intent(inout) :: self
class (nodeComponent ), intent(inout) :: component
double precision , intent(in ) :: rateEnergyInput , rateStarFormation
double precision , intent( out) :: rateOutflowEjective, rateOutflowExpulsive
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
double precision :: expansionFactor , velocityMaximum
!$GLC attributes unused :: rateStarFormation
! Get the basic component.
basic => component%hostNode%basic()
! Get virial velocity and expansion factor.
- velocityMaximum=self%darkMatterProfileDMO_%circularVelocityMaximum(component%hostNode )
- expansionFactor=self%cosmologyFunctions_ %expansionFactor (basic %time ())
+ massDistribution_ => self %darkMatterProfileDMO_%get (component%hostNode )
+ velocityMaximum = massDistribution_ %velocityRotationCurveMaximum( )
+ expansionFactor = self %cosmologyFunctions_ %expansionFactor (basic %time ())
+ !![
+
+ !!]
! Compute the velocity factor.
if (velocityMaximum /= self%velocityPrevious) then
self%velocityPrevious = velocityMaximum
diff --git a/source/stellar_populations.spectra.postprocess.age_window.F90 b/source/stellar_populations.spectra.postprocess.age_window.F90
index a48f9deeb1..7d6ee201ec 100644
--- a/source/stellar_populations.spectra.postprocess.age_window.F90
+++ b/source/stellar_populations.spectra.postprocess.age_window.F90
@@ -19,11 +19,13 @@
!% An implementation of a spectrum postprocessor that keeps only populations in a specified age window.
- !#
- !#
- !# A stellar population postprocessor class which keeps only emission from populations with ages between {\normalfont \ttfamily [ageMinimum]} and {\normalfont \ttfamily [ageMaximum]}.
- !#
- !#
+ !![
+
+
+ A stellar population postprocessor class which keeps only emission from populations with ages between {\normalfont \ttfamily [ageMinimum]} and {\normalfont \ttfamily [ageMaximum]}.
+
+
+ !!]
type, extends(stellarPopulationSpectraPostprocessorClass) :: stellarPopulationSpectraPostprocessorAgeWindow
!% An ageWindow spectrum postprocessor.
private
@@ -49,18 +51,20 @@ function ageWindowConstructorParameters(parameters) result(self)
type (inputParameters ), intent(inout) :: parameters
double precision :: ageMinimum, ageMaximum
- !#
- !# ageMinimum
- !# 0.0d0
- !# The minimum age of stellar populations to retain.
- !#
- !#
- !#
- !# ageMaximum
- !# huge(0.0d0)
- !# The maximum age of stellar populations to retain.
- !#
- !#
+ !![
+
+ ageMinimum
+ 0.0d0
+ The minimum age of stellar populations to retain.
+
+
+
+ ageMaximum
+ huge(0.0d0)
+ The maximum age of stellar populations to retain.
+
+
+ !!]
self=stellarPopulationSpectraPostprocessorAgeWindow(ageMinimum,ageMaximum)
return
end function ageWindowConstructorParameters
@@ -70,8 +74,10 @@ function ageWindowConstructorInternal(ageMinimum,ageMaximum) result(self)
implicit none
type (stellarPopulationSpectraPostprocessorAgeWindow) :: self
double precision , intent(in ) :: ageMinimum, ageMaximum
- !#
-
+ !![
+
+ !!]
+
return
end function ageWindowConstructorInternal
diff --git a/source/stellar_populations.standard.F90 b/source/stellar_populations.standard.F90
index 1cb354fa51..19a9fd3f0c 100644
--- a/source/stellar_populations.standard.F90
+++ b/source/stellar_populations.standard.F90
@@ -539,6 +539,7 @@ double precision function standardInterpolate(self,abundances_,ageMinimum,ageMax
call file %writeDataset(property%property,char(property%label))
call file %close ( )
!$ call hdf5Access%unset ( )
+ call displayIndent('Storing to file: '//fileName,verbosityLevelWorking)
end if
call File_Unlock(lock)
! Build interpolators.
diff --git a/source/structure_formation.accretion_flow.DiemerKravtsov2014.F90 b/source/structure_formation.accretion_flow.DiemerKravtsov2014.F90
deleted file mode 100644
index af263a089b..0000000000
--- a/source/structure_formation.accretion_flow.DiemerKravtsov2014.F90
+++ /dev/null
@@ -1,238 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An accretion flow class which models the accretion flow using the fitting function of \cite{diemer_dependence_2014}.
- !!}
-
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmological_Density_Field, only : cosmologicalMassVarianceClass, criticalOverdensityClass
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
-
- !![
-
-
- An accretion flow class which models the accretion flow using the fitting function of
- \cite{diemer_dependence_2014}. Specifically, the density profile of the accretion flow is modeled using their equation~(4),
- along with fits to the redshift and $\nu$ dependencies of the fitting parameters $b_\mathrm{e}$ and $s_\mathrm{e}$ chosen to
- match the results of their figure~18.
-
-
- !!]
- type, extends(accretionFlowsClass) :: accretionFlowsDiemerKravtsov2014
- !!{
- An accretion flow class which models the accretion flow using the fitting function of \cite{diemer_dependence_2014}.
- !!}
- private
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (cosmologicalMassVarianceClass), pointer :: cosmologicalMassVariance_ => null()
- class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
- double precision :: b0 , s0 , &
- & bz , sz , &
- & bnu , snu
- contains
- final :: diemerKravtsov2014Destructor
- procedure :: density => diemerKravtsov2014Density
- procedure :: velocity => diemerKravtsov2014Velocity
- end type accretionFlowsDiemerKravtsov2014
-
- interface accretionFlowsDiemerKravtsov2014
- !!{
- Constructors for the {\normalfont \ttfamily diemerKravtsov2014} accretion flows class.
- !!}
- module procedure diemerKravtsov2014ConstructorParameters
- module procedure diemerKravtsov2014ConstructorInternal
- end interface accretionFlowsDiemerKravtsov2014
-
-contains
-
- function diemerKravtsov2014ConstructorParameters(parameters) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily diemerKravtsov2014} accretion flow class that takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameter, inputParameters
- implicit none
- type (accretionFlowsDiemerKravtsov2014) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
- class (criticalOverdensityClass ), pointer :: criticalOverdensity_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
- double precision :: b0 , s0 , &
- & bz , sz , &
- & bnu , snu
-
- !![
-
- b0
-
- +1.1250d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $b_0$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
- bz
-
- +0.625d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $b_z$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
- bnu
-
- -0.2250d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $b_\nu$ in the fitting function $b(\nu,z)=b_0 (1+z)^{b_z} \nu^{b_\nu}$ for the parameter $b(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
- s0
-
- +1.3925d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $s_0$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
- sz
-
- -0.199d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $s_z$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
- snu
-
- +0.0875d0
- Derived by Andrew Benson by constructing simple functional forms which fit the plots in figure 18 of \cite{diemer_dependence_2014}.
- The parameter $s_\nu$ in the fitting function $s(\nu,z)=s_0 (1+z)^{s_z} \nu^{s_\nu}$ for the parameter $s(\nu,z)$ appearing in equation (4) of \cite{diemer_dependence_2014}.
-
-
-
-
-
- !!]
- self=accretionFlowsDiemerKravtsov2014(b0,bz,bnu,s0,sz,snu,cosmologyFunctions_,cosmologicalMassVariance_,criticalOverdensity_,darkMatterProfileDMO_)
- !![
-
-
-
-
-
- !!]
- return
- end function diemerKravtsov2014ConstructorParameters
-
- function diemerKravtsov2014ConstructorInternal(b0,bz,bnu,s0,sz,snu,cosmologyFunctions_,cosmologicalMassVariance_,criticalOverdensity_,darkMatterProfileDMO_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily diemerKravtsov2014} accretion flows class.
- !!}
- implicit none
- type (accretionFlowsDiemerKravtsov2014) :: self
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (cosmologicalMassVarianceClass ), intent(in ), target :: cosmologicalMassVariance_
- class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
- double precision , intent(in ) :: b0 , s0 , &
- & bz , sz , &
- & bnu , snu
- !![
-
- !!]
-
- return
- end function diemerKravtsov2014ConstructorInternal
-
- subroutine diemerKravtsov2014Destructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily diemerKravtsov2014} accretion flows class.
- !!}
- implicit none
- type(accretionFlowsDiemerKravtsov2014), intent(inout) :: self
-
- !![
-
-
-
-
- !!]
- return
- end subroutine diemerKravtsov2014Destructor
-
- double precision function diemerKravtsov2014Density(self,node,radius)
- !!{
- Compute the density of the accretion flow at the given radius.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (accretionFlowsDiemerKravtsov2014), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- double precision :: time , mass , &
- & radius200Mean, densityMean, &
- & nu , redshift , &
- & b , s
-
- ! Extract basic quantities for the halo.
- basic => node %basic()
- time = basic%time ()
- mass = basic%mass ()
- ! Evaluate the control parameters.
- redshift=+self%cosmologyFunctions_ %redshiftFromExpansionFactor( &
- & self%cosmologyFunctions_%expansionFactor (time=time ) &
- & )
- nu =+self%criticalOverdensity_ %value (time=time,mass=mass,node=node) &
- & /self%cosmologicalMassVariance_%rootVariance (time=time,mass=mass )
- ! Evaluate the parameters of the fitting function. These fits were derived by Andrew Benson by constructing simple functional
- ! forms which fit the plots in figure 18 of Diemer & Kravtsov (2014). There is no guarantee that these fits will perform
- ! sensibly outside the range of that plot (and, of course, they are only approximate even within the range of that plot).
- b=+self%b0*(1.0+redshift)**self%bz*nu**self%bnu
- s=+self%s0*(1.0+redshift)**self%sz*nu**self%snu
- ! Find the radius enclosing 200 times the mean density.
- densityMean =self%cosmologyFunctions_ %matterDensityEpochal (time )
- radius200Mean=self%darkMatterProfileDMO_%radiusEnclosingDensity(node,+200.0d0*densityMean)
- ! Evaluate equation (4) from Diemer & Kravtsov (2014).
- diemerKravtsov2014Density=+densityMean &
- & *( &
- & +1.0d0 &
- & +b &
- & /( &
- & +radius &
- & /5.0d0 &
- & /radius200Mean &
- & )**s &
- & )
- return
- end function diemerKravtsov2014Density
-
- double precision function diemerKravtsov2014Velocity(self,node,radius)
- !!{
- Compute the velocity of the accretion flow at the given radius.
- !!}
- use :: Error, only : Error_Report
- implicit none
- class (accretionFlowsDiemerKravtsov2014), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- diemerKravtsov2014Velocity=0.0d0
- call Error_Report('velocity is unsupported'//{introspection:location})
- return
- end function diemerKravtsov2014Velocity
diff --git a/source/structure_formation.accretion_flow.F90 b/source/structure_formation.accretion_flow.F90
deleted file mode 100644
index b403d4fdae..0000000000
--- a/source/structure_formation.accretion_flow.F90
+++ /dev/null
@@ -1,54 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 provides a class implementing models of accretion flows onto dark matter halos.
-!!}
-
-module Spherical_Collapse_Accretion_Flows
- !!{
- Provides a class implementing models of accretion flows onto dark matter halos.
- !!}
- use :: Galacticus_Nodes, only : treeNode
- private
-
- !![
-
- accretionFlows
- Accretion Flows onto Dark Matter Halos
- Class providing models of accretion flows onto dark matter halos.
- shi2016
-
- Compute the density of the accretion flow at the given radius.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- Compute the velocity of the accretion flow at the given radius.
- double precision
- yes
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
-
- !!]
-
-end module Spherical_Collapse_Accretion_Flows
diff --git a/source/structure_formation.accretion_flow.Lam2013.F90 b/source/structure_formation.accretion_flow.Lam2013.F90
deleted file mode 100644
index eb53262c41..0000000000
--- a/source/structure_formation.accretion_flow.Lam2013.F90
+++ /dev/null
@@ -1,221 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An accretion flow class which models the accretion flow using the model of \cite{lam_modeling_2013}.
- !!}
-
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmological_Density_Field , only : criticalOverdensityClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Correlation_Functions_Two_Point, only : correlationFunctionTwoPointClass
- use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBiasClass
- use :: Linear_Growth , only : linearGrowthClass
-
-
- !![
-
-
- An accretion flow class using the model of \cite{lam_modeling_2013}.
-
-
- !!]
- type, extends(accretionFlowsClass) :: accretionFlowsLam2013
- !!{
- An accretion flow class which models the accretion flow using the model of \cite{lam_modeling_2013}.
- !!}
- private
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
- class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null()
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (correlationFunctionTwoPointClass), pointer :: correlationFunctionTwoPoint_ => null()
- class (linearGrowthClass ), pointer :: linearGrowth_ => null()
- double precision :: scaleFactorVelocity
- contains
- final :: lam2013Destructor
- procedure :: density => lam2013Density
- procedure :: velocity => lam2013Velocity
- end type accretionFlowsLam2013
-
- interface accretionFlowsLam2013
- !!{
- Constructors for the {\normalfont \ttfamily lam2013} accretion flows class.
- !!}
- module procedure lam2013ConstructorParameters
- module procedure lam2013ConstructorInternal
- end interface accretionFlowsLam2013
-
-contains
-
- function lam2013ConstructorParameters(parameters) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily lam2013} accretion flow class that takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameter, inputParameters
- implicit none
- type (accretionFlowsLam2013) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (criticalOverdensityClass ), pointer :: criticalOverdensity_
- class (darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (correlationFunctionTwoPointClass), pointer :: correlationFunctionTwoPoint_
- class (linearGrowthClass ), pointer :: linearGrowth_
- double precision :: scaleFactorVelocity
-
- !![
-
- scaleFactorVelocity
-
- 1.0d0
- A scale factor to be applied to inflow velocities.
-
-
-
-
-
-
-
- !!]
- self=accretionFlowsLam2013(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,darkMatterHaloBias_,darkMatterHaloScale_,correlationFunctionTwoPoint_,linearGrowth_)
- !![
-
-
-
-
-
-
-
- !!]
- return
- end function lam2013ConstructorParameters
-
- function lam2013ConstructorInternal(scaleFactorVelocity,cosmologyFunctions_,criticalOverdensity_,darkMatterHaloBias_,darkMatterHaloScale_,correlationFunctionTwoPoint_,linearGrowth_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily lam2013} accretion flows class.
- !!}
- implicit none
- type (accretionFlowsLam2013 ) :: self
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
- class (darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (correlationFunctionTwoPointClass), intent(in ), target :: correlationFunctionTwoPoint_
- class (linearGrowthClass ), intent(in ), target :: linearGrowth_
- double precision , intent(in ) :: scaleFactorVelocity
- !![
-
- !!]
-
- return
- end function lam2013ConstructorInternal
-
- subroutine lam2013Destructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily lam2013} accretion flows class.
- !!}
- implicit none
- type(accretionFlowsLam2013), intent(inout) :: self
-
- !![
-
-
-
-
-
-
- !!]
- return
- end subroutine lam2013Destructor
-
- double precision function lam2013Density(self,node,radius)
- !!{
- Compute the density of the accretion flow at the given radius.
- !!}
- use :: Error, only : Error_Report
- implicit none
- class (accretionFlowsLam2013), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- lam2013Density=0.0d0
- call Error_Report('density is unsupported'//{introspection:location})
- return
- end function lam2013Density
-
- double precision function lam2013Velocity(self,node,radius)
- !!{
- Compute the mean radial velocity of the accretion flow at the given radius.
- !!}
- use :: Galacticus_Nodes , only : nodeComponentBasic
- use :: Numerical_Constants_Math, only : Pi
- implicit none
- class (accretionFlowsLam2013), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- double precision :: time , overdensityCritical, &
- & radiusVirial , massShell , &
- & densityContrastNonLinear
-
- basic => node %basic ( )
- time = basic %time ( )
- overdensityCritical = self %criticalOverdensity_%value (time)
- radiusVirial = self %darkMatterHaloScale_%radiusVirial (node)
- ! Evaluate the mass in the shell outside the halo virial radius using equation (B4) of Lam et al. (2013).
- if (radius > radiusVirial) then
- massShell = +self %cosmologyFunctions_ %matterDensityEpochal(time) &
- & *4.0d0 &
- & *Pi &
- & /3.0d0 &
- & *( &
- & +radius **3*(1.0d0+self%darkMatterHaloBias_%bias(node,radius )*self%correlationFunctionTwoPoint_%correlationVolumeAveraged(radius ,time)) &
- & -radiusVirial**3*(1.0d0+self%darkMatterHaloBias_%bias(node,radiusVirial)*self%correlationFunctionTwoPoint_%correlationVolumeAveraged(radiusVirial,time)) &
- & )
- else
- massShell=0.0d0
- end if
- ! Compute the nonlinear density contrast using equation (B1) of Lam et al. (2013).
- densityContrastNonlinear = +( &
- & +basic%mass () &
- & + massShell &
- & ) &
- & /self%cosmologyFunctions_%matterDensityEpochal (time) &
- & /( &
- & +4.0d0 &
- & *Pi &
- & /3.0d0 &
- & *radius**3 &
- & )
- ! Evaluate the inflow velocity in the spherical collapse model using equation (B2) of Lam et al. (2013).
- lam2013Velocity = -self%scaleFactorVelocity &
- & *self%cosmologyFunctions_%hubbleParameterEpochal (time) &
- & *radius &
- & *self%cosmologyFunctions_%expansionFactor (time) &
- & *self%linearGrowth_ %logarithmicDerivativeExpansionFactor(time) &
- & /3.0d0 &
- & * overdensityCritical &
- & *( &
- & +densityContrastNonLinear**(1.0d0/overdensityCritical) &
- & -1.0d0 &
- & )
- return
- end function lam2013Velocity
diff --git a/source/structure_formation.accretion_flow.Shi2016.F90 b/source/structure_formation.accretion_flow.Shi2016.F90
deleted file mode 100644
index 34bc387bc8..0000000000
--- a/source/structure_formation.accretion_flow.Shi2016.F90
+++ /dev/null
@@ -1,805 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An accretion flow class using the framework of \cite{shi_outer_2016}.
- !!}
-
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Cosmology_Parameters , only : cosmologyParametersClass
- use :: Dark_Matter_Halo_Mass_Accretion_Histories, only : darkMatterHaloMassAccretionHistoryClass
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Numerical_Interpolation , only : gsl_interp_cspline , interpolator
- use :: Spherical_Collapse_Solvers , only : sphericalCollapseSolverClass
-
- ! Note: Throughout this class the following acronyms are used:
- ! * HRTA - "half radius turnaround" - i.e. half of the turnaround radius for a given shell (more precisely, we use the
- ! ratio of the virial radius to turnaround radius determined by spherical collapse models - this is previously
- ! 1/2 for an Einstein-de Sitter universe, but differs by a small amount for other cosmologies).
-
- ! Note: Throughout this class different three separate unit systems are used, identified by the variable name suffix:
- ! * Scaled - these correspond to the scaled, self-similar variables used in Appendix A of Shi (2016) - i.e. column 3 of Table A1, "y" for radius, etc.
- ! * Original - these correspond to the original variables used in Appendix A of Shi (2016) - i.e. column 2 of Table A1, "R" for radius, etc.
- ! * ScaleHRTA - these correspond to the HRTA unit system - that is, quantities are scaled to Rₕᵣₜₐ(a) and Mₕᵣₜₐ(a).
- ! * Physical - these correspond to physical units (Mpc, M☉, km/s).
-
- !![
-
- An accretion flow class using the framework of \cite{shi_outer_2016}.
-
- !!]
- type, extends(accretionFlowsClass) :: accretionFlowsShi2016
- !!{
- An accretion flow class using the framework of \cite{shi_outer_2016}.
- !!}
- private
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
- class (darkMatterHaloMassAccretionHistoryClass), pointer :: darkMatterHaloMassAccretionHistory_ => null()
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
- class (sphericalCollapseSolverClass ), pointer :: sphericalCollapseSolver_ => null()
- type (interpolator ), allocatable :: interpolatorDensityPhysical , interpolatorVelocityPhysical , &
- & interpolatorScaleFactorHalfRadiusTurnaroundScaled , interpolatorRadiusScaled , &
- & interpolatorRadiusTurnaroundOriginal , interpolatorRadiusHRTAOriginal , &
- & interpolatorRadiusComovingInitialOriginal , interpolatorMassTurnaroundScaled , &
- & interpolatorMassHRTAOriginal , interpolatorMassMultiStreamScaleHRTA
- double precision , allocatable, dimension(:) :: radiusScaled , overdensityScaled , &
- & expansionFactorHRTAScaled , radiusGrowthRateScaled , &
- & radiusComovingInitialOriginal , massEnclosedInitialOriginal , &
- & timeTurnaroundScaled , timeHRTAScaled , &
- & radiusTurnaroundScaled , radiusOrderedOriginal , &
- & massShellOrderedOriginal , densityOrderedOriginal , &
- & massEnclosedOrderedOriginal
- double precision :: radiusMaximumPhysical , timePreviousPhysical , &
- & massPreviousPhysical , growthIndexPrevious , &
- & expansionFactorScaled , radiusMultistreamMinimumScaledHRTA , &
- & radiusMultistreamMaximumScaledHRTA , cosmologicalConstantScaled , &
- & radiusTurnaroundNowOriginal , radiusHRTANowOriginal , &
- & timeNowScaled , radiusSplashbackTurnaround , &
- & ratioRadiusSplashbackHRTA , radiusSplashbackOriginal , &
- & radiusSplashbackScaled , radiusMinimumPhysical , &
- & scaleFactorVelocity
- contains
- !![
-
-
-
- !!]
- final :: shi2016Destructor
- procedure :: density => shi2016Density
- procedure :: velocity => shi2016Velocity
- procedure :: solve => shi2016Solve
- end type accretionFlowsShi2016
-
- interface accretionFlowsShi2016
- !!{
- Constructors for the {\normalfont \ttfamily shi2016} accretion flows class.
- !!}
- module procedure shi2016ConstructorParameters
- module procedure shi2016ConstructorInternal
- end interface accretionFlowsShi2016
-
- ! Sub-module scope variables used in root finding.
- double precision :: timeTurnaroundScaled__, radiusTurnaroundScaled__, ratioRadiusTurnaroundVirial
- !$omp threadprivate(timeTurnaroundScaled__, radiusTurnaroundScaled__, ratioRadiusTurnaroundVirial)
-
- ! Sub-module scope variables used in ODE solving.
- double precision :: radiusComovingInitialOriginal , massEnclosedInitialOriginal
- class (accretionFlowsShi2016), pointer :: self_
- logical :: noShellCrossing =.false.
- !$omp threadprivate(self_,massEnclosedInitialOriginal,radiusComovingInitialOriginal,noShellCrossing)
-
-contains
-
- function shi2016ConstructorParameters(parameters) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily shi2016} accretion flow class that takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameter, inputParameters
- implicit none
- type (accretionFlowsShi2016 ) :: self
- type (inputParameters ), intent(inout) :: parameters
- class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (cosmologyParametersClass ), pointer :: cosmologyParameters_
- class (darkMatterHaloMassAccretionHistoryClass), pointer :: darkMatterHaloMassAccretionHistory_
- class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
- class (sphericalCollapseSolverClass ), pointer :: sphericalCollapseSolver_
- double precision :: scaleFactorVelocity
-
- !![
-
- scaleFactorVelocity
-
- 1.0d0
- A scale factor to be applied to inflow velocities.
-
-
-
-
-
-
- !!]
- self=accretionFlowsShi2016(scaleFactorVelocity,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloMassAccretionHistory_,darkMatterHaloScale_,sphericalCollapseSolver_)
- !![
-
-
-
-
-
- !!]
- return
- end function shi2016ConstructorParameters
-
- function shi2016ConstructorInternal(scaleFactorVelocity,cosmologyParameters_,cosmologyFunctions_,darkMatterHaloMassAccretionHistory_,darkMatterHaloScale_,sphericalCollapseSolver_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily shi2016} accretion flows class.
- !!}
- use :: Numerical_Comparison, only : Values_Agree
- implicit none
- type (accretionFlowsShi2016 ) :: self
- class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
- class (darkMatterHaloMassAccretionHistoryClass), intent(in ), target :: darkMatterHaloMassAccretionHistory_
- class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
- class (sphericalCollapseSolverClass ), intent(in ), target :: sphericalCollapseSolver_
- double precision , intent(in ) :: scaleFactorVelocity
- !![
-
- !!]
-
- ! Validate cosmology.
- if (.not.Values_Agree(self%cosmologyParameters_%OmegaCurvature (),+0.0d0,absTol=1.0d-3)) &
- & call Error_Report('this class is applicable only for flat Ω+Λ=1 universes'//{introspection:location})
- if (.not.Values_Agree(self%cosmologyFunctions_ %equationOfStateDarkEnergy(),-1.0d0,absTol=1.0d-3)) &
- & call Error_Report('this class is applicable only for flat Ω+Λ=1 universes'//{introspection:location})
- if (.not.Values_Agree(self%cosmologyFunctions_ %exponentDarkEnergy (),+0.0d0,absTol=1.0d-3)) &
- & call Error_Report('this class is applicable only for flat Ω+Λ=1 universes'//{introspection:location})
- ! Set previous state to unphysical values.
- self%timePreviousPhysical =-huge(0.0d0)
- self%massPreviousPhysical =-huge(0.0d0)
- self%growthIndexPrevious=-huge(0.0d0)
- return
- end function shi2016ConstructorInternal
-
- subroutine shi2016Destructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily shi2016} accretion flows class.
- !!}
- implicit none
- type(accretionFlowsShi2016), intent(inout) :: self
-
- !![
-
-
-
-
-
- !!]
- return
- end subroutine shi2016Destructor
-
- double precision function shi2016Density(self,node,radius)
- !!{
- Compute the density of the accretion flow at the given radius.
- !!}
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (accretionFlowsShi2016), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
-
- call self%solve(node)
- if (radius > self%radiusMaximumPhysical) then
- ! Beyond the maximum radius for the flow just return the mean matter density.
- basic => node%basic ( )
- shi2016Density = self%cosmologyFunctions_ %matterDensityEpochal(basic%time())
- else if (radius < self%radiusMinimumPhysical) then
- shi2016Density = 0.0d0
- call Error_Report('radius is less than minimum tabulated for accretion flow'//{introspection:location})
- else
- shi2016Density = self%interpolatorDensityPhysical%interpolate ( radius)
- end if
- return
- end function shi2016Density
-
- double precision function shi2016Velocity(self,node,radius)
- !!{
- Compute the velocity of the accretion flow at the given radius.
- !!}
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (accretionFlowsShi2016), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
-
- call self%solve(node)
- if (radius > self%radiusMaximumPhysical) then
- ! Beyond the maximum radius for the flow just return the mean matter velocity.
- basic => node%basic()
- shi2016Velocity = +self%cosmologyFunctions_ %hubbleParameterEpochal(basic%time()) &
- & *radius
- else if (radius < self%radiusMinimumPhysical) then
- shi2016Velocity = 0.0d0
- call Error_Report('radius is less than minimum tabulated for accretion flow'//{introspection:location})
- else
- shi2016Velocity = self%interpolatorVelocityPhysical%interpolate ( radius)
- end if
- shi2016Velocity=+shi2016Velocity &
- & *self%scaleFactorVelocity
- return
- end function shi2016Velocity
-
- subroutine shi2016Solve(self,node)
- !!{
- Solve the accretion flow.
- !!}
- use :: Array_Utilities , only : Array_Reverse
- use :: Display , only : displayCounter , displayCounterClear , displayIndent , displayUnindent, &
- & verbosityLevelWorking
- use :: Elliptic_Integrals , only : Elliptic_Integral_K , Elliptic_Integral_Pi
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeComponentBasic
- use :: ISO_Varying_String , only : var_str
- use :: Numerical_Comparison , only : Values_Differ
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
- use :: Numerical_Constants_Math , only : Pi
- use :: Numerical_Constants_Prefixes , only : kilo
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Root_Finder , only : rangeExpandMultiplicative, rangeExpandSignExpectNegative, rangeExpandSignExpectPositive, rootFinder
- use :: Sorting , only : sortIndex
- use :: String_Handling , only : operator(//)
- use :: Tables , only : table1D
- implicit none
- class (accretionFlowsShi2016), intent(inout), target :: self
- type (treeNode ), intent(inout) :: node
- double precision , allocatable , dimension(:) :: radiusOriginal , densityOriginal , &
- & velocityOriginal , radiusSingleStreamAnalyticPhysical , &
- & velocitySingleStreamAnalyticPhysical , densitySingleStreamAnalyticPhysical , &
- & expansionFactorHRTAScaled , radiusPhysical , &
- & velocityPhysical , densityPhysical
- integer (c_size_t ), allocatable , dimension(:) :: order
- class (nodeComponentBasic ), pointer :: basic
- type (interpolator ), allocatable :: interpolatorMassMultiStreamNewScaleHRTA
- class (table1D ), allocatable :: ratioRadiusTurnaroundVirialTable
- integer , parameter :: countRadii =1000
- integer , parameter :: countCompare = 10
- integer , parameter :: iterationMaximum = 30
- double precision , parameter :: expansionFactorRelativeInitial =1.0d-6
- double precision , parameter :: radiusMultiStreamFractionalSmall =1.0d-6
- double precision , parameter :: multistreamToleranceRelative =5.0d-2
- double precision :: expansionFactorOriginal , timeInitialScaled , &
- & radiusInitialScaled , radiusGrowthRateInitialScaled , &
- & overdensityMinimumScaled , overdensityMaximumScaled , &
- & bigA , bigB , &
- & bigC , expansionFactorScaledInitial , &
- & massVirialOriginal , radiusVirialOriginal , &
- & growthIndex , radiusScaleHRTA , &
- & radiusMultistreamMinimumNewScaledHRTA , radiusMultistreamMaximumNewScaledHRTA, &
- & radiusCompareMinimumScaledHRTA , radiusCompareMaximumScaledHRTA , &
- & massMultiStream , massMultiStreamNew , &
- & changeRelative , changeRelativeMaximum , &
- & radiusSplashbackPhysical , h
- integer :: i , j , &
- & iTurnaround , iFirstZero , &
- & iVirial , iteration
- type (rootFinder ) :: finder
- logical :: firstZeroFound , multistreamConverged
- character (len=12 ) :: label
-
- ! Extract basic properties.
- basic => node%basic()
- ! The growth index specifies the profile of the initial mass perturbation, δMᵢ/Mᵢ ∝ M^{-1/s}, or, equivalently, the growth rate,
- ! M(t) ∝ aˢ.
- growthIndex=+self%darkMatterHaloMassAccretionHistory_%massAccretionRate(node, basic%time()) &
- & /self%cosmologyFunctions_ %expansionRate ( self%cosmologyFunctions_%expansionFactor(basic%time())) &
- & / basic%mass()
- ! Determine if we need to updated the scaled solution.
- if ( &
- & basic%time() /= self%timePreviousPhysical &
- & .or. &
- & growthIndex /= self%growthIndexPrevious &
- & ) then
- ! Time has changed - we must recompute the scale-free solution.
- self%timePreviousPhysical=basic%time()
- self%growthIndexPrevious =growthIndex
- self%massPreviousPhysical=-huge(0.0d0)
- if (allocated(self%overdensityScaled )) deallocate(self%overdensityScaled )
- if (allocated(self%radiusScaled )) deallocate(self%radiusScaled )
- if (allocated(self%radiusGrowthRateScaled )) deallocate(self%radiusGrowthRateScaled )
- if (allocated(self%expansionFactorHRTAScaled )) deallocate(self%expansionFactorHRTAScaled )
- if (allocated(self%radiusComovingInitialOriginal)) deallocate(self%radiusComovingInitialOriginal)
- if (allocated(self%massEnclosedInitialOriginal )) deallocate(self%massEnclosedInitialOriginal )
- if (allocated(self%radiusTurnaroundScaled )) deallocate(self%radiusTurnaroundScaled )
- if (allocated(self%timeTurnaroundScaled )) deallocate(self%timeTurnaroundScaled )
- if (allocated(self%radiusOrderedOriginal )) deallocate(self%radiusOrderedOriginal )
- if (allocated(self%massShellOrderedOriginal )) deallocate(self%massShellOrderedOriginal )
- if (allocated(self%massEnclosedOrderedOriginal )) deallocate(self%massEnclosedOrderedOriginal )
- if (allocated(self%densityOrderedOriginal )) deallocate(self%densityOrderedOriginal )
- if (allocated(self%timeHRTAScaled )) deallocate(self%timeHRTAScaled )
- allocate(self%overdensityScaled (countRadii))
- allocate(self%radiusScaled (countRadii))
- allocate(self%radiusGrowthRateScaled (countRadii))
- allocate(self%expansionFactorHRTAScaled (countRadii))
- allocate(self%radiusComovingInitialOriginal(countRadii))
- allocate(self%massEnclosedInitialOriginal (countRadii))
- allocate(self%radiusTurnaroundScaled (countRadii))
- allocate(self%timeTurnaroundScaled (countRadii))
- allocate(self%radiusOrderedOriginal (countRadii))
- allocate(self%massShellOrderedOriginal (countRadii))
- allocate(self%massEnclosedOrderedOriginal (countRadii))
- allocate(self%densityOrderedOriginal (countRadii))
- allocate(self%timeHRTAScaled (countRadii))
- allocate( order (countRadii))
- allocate( expansionFactorHRTAScaled (countRadii))
- allocate( radiusOriginal (countRadii))
- ! Create a module-scope pointer to self for use in ODE solver functions.
- self_ => self
- ! Find the ratio of turnaround to virial radius.
- call self%sphericalCollapseSolver_%radiusTurnaround(time=basic%time(),tableStore=.false.,radiusTurnaround_=ratioRadiusTurnaroundVirialTable)
- ratioRadiusTurnaroundVirial=ratioRadiusTurnaroundVirialTable%interpolate(basic%time())
- ! Compute the scaled cosmological constant parameter ("w" in the notation of Shi 2016, Table A1).
- self%cosmologicalConstantScaled=+1.0d0/self%cosmologyFunctions_ %OmegaMatterEpochal(basic%time()) &
- & -1.0d0
- ! Compute expansion factor, and scaled expansion factor ("y" in the notation of Shi 2016, Table A1).
- expansionFactorOriginal =+self%cosmologyFunctions_ %expansionFactor (basic%time())
- self%expansionFactorScaled =+self%cosmologicalConstantScaled **(1.0d0/3.0d0) &
- & *expansionFactorOriginal
- ! Find the scaled time at the current epoch.
- self%timeNowScaled =+sqrt(1.0d0-self %cosmologyFunctions_%OmegaMatterEpochal(basic%time() )) & ! Equation A5 from Shi (2016).
- & * self %cosmologyFunctions_%expansionRate (expansionFactorOriginal) &
- & * basic%time ( )
- ! Choose an initial epoch and (scaled) radius for the ODE. This is chosen to be an expansion factor much smaller than the
- ! present day such that the perturbations will be small.
- expansionFactorScaledInitial =+ expansionFactorRelativeInitial &
- & *self%expansionFactorScaled
- radiusInitialScaled =+ expansionFactorScaledInitial
- ! Choose a value of the scaled overdensity, "β" in the notation of Shi (2016), identifying a mass shell. We avoid β=1 because such a shell never collapses.
- overdensityMinimumScaled =+1.001d0
- ! Choose a maximum overdensity. We use β=10 here as it's more than sufficient to allow most of the accretion stream to be captured,
- overdensityMaximumScaled =+1.000d1
- ! Build array of overdensities.
- self%overdensityScaled =Make_Range(overdensityMaximumScaled,overdensityMinimumScaled,countRadii,rangeTypeLogarithmic)
- ! Build a root finder which will be used for finding the time at half the turnaround radius.
- finder=rootFinder( &
- & rootFunction =halfRadiusTurnAroundRoot, &
- & toleranceAbsolute=0.0d+0 , &
- & toleranceRelative=1.0d-3 &
- & )
- ! Find turnaround radius and mass as a function of time, along with epoch at which half the turnaround radius is
- ! reached. This is all in scale-free units.
- do i=1,countRadii
- ! Find the epoch of turnaround.
- self%radiusTurnaroundScaled(i)=+2.0d0**(2.0d0/3.0d0) & ! Equation A9 from Shi (2016).
- & * sqrt( self%overdensityScaled(i) ) &
- & *sin((1.0d0/3.0d0)*asin(1.0d0/self%overdensityScaled(i)**1.5d0))
- bigA =+1.0d0 & ! Text after equation of A12 from Shi (2016).
- & /self%radiusTurnaroundScaled(i)**3
- bigB =+2.0d0 & ! Text after equation of A12 from Shi (2016).
- & / ( +3.0d0+sqrt(1.0d0+4.0d0*bigA) )
- bigC =+ sqrt(1.0d0+4.0d0*bigA) & ! Text after equation of A12 from Shi (2016).
- & / (bigA-0.5d0+sqrt(1.0d0+4.0d0*bigA)/2.0d0)
- self%timeTurnaroundScaled (i)=+ ( +1.0d0+sqrt(1.0d0+4.0d0*bigA) ) & ! Equation A12 of Shi (2016).
- & /sqrt(bigA-0.5d0+sqrt(1.0d0+4.0d0*bigA)/2.0d0) &
- & *( &
- & +Elliptic_Integral_Pi(bigC,bigB) &
- & -Elliptic_Integral_K (bigC ) &
- & )
- ! Find the epoch corresponding to reaching half of the turnaround radius.
- radiusTurnaroundScaled__=self%radiusTurnaroundScaled(i)
- timeTurnaroundScaled__ =self%timeTurnaroundScaled (i)
- call finder%rangeExpand( &
- & rangeExpandUpward =1.1d0 , &
- & rangeExpandDownward =0.5d0 , &
- & rangeExpandType =rangeExpandMultiplicative , &
- & rangeExpandUpwardSignExpect =rangeExpandSignExpectNegative, &
- & rangeExpandDownwardSignExpect=rangeExpandSignExpectPositive, &
- & rangeDownwardLimit =timeTurnaroundScaled__ &
- & )
- self%timeHRTAScaled (i)=finder%find(rootGuess=timeTurnaroundScaled__)
- self%expansionFactorHRTAScaled(i)=expansionFactorFromTimeScaled(self%timeHRTAScaled(i))
- end do
- ! Find enclosed masses. Note that this is found using the self-similarity assumption that the mass of a shell should scale
- ! as the expansion factor at "half-radius-turnaround" to the power of the growth index, i.e. M ∝ uₕᵣₜₐˢ. In regions
- ! where the cosmological constant is negligible (high overdensities, which collapse at early times), this will also give
- ! the expected scaling with overdensity, M ∝ β⁻ˢ. But, at lower overdensities, which collapse when the
- ! cosmological constant is non-negligible, the scaling with overdensity will change. What we do here seems to be consistent
- ! with what Shi (2016) assumes, and has the nice feature that it ensures the mass-epoch relation is the simple scale-free
- ! expectation at all times.
- expansionFactorHRTAScaled=expansionFactorFromTimeScaled(self%timeHRTAScaled)
- self%massEnclosedInitialOriginal =(expansionFactorHRTAScaled/self%expansionFactorScaled)**growthIndex
- self%radiusComovingInitialOriginal = self%massEnclosedInitialOriginal **(1.0d0/3.0d0)
- ! Build a variety of interpolators for different radii and masses as functions of scaled time.
- if (allocated(self%interpolatorRadiusTurnaroundOriginal )) deallocate(self%interpolatorRadiusTurnaroundOriginal )
- if (allocated(self%interpolatorRadiusHRTAOriginal )) deallocate(self%interpolatorRadiusHRTAOriginal )
- if (allocated(self%interpolatorRadiusComovingInitialOriginal )) deallocate(self%interpolatorRadiusComovingInitialOriginal )
- if (allocated(self%interpolatorMassTurnaroundScaled )) deallocate(self%interpolatorMassTurnaroundScaled )
- if (allocated(self%interpolatorMassHRTAOriginal )) deallocate(self%interpolatorMassHRTAOriginal )
- if (allocated(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled)) deallocate(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled)
- allocate(self%interpolatorRadiusTurnaroundOriginal )
- allocate(self%interpolatorRadiusHRTAOriginal )
- allocate(self%interpolatorRadiusComovingInitialOriginal )
- allocate(self%interpolatorMassTurnaroundScaled )
- allocate(self%interpolatorMassHRTAOriginal )
- allocate(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled)
- self%interpolatorRadiusTurnaroundOriginal =interpolator( self%timeTurnaroundScaled , self%radiusTurnaroundScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0) )
- self%interpolatorRadiusComovingInitialOriginal =interpolator( self%timeTurnaroundScaled , self%radiusComovingInitialOriginal )
- self%interpolatorMassTurnaroundScaled =interpolator( self%timeTurnaroundScaled , self%massEnclosedInitialOriginal )
- self%interpolatorRadiusHRTAOriginal =interpolator( self%timeHRTAScaled , self%radiusTurnaroundScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)/ratioRadiusTurnaroundVirial )
- self%interpolatorMassHRTAOriginal =interpolator( self%timeHRTAScaled , self%massEnclosedInitialOriginal )
- self%interpolatorScaleFactorHalfRadiusTurnaroundScaled=interpolator(Array_Reverse(self%overdensityScaled ),Array_Reverse(self%expansionFactorHRTAScaled ))
- ! Compute present epoch turnaround and half-turnaround radii.
- self%radiusTurnaroundNowOriginal=self%interpolatorRadiusTurnaroundOriginal%interpolate(self%timeNowScaled)
- self%radiusHRTANowOriginal =self%interpolatorRadiusHRTAOriginal %interpolate(self%timeNowScaled)
- ! Make an estimate of the splashback radius (in units of the turnaround radius, using eqn. 2 of Shi 2016 which is for an
- ! Einstein-de Sitter universe). An approximate value is acceptable here as this is used only on the first iteration. It's
- ! not clear if this is precisely what Shi (2016) chose, but it should not matter.
- if (growthIndex <= 1.5d0) then
- self%radiusSplashbackTurnaround=+1.0d0/3.0d0**(2.0d0/3.0d0+2.0d0*growthIndex/9.0d0)
- else
- self%radiusSplashbackTurnaround=+1.0d0/(1.0d0+4.0d0*(4.0d0*growthIndex/9.0d0+1.0d0/3.0d0)/sqrt(Pi))
- end if
- ! Find the splashback radius in units of the present day half-turnaround radius.
- self%ratioRadiusSplashbackHRTA=self%radiusSplashbackTurnaround*self%radiusTurnaroundNowOriginal/self%radiusHRTANowOriginal
- ! For the first iteration, adopt a mass profile solution in the multi-stream region that has the form f(x)=x (Shi 2016,
- ! section 2.1).
- if (allocated(self%interpolatorMassMultiStreamScaleHRTA)) deallocate(self%interpolatorMassMultiStreamScaleHRTA)
- allocate(self%interpolatorMassMultiStreamScaleHRTA)
- self%radiusMultistreamMinimumScaledHRTA=radiusMultiStreamFractionalSmall*self%ratioRadiusSplashbackHRTA
- self%radiusMultistreamMaximumScaledHRTA= self%ratioRadiusSplashbackHRTA
- self%interpolatorMassMultiStreamScaleHRTA=interpolator([self%radiusMultistreamMinimumScaledHRTA,self%radiusMultistreamMaximumScaledHRTA],[self%radiusMultistreamMinimumScaledHRTA,self%radiusMultistreamMaximumScaledHRTA])
- ! Begin iterating to find a solution.
- iteration =0
- multistreamConverged=.false.
- do while (iteration < iterationMaximum .and. .not.multistreamConverged)
- iteration=iteration+1
- call displayIndent(var_str('multistream mass profile iteration ')//iteration,verbosity=verbosityLevelWorking)
- ! Iterate over all overdensity shells solving for their radial position and velocity at the present epoch.
- do i=1,countRadii
- call displayCounter(int(100.0d0*dble(i-1)/dble(countRadii)),isNew=i==1,verbosity=verbosityLevelWorking)
- ! Solve the dynamical ODEs to get the scaled radius at the final time.
- radiusComovingInitialOriginal=self%radiusComovingInitialOriginal(i)
- massEnclosedInitialOriginal =self%massEnclosedInitialOriginal (i)
- radiusGrowthRateInitialScaled=+sqrt( & ! Equation A8 from Shi (2016).
- & +1.0d0/ radiusInitialScaled &
- & + radiusInitialScaled **2 &
- & -3.0d0*self%overdensityScaled (i) &
- & /2.0d0 **(2.0d0/3.0d0) &
- & )
- timeInitialScaled =+2.0d0 & ! Equation A6 from Shi (2016).
- & /3.0d0 &
- & *asinh( expansionFactorScaledInitial **1.5d0)
- call radiusScaledSolver(timeInitialScaled,self%timeNowScaled,radiusInitialScaled,radiusGrowthRateInitialScaled,self%radiusScaled(i),self%radiusGrowthRateScaled(i))
- end do
- call displayCounterClear(verbosity=verbosityLevelWorking)
- ! Build an interpolator for the scaled radius as a function of overdensity.
- if (allocated(self%interpolatorRadiusScaled)) deallocate(self%interpolatorRadiusScaled)
- allocate(self%interpolatorRadiusScaled)
- self%interpolatorRadiusScaled=interpolator(Array_Reverse(self%overdensityScaled),Array_Reverse(self%radiusScaled))
- ! Construct the mass and density profile by ordering the shells in radius. Also find the splashback radius.
- radiusOriginal =abs(self%radiusScaled)*self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
- order =sortIndex(radiusOriginal)
- self%radiusSplashBackOriginal=-huge(0.0d0)
- firstZeroFound =.false.
- do i=1,countRadii
- ! Order radii.
- self%radiusOrderedOriginal(i)=radiusOriginal(order(i))
- ! Compute mass and density in this shell.
- if (order(i) == 1) then
- self%massShellOrderedOriginal(i)=self%massEnclosedInitialOriginal(order(i))
- self%densityOrderedOriginal (i)=self%massShellOrderedOriginal(i)*3.0d0/4.0d0/Pi/ self%radiusOrderedOriginal(i)**3
- else
- self%massShellOrderedOriginal(i)=self%massEnclosedInitialOriginal(order(i))-self%massEnclosedInitialOriginal(order(i)-1)
- self%densityOrderedOriginal (i)=self%massShellOrderedOriginal(i)*3.0d0/4.0d0/Pi/(self%radiusOrderedOriginal(i)**3-self%radiusOrderedOriginal(i-1)**3)
- end if
- ! Find the mass enclosed by this shell.
- self%massEnclosedOrderedOriginal(i)=sum(self%massShellOrderedOriginal(1:i))
- end do
- ! Find the splashback radius.
- do i=countRadii,1,-1
- firstZeroFound=firstZeroFound.or.self%radiusScaled(i) <= 0.0d0
- if (firstZeroFound .and. radiusOriginal(i) > self%radiusSplashBackOriginal) then
- self%radiusSplashbackOriginal= radiusOriginal(i)
- self%radiusSplashbackScaled =abs(self%radiusScaled (i))
- end if
- end do
- deallocate(radiusOriginal)
- ! Find the splashback radius in units of the turnaround radius, and the half-turnaround radius.
- self%radiusSplashbackTurnaround=self%radiusSplashBackOriginal/self%radiusTurnaroundNowOriginal
- self%ratioRadiusSplashbackHRTA =self%radiusSplashBackOriginal/self%radiusHRTANowOriginal
- ! Build a new interpolator for the mass profile in the multistream region, and compare it to the previous one.
- allocate(interpolatorMassMultiStreamNewScaleHRTA)
- interpolatorMassMultiStreamNewScaleHRTA=interpolator(self%radiusOrderedOriginal/self%radiusHRTANowOriginal,self%massEnclosedOrderedOriginal)
- radiusMultistreamMinimumNewScaledHRTA =self%radiusOrderedOriginal( 1)/self%radiusHRTANowOriginal
- radiusMultistreamMaximumNewScaledHRTA =self%radiusOrderedOriginal(countRadii)/self%radiusHRTANowOriginal
- radiusCompareMinimumScaledHRTA =max(radiusMultistreamMinimumNewScaledHRTA,self%radiusMultistreamMinimumScaledHRTA)
- radiusCompareMaximumScaledHRTA =min(radiusMultistreamMaximumNewScaledHRTA,self%radiusMultistreamMaximumScaledHRTA)
- changeRelativeMaximum =-huge(0.0d0)
- do j=1,countCompare
- radiusScaleHRTA =+ radiusCompareMinimumScaledHRTA &
- & +(radiusCompareMaximumScaledHRTA-radiusCompareMinimumScaledHRTA) &
- & *dble( j) &
- & /dble(countCompare)
- massMultiStream =self%interpolatorMassMultiStreamScaleHRTA %interpolate(radiusScaleHRTA)
- massMultiStreamNew = interpolatorMassMultiStreamNewScaleHRTA%interpolate(radiusScaleHRTA)
- changeRelative =+abs(massMultiStream-massMultiStreamNew) &
- & / (massMultiStream+massMultiStreamNew) &
- & /0.5d0
- changeRelativeMaximum=max(changeRelative,changeRelativeMaximum)
- end do
- multistreamConverged=changeRelativeMaximum <= multistreamToleranceRelative
- deallocate(interpolatorMassMultiStreamNewScaleHRTA)
- ! Replace the interpolator for the mass profile in the multistream region with the updated one.
- deallocate(self%interpolatorMassMultiStreamScaleHRTA)
- allocate (self%interpolatorMassMultiStreamScaleHRTA)
- self%interpolatorMassMultiStreamScaleHRTA=interpolator(self%radiusOrderedOriginal/self%radiusHRTANowOriginal,self%massEnclosedOrderedOriginal)
- self%radiusMultistreamMinimumScaledHRTA =self%radiusOrderedOriginal( 1)/self%radiusHRTANowOriginal
- self%radiusMultistreamMaximumScaledHRTA =self%radiusOrderedOriginal(countRadii)/self%radiusHRTANowOriginal
- write (label,'(e8.2)') changeRelativeMaximum
- call displayUnindent(var_str('done [fractional change = ')//trim(adjustl(label))//']',verbosity=verbosityLevelWorking)
- end do
- if (.not.multistreamConverged) call Error_Report('failed to reach convergence in the multistream region'//{introspection:location})
- end if
- ! Determine if the dimensionful solution needs to be updated.
- if (basic%mass() == self%massPreviousPhysical .and. growthIndex == self%growthIndexPrevious) return
- self%massPreviousPhysical=basic%mass()
- self%growthIndexPrevious =growthIndex
- ! Compute properties along the stream.
- allocate(radiusOriginal (countRadii))
- allocate(densityOriginal (countRadii))
- allocate(velocityOriginal(countRadii))
- !! Find the radii and velocities in the stream.
- radiusOriginal =self%radiusScaled *self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
- velocityOriginal=self%radiusGrowthRateScaled*self%radiusComovingInitialOriginal/self%cosmologicalConstantScaled**(1.0d0/3.0d0)
- !! Compute densities.
- do i=1,countRadii
- if (i == 1) then
- densityOriginal(i)= self%massEnclosedInitialOriginal(i) *3.0d0/4.0d0/Pi/abs(radiusOriginal(i))**3
- else
- densityOriginal(i)=(self%massEnclosedInitialOriginal(i)-self%massEnclosedInitialOriginal(i-1))*3.0d0/4.0d0/Pi/abs(radiusOriginal(i) **3-radiusOriginal(i-1)**3)
- end if
- end do
- ! Find the shells at their turnaround radius and "half" of their turnaround radius (this defines the virial mass/radius), and
- ! also the shell which is about to make its first passage through zero radius.
- iVirial =-1
- iTurnaround=-1
- iFirstZero =-1
- do i=countRadii,1,-1
- if (iVirial < 0 .and. self%radiusGrowthRateScaled(i) < 0.0d0 .and. self%radiusScaled(i) <= self%radiusTurnaroundScaled(i)/ratioRadiusTurnaroundVirial) &
- & iVirial =i
- if (iTurnaround < 0 .and. self%radiusGrowthRateScaled(i) <= 0.0d0 ) &
- & iTurnaround=i
- if (iFirstZero < 0 .and. self%radiusGrowthRateScaled(i) < 0.0d0 .and. self%radiusScaled(i) < 0.0d0 ) &
- & iFirstZero =i+1
- end do
- ! Interpolate to get a more precise virial radius.
- h =+(1.0d0 /ratioRadiusTurnaroundVirial -self%radiusScaled(iVirial)/self%radiusTurnaroundScaled(iVirial)) &
- & /(self%radiusScaled(iVirial+1)/self%radiusTurnaroundScaled(iVirial+1)-self%radiusScaled(iVirial)/self%radiusTurnaroundScaled(iVirial))
- radiusVirialOriginal=+( &
- & +self%radiusScaled(iVirial )*self%radiusComovingInitialOriginal(iVirial )*(1.0d0-h) &
- & +self%radiusScaled(iVirial+1)*self%radiusComovingInitialOriginal(iVirial+1)* h &
- & ) &
- & /self%cosmologicalConstantScaled**(1.0d0/3.0d0)
- massVirialOriginal =+self%interpolatorMassHRTAOriginal %interpolate( self%timeNowScaled ) &
- & *self%interpolatorMassMultiStreamScaleHRTA%interpolate(radiusVirialOriginal/self%radiusHRTANowOriginal)
- ! Scale radii, velocities, and densities to the virial radius/mass.
- allocate(radiusPhysical (countRadii))
- allocate(velocityPhysical(countRadii))
- allocate(densityPhysical (countRadii))
- radiusPhysical =+radiusOriginal &
- & * self_%darkMatterHaloScale_%radiusVirial(node)/radiusVirialOriginal
- velocityPhysical=+velocityOriginal &
- & * self_%darkMatterHaloScale_%radiusVirial(node)/radiusVirialOriginal &
- & * self_%timeNowScaled /basic%time () &
- & *megaParsec &
- & /gigaYear &
- & /kilo
- densityPhysical =+densityOriginal &
- & * basic%mass ( )/massVirialOriginal &
- & /(self_%darkMatterHaloScale_%radiusVirial(node)/radiusVirialOriginal)**3
- ! Build interpolators into the infall stream.
- if (allocated(self%interpolatorDensityPhysical )) deallocate(self%interpolatorDensityPhysical )
- if (allocated(self%interpolatorVelocityPhysical)) deallocate(self%interpolatorVelocityPhysical)
- allocate(self%interpolatorDensityPhysical )
- allocate(self%interpolatorVelocityPhysical)
- self%interpolatorDensityPhysical =interpolator(radiusPhysical(iFirstZero:countRadii),densityPhysical (iFirstZero:countRadii),interpolationType=gsl_interp_cspline)
- self%interpolatorVelocityPhysical=interpolator(radiusPhysical(iFirstZero:countRadii),velocityPhysical(iFirstZero:countRadii),interpolationType=gsl_interp_cspline)
- self%radiusMinimumPhysical =radiusPhysical(iFirstZero)
- self%radiusMaximumPhysical =radiusPhysical(countRadii)
- ! Compute the analytic solution in the single stream regime.
- allocate(radiusSingleStreamAnalyticPhysical (countRadii-iVirial-1))
- allocate(densitySingleStreamAnalyticPhysical (countRadii-iVirial-1))
- allocate(velocitySingleStreamAnalyticPhysical(countRadii-iVirial-1))
- do i=iVirial+1,countRadii-1
- radiusSingleStreamAnalyticPhysical (i-iVirial)=+( &
- & +3.0d0 &
- & *basic%mass() &
- & /4.0d0 &
- & /Pi &
- & /self%cosmologyFunctions_%matterDensityEpochal(basic%time()) &
- & )**(1.0d0/3.0d0) &
- & *( &
- & +self%expansionFactorHRTAScaled(i) &
- & /self%expansionFactorScaled &
- & )**(growthIndex/3.0d0) &
- & *self%radiusScaled(i) &
- & /self%expansionFactorScaled
- densitySingleStreamAnalyticPhysical (i-iVirial)=+self%cosmologyFunctions_%matterDensityEpochal(basic%time()) &
- & *( &
- & +self%expansionFactorScaled &
- & /self%radiusScaled (i) &
- & )**3 &
- & /( &
- & +1.0d0 &
- & +3.0d0 &
- & /growthIndex &
- & /(self%interpolatorScaleFactorHalfRadiusTurnaroundScaled%derivative(self%overdensityScaled(i))/self%expansionFactorHRTAScaled(i)) &
- & *(self%interpolatorRadiusScaled %derivative(self%overdensityScaled(i))/self%radiusScaled (i)) &
- & )
- velocitySingleStreamAnalyticPhysical(i-iVirial)=+self%radiusGrowthRateScaled (i ) &
- & * radiusSingleStreamAnalyticPhysical(i-iVirial) &
- & /self%radiusScaled (i ) &
- & *sqrt(1.0d0-self%cosmologyFunctions_%OmegaMatterEpochal (basic%time())) &
- & * self%cosmologyFunctions_%HubbleParameterEpochal(basic%time())
- end do
- ! Check that the numerical solution matches the analytic solution in the single stream region.
- radiusSplashbackPhysical=+self %radiusSplashBackOriginal &
- & *self%darkMatterHaloScale_%radiusVirial (node) &
- & / radiusVirialOriginal
- do i=iVirial+1,countRadii-1
- if ( &
- & radiusPhysical(i) > radiusSplashBackPhysical &
- & .and. &
- & Values_Differ( &
- & radiusSingleStreamAnalyticPhysical(i-iVirial)/radiusSingleStreamAnalyticPhysical(iTurnaround-iVirial), &
- & radiusPhysical (i )/radiusPhysical (iTurnaround ), &
- & relTol=1.0d-6 &
- & ) &
- & ) &
- & call Error_Report('numerical and analytic solutions disagree in single stream region'//{introspection:location})
- end do
- return
- end subroutine shi2016Solve
-
- double precision function halfRadiusTurnAroundRoot(timeFinalScaled)
- !!{
- Root function used in finding the epoch at which a shell reaches a radius equal to half of its turnaround radius, $y^*$.
- !!}
- implicit none
- double precision, intent(in ) :: timeFinalScaled
- double precision :: radiusScaled , radiusGrowthRateScaled
-
- ! Integrate the dynamical equations governing the evolution of the shell radius starting from the turnaround time, I⋆, at
- ! which the radius is y⋆, and the rate of change of radius is zero (by definition).
- noShellCrossing=.true.
- call radiusScaledSolver(timeTurnaroundScaled__,timeFinalScaled,radiusTurnaroundScaled__,0.0d0,radiusScaled,radiusGrowthRateScaled)
- noShellCrossing=.false.
- halfRadiusTurnAroundRoot=+radiusScaled &
- & -radiusTurnaroundScaled__ &
- & /ratioRadiusTurnaroundVirial
- return
- end function halfRadiusTurnAroundRoot
-
- subroutine radiusScaledSolver(timeInitialScaled,timeFinalScaled,radiusInitialScaled,radiusGrowthRateInitialScaled,radiusScaled,radiusGrowthRateScaled)
- !!{
- Compute the scaled radius (and its growth rate) as a function of the initial state and final time.
- !!}
- use :: Interface_GSL , only : GSL_Success
- use :: Numerical_ODE_Solvers, only : odeSolver
- implicit none
- double precision , intent(in ) :: timeInitialScaled , timeFinalScaled , &
- & radiusInitialScaled , radiusGrowthRateInitialScaled
- double precision , intent( out) :: radiusScaled , radiusGrowthRateScaled
- double precision , dimension(2) :: odeVariables
- double precision , parameter :: odeToleranceAbsolute=0.0d0, odeToleranceRelative =1.0d-9
- type (odeSolver) :: solver
- double precision :: timeScaled
-
- solver =odeSolver(2_c_size_t,dynamicalODEs,toleranceAbsolute=odeToleranceAbsolute,toleranceRelative=odeToleranceRelative)
- odeVariables=[radiusInitialScaled,radiusGrowthRateInitialScaled]
- timeScaled = timeInitialScaled
- call solver%solve(timeScaled,timeFinalScaled,odeVariables)
- radiusScaled =odeVariables(1)
- radiusGrowthRateScaled=odeVariables(2)
- return
- end subroutine radiusScaledSolver
-
- integer function dynamicalODES(timeScaled,odeVariables,odeVariablesGrowthRate)
- !!{
- The dynamical equation describing the motion of a shell of matter in scaled variables \citep[][eqn.~A7]{shi_outer_2016}.
- !!}
- use :: Interface_GSL, only : GSL_Success
- implicit none
- double precision , intent(in ) :: timeScaled
- double precision, dimension(:), intent(in ) :: odeVariables
- double precision, dimension(:), intent( out) :: odeVariablesGrowthRate
- double precision :: radiusScaled , radiusGrowthRateScaled, radiusHRTA, radiusSplashback, radius, massHRTA, massEnclosedRatio
- !$GLC attributes unused :: timeScaled
-
- ! Extract ODE variables into named variables for clarity.
- radiusScaled =odeVariables(1)
- radiusGrowthRateScaled=odeVariables(2)
- ! Determine the ratio of the mass enclosed within the current radius to the mass enclosed within the shell at the initial
- ! time.
- if (noShellCrossing .or. timeScaled < self_%timeHRTAScaled(1)) then
- ! If shell crossing is being ignored, or if the current time is less than the earliest time for which we have the
- ! half-turnaround radius tabulated, simply assume a mass ratio of unity.
- massEnclosedRatio=1.0d0
- else
- ! Our solution for the mass in the multistream regime is the self-similar solution expressed in units of the radius of the
- ! shell at its half-turnaround radius, and the mass within that shell (assuming no shell crossing). Compute that radius and
- ! mass at the present epoch.
- radiusHRTA=self_%interpolatorRadiusHRTAOriginal%interpolate(timeScaled)
- massHRTA =self_%interpolatorMassHRTAOriginal %interpolate(timeScaled)
- ! Find the current splashback radius by scaling the ratio of splashback to half-turnaround radius to the current epoch.
- radiusSplashback=+self_%ratioRadiusSplashbackHRTA &
- & * radiusHRTA
- ! Find the radius of the current shell in unscaled units.
- radius=abs(radiusScaled)*radiusComovingInitialOriginal/self_%cosmologicalConstantScaled**(1.0d0/3.0d0)
- ! Determine where in the stream our shell is.
- if (radius < radiusSplashback) then
- ! The shell is within the splashback radius.
- if (radius > self_%radiusMultistreamMinimumScaledHRTA*radiusHRTA) then
- ! Shell is outside the minimum radius that we have tabulated for the multistream region. Therefore, simply
- ! interpolate to get the mass ratio.
- massEnclosedRatio=+massHRTA &
- & /massEnclosedInitialOriginal &
- & *self_%interpolatorMassMultiStreamScaleHRTA%interpolate(radius/radiusHRTA )
- else
- ! Shell is inside the minimum radius that we have tabulated for the multistream region. In this region we assume that
- ! the mass enclosed by the innermost tabulated point is distributed with uniform density. The mass enclosed therefore
- ! grows as the cube of radius.
- massEnclosedRatio=+massHRTA &
- & /massEnclosedInitialOriginal &
- & *self_%interpolatorMassMultiStreamScaleHRTA%interpolate(self_%radiusMultistreamMinimumScaledHRTA) &
- & *(radius/radiusHRTA/self_%radiusMultistreamMinimumScaledHRTA)**3
- end if
- else
- ! Mass is outside the splashback radius, so no shell crossing has occurred. The mass ratio is therefore unity.
- massEnclosedRatio=1.0d0
- end if
- end if
- ! Set ODE rates of change.
- !! Radius rate of change is just the velocity.
- odeVariablesGrowthRate(1)=+radiusGrowthRateScaled
- !! Velocity rate of change is given by equation (A7) of Shi (2016).
- if (radiusScaled == 0.0d0) then
- odeVariablesGrowthRate(2)=+0.0d0
- else
- odeVariablesGrowthRate(2)=-0.5d0*massEnclosedRatio*sign(1.0d0,radiusScaled)/radiusScaled**2 &
- & + radiusScaled
- end if
- dynamicalODES=GSL_Success
- return
- end function dynamicalODES
-
- elemental double precision function expansionFactorFromTimeScaled(timeScaled) result(expansionFactor)
- !!{
- Compute the scaled expansion factor from the scaled time using equation~(A6) of \cite{shi_outer_2016}.
- !!}
- implicit none
- double precision, intent(in ) :: timeScaled
-
- expansionFactor=sinh(1.5d0*timeScaled)**(2.0d0/3.0d0)
- return
- end function expansionFactorFromTimeScaled
-
diff --git a/source/structure_formation.accretion_flow.correlation_function.F90 b/source/structure_formation.accretion_flow.correlation_function.F90
deleted file mode 100644
index 83a5b798d6..0000000000
--- a/source/structure_formation.accretion_flow.correlation_function.F90
+++ /dev/null
@@ -1,152 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 .
-
- !!{
- An accretion flow class which models the accretion flow using the 2-halo correlation function.
- !!}
-
- use :: Correlation_Functions_Two_Point, only : correlationFunctionTwoPointClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
- use :: Dark_Matter_Halo_Biases , only : darkMatterHaloBiasClass
-
- !![
-
- An accretion flow class which models the accretion flow using the 2-halo correlation function.
-
- !!]
- type, extends(accretionFlowsClass) :: accretionFlowsCorrelationFunction
- !!{
- An accretion flow class which models the accretion flow using the 2-halo correlation function.
- !!}
- private
- class(correlationFunctionTwoPointClass), pointer :: correlationFunctionTwoPoint_ => null()
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class(darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_ => null()
- contains
- final :: correlationFunctionDestructor
- procedure :: density => correlationFunctionDensity
- procedure :: velocity => correlationFunctionVelocity
- end type accretionFlowsCorrelationFunction
-
- interface accretionFlowsCorrelationFunction
- !!{
- Constructors for the {\normalfont \ttfamily correlationFunction} accretion flows class.
- !!}
- module procedure correlationFunctionConstructorParameters
- module procedure correlationFunctionConstructorInternal
- end interface accretionFlowsCorrelationFunction
-
-contains
-
- function correlationFunctionConstructorParameters(parameters) result(self)
- !!{
- Constructor for the {\normalfont \ttfamily correlationFunction} accretion flow class that takes a parameter set as input.
- !!}
- use :: Input_Parameters, only : inputParameter, inputParameters
- implicit none
- type (accretionFlowsCorrelationFunction) :: self
- type (inputParameters ), intent(inout) :: parameters
- class(correlationFunctionTwoPointClass ), pointer :: correlationFunctionTwoPoint_
- class(cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class(darkMatterHaloBiasClass ), pointer :: darkMatterHaloBias_
-
- !![
-
-
-
- !!]
- self=accretionFlowsCorrelationFunction(cosmologyFunctions_,correlationFunctionTwoPoint_,darkMatterHaloBias_)
- !![
-
-
-
-
- !!]
- return
- end function correlationFunctionConstructorParameters
-
- function correlationFunctionConstructorInternal(cosmologyFunctions_,correlationFunctionTwoPoint_,darkMatterHaloBias_) result(self)
- !!{
- Internal constructor for the {\normalfont \ttfamily correlationFunction} accretion flows class.
- !!}
- implicit none
- type (accretionFlowsCorrelationFunction) :: self
- class(correlationFunctionTwoPointClass ), intent(in ), target :: correlationFunctionTwoPoint_
- class(cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class(darkMatterHaloBiasClass ), intent(in ), target :: darkMatterHaloBias_
- !![
-
- !!]
-
- return
- end function correlationFunctionConstructorInternal
-
- subroutine correlationFunctionDestructor(self)
- !!{
- Destructor for the {\normalfont \ttfamily correlationFunction} accretion flows class.
- !!}
- implicit none
- type(accretionFlowsCorrelationFunction), intent(inout) :: self
-
- !![
-
-
-
- !!]
- return
- end subroutine correlationFunctionDestructor
-
- double precision function correlationFunctionDensity(self,node,radius)
- !!{
- Compute the density of the accretion flow at the given radius.
- !!}
- use :: Galacticus_Nodes, only : nodeComponentBasic
- implicit none
- class (accretionFlowsCorrelationFunction), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- class (nodeComponentBasic ), pointer :: basic
- double precision :: time
-
- basic => node %basic()
- time = basic%time ()
- correlationFunctionDensity = +( &
- & +1.0d0 &
- & +self%darkMatterHaloBias_ %bias (node,radius ) &
- & *self%correlationFunctionTwoPoint_%correlation ( radius,time) &
- & ) &
- & * self%cosmologyFunctions_ %matterDensityEpochal( time)
- return
- end function correlationFunctionDensity
-
- double precision function correlationFunctionVelocity(self,node,radius)
- !!{
- Compute the velocity of the accretion flow at the given radius.
- !!}
- use :: Error, only : Error_Report
- implicit none
- class (accretionFlowsCorrelationFunction), intent(inout) :: self
- type (treeNode ), intent(inout) :: node
- double precision , intent(in ) :: radius
- !$GLC attributes unused :: self, node, radius
-
- correlationFunctionVelocity=0.0d0
- call Error_Report('velocity is currently unsupported'//{introspection:location})
- return
- end function correlationFunctionVelocity
diff --git a/source/structure_formation.accretion_flow.utilities.F90 b/source/structure_formation.accretion_flow.utilities.F90
deleted file mode 100644
index 88e4b55932..0000000000
--- a/source/structure_formation.accretion_flow.utilities.F90
+++ /dev/null
@@ -1,222 +0,0 @@
-!! Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-!! 2019, 2020, 2021, 2022, 2023, 2024
-!! 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 of globally-accessible functions supporting the {\normalfont \ttfamily accretionFlows} class.
-!!}
-
-module Accretion_Flows_Utilities
- !!{
- Provides globally-accessible functions supporting the {\normalfont \ttfamily accretionFlows} class.
- !!}
- private
- public :: accretionFlowsConstruct , accretionFlowsDensity , accretionFlowsDestruct, accretionFlowsDeepCopy, &
- & accretionFlowsDeepCopyReset, accretionFlowsDeepCopyFinalize
-
- ! Module-scope pointer to our accretionFlow object. This is used for reference counting so that debugging information is consistent
- ! between the increments and decrements.
- class(*), pointer :: accretionFlows__
- !$omp threadprivate(accretionFlows__)
-
-contains
-
- !![
-
- accretionFlowsConstruct
- void
- Input_Parameters, only : inputParameters
- type (inputParameters), intent(inout), target :: parameters
- class(* ), intent( out), pointer :: accretionFlows_
-
- !!]
- subroutine accretionFlowsConstruct(parameters,accretionFlows_)
- !!{
- Build a {\normalfont \ttfamily accretionFlowsClass} object from a given parameter set. This is a globally-callable function
- to allow us to subvert the class/module hierarchy.
- !!}
- use :: Error , only : Error_Report
- use :: Input_Parameters , only : inputParameter, inputParameters
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlows, accretionFlowsClass
- implicit none
- type (inputParameters), intent(inout), target :: parameters
- class(* ), intent( out), pointer :: accretionFlows_
- type (inputParameters) , pointer :: parametersCurrent
-
- parametersCurrent => parameters
- do while (.not.parametersCurrent%isPresent('accretionFlows').and.associated(parametersCurrent%parent))
- parametersCurrent => parametersCurrent%parent
- end do
- if (.not.parametersCurrent%isPresent('accretionFlows')) parametersCurrent => parameters
- accretionFlows__ => accretionFlows(parametersCurrent)
- select type (accretionFlows__)
- class is (accretionFlowsClass)
- !![
-
- !!]
- call accretionFlows__%autoHook()
- class default
- call Error_Report('accretionFlow must be of the "accretionFlowsClass" class'//{introspection:location})
- end select
- accretionFlows_ => accretionFlows__
- return
- end subroutine accretionFlowsConstruct
-
- !![
-
- accretionFlowsDensity
- double precision
- Galacticus_Nodes, only : treeNode
- class (* ), intent(inout) :: accretionFlows_
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- !!]
- double precision function accretionFlowsDensity(accretionFlows_,node,radius)
- !!{
- Perform the accretionFlow for a {\normalfont \ttfamily accretionFlowsClass} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : treeNode
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- implicit none
- class (* ), intent(inout) :: accretionFlows_
- type (treeNode), intent(inout) :: node
- double precision , intent(in ) :: radius
-
- select type (accretionFlows_)
- class is (accretionFlowsClass)
- accretionFlowsDensity=accretionFlows_%density(node,radius)
- class default
- accretionFlowsDensity=0.0d0
- call Error_Report('accretionFlow must be of the "accretionFlowsClass" class'//{introspection:location})
- end select
- return
- end function accretionFlowsDensity
-
- !![
-
- accretionFlowsDestruct
- void
- class(*), intent(inout), pointer :: accretionFlows_
-
- !!]
- subroutine accretionFlowsDestruct(accretionFlows_)
- !!{
- Destruct a {\normalfont \ttfamily accretionFlowsClass} object passed to us as an unlimited polymorphic object.
- !!}
- use :: Error , only : Error_Report
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- use :: Function_Classes
- use :: iso_varying_string
- implicit none
- class(*), intent(inout), pointer :: accretionFlows_
-
- accretionFlows__ => accretionFlows_
- select type (accretionFlows__)
- class is (accretionFlowsClass)
- !![
-
- !!]
- class default
- call Error_Report('accretionFlow must be of the "accretionFlowsClass" class'//{introspection:location})
- end select
- return
- end subroutine accretionFlowsDestruct
-
- !![
-
- accretionFlowsDeepCopyReset
- void
- class(*), intent(inout) :: self
-
- !!]
- subroutine accretionFlowsDeepCopyReset(self)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- implicit none
- class(*), intent(inout) :: self
-
- select type (self)
- class is (accretionFlowsClass)
- call self%deepCopyReset()
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine accretionFlowsDeepCopyReset
-
- !![
-
- accretionFlowsDeepCopyFinalize
- void
- class(*), intent(inout) :: self
-
- !!]
- subroutine accretionFlowsDeepCopyFinalize(self)
- !!{
- Finalize a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- implicit none
- class(*), intent(inout) :: self
-
- select type (self)
- class is (accretionFlowsClass)
- call self%deepCopyFinalize()
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine accretionFlowsDeepCopyFinalize
-
- !![
-
- accretionFlowsDeepCopy
- void
- class(*), intent(inout) :: self, destination
-
- !!]
- subroutine accretionFlowsDeepCopy(self,destination)
- !!{
- Perform a deep copy of galactic structure objects.
- !!}
- use :: Error , only : Error_Report
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- implicit none
- class(*), intent(inout) :: self, destination
-
- select type (self)
- class is (accretionFlowsClass)
- select type (destination)
- class is (accretionFlowsClass)
- call self%deepCopy(destination)
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- class default
- call Error_Report("unexpected class"//{introspection:location})
- end select
- return
- end subroutine accretionFlowsDeepCopy
-
-end module Accretion_Flows_Utilities
diff --git a/source/structure_formation.halo_mass_function.friends_of_friends_bias.F90 b/source/structure_formation.halo_mass_function.friends_of_friends_bias.F90
index 65a9b5569f..cbfb86586d 100644
--- a/source/structure_formation.halo_mass_function.friends_of_friends_bias.F90
+++ b/source/structure_formation.halo_mass_function.friends_of_friends_bias.F90
@@ -164,8 +164,10 @@ double precision function fofBiasDifferential(self,time,mass,node)
!!{
Return the differential halo mass function at the given time and mass.
!!}
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeComponentBasic, treeNode
+ use :: Galacticus_Nodes , only : nodeComponentBasic , treeNode
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Math, only : Pi
implicit none
class (haloMassFunctionFofBias), intent(inout), target :: self
@@ -180,6 +182,8 @@ Return the differential halo mass function at the given time and mass.
integer , parameter :: iterationCountMaximum =1000
type (treeNode ), pointer :: nodeWork
class (nodeComponentBasic ), pointer :: basic
+ class (massDistributionClass ), pointer :: massDistribution_
+ type (coordinateSpherical ) :: coordinates
integer :: iterationCount
double precision :: fractionalAccuracyParameter , &
& densityProfileLogarithmicSlope , &
@@ -234,7 +238,12 @@ Return the differential halo mass function at the given time and mass.
& *gradientRadiusHaloMass &
& /linkingLength
! Compute negative a logarithmic slope of the density profile at the outer edge of the halo.
- densityProfileLogarithmicSlope=+self%darkMatterProfileDMO_%densityLogSlope(nodeWork,radiusHalo)
+ coordinates = [radiusHalo,0.0d0,0.0d0]
+ massDistribution_ => self %darkMatterProfileDMO_%get (nodeWork )
+ densityProfileLogarithmicSlope = +massDistribution_ %densityGradientRadial(coordinates,logarithmic=.true.)
+ !![
+
+ !!]
! Compute absolute value of the rate of change of logarithmic mass at halo outer edge as the
! percolation critical probability changes.
numberDensityCritical =+numberDensityPercolation &
diff --git a/source/structure_formation.transfer_function.CAMB.F90 b/source/structure_formation.transfer_function.CAMB.F90
index 3e5de46537..04ae960b4e 100644
--- a/source/structure_formation.transfer_function.CAMB.F90
+++ b/source/structure_formation.transfer_function.CAMB.F90
@@ -146,11 +146,13 @@ Internal constructor for the \href{http://camb.info}{\normalfont \scshape CAMB}
self%transferFunctionReferenceAvailable = .false.
self%transferFunctionReference => null()
! Set the epoch time for this transfer function.
- self%time=self%cosmologyFunctions_%cosmicTime(self%cosmologyFunctions_%expansionFactorFromRedshift(redshift))
+ self%time = self%cosmologyFunctions_%cosmicTime(self%cosmologyFunctions_%expansionFactorFromRedshift(redshift))
! Set maximum wavenumber.
- self%wavenumberMaximum=+wavenumberMaximumLimit &
- & *self%cosmologyParameters_%hubbleConstant(units=hubbleUnitsLittleH)
- self%wavenumberMaximumReached=.false.
+ self%wavenumberMaximum = +wavenumberMaximumLimit &
+ & *self%cosmologyParameters_%hubbleConstant(units=hubbleUnitsLittleH)
+ self%wavenumberMaximumReached = .false.
+ ! Set an empty file name - this will be generated when we run CAMB,
+ self%fileName = ""
return
end function cambConstructorInternal
diff --git a/source/structure_formation.virial_density_contrast.percolation.utilities.F90 b/source/structure_formation.virial_density_contrast.percolation.utilities.F90
index 52935d475b..1a2963b0af 100644
--- a/source/structure_formation.virial_density_contrast.percolation.utilities.F90
+++ b/source/structure_formation.virial_density_contrast.percolation.utilities.F90
@@ -485,11 +485,15 @@ double precision function haloRadiusRootFunction(haloRadiusTrial)
Root function used to find the radius of a halo giving the correct bounding density.
!!}
use :: Calculations_Resets , only : Calculations_Reset
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Mass_Distributions , only : massDistributionClass
use :: Numerical_Constants_Math, only : Pi
implicit none
- double precision, intent(in ) :: haloRadiusTrial
- double precision :: scaleRadius , densityHaloRadius
-
+ double precision , intent(in ) :: haloRadiusTrial
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: scaleRadius , densityHaloRadius
+ type (coordinateSpherical ) :: coordinates
+
! Construct the current density contrast.
state(stateCount)%densityContrast=+3.0d0 &
& *state(stateCount)%massHalo &
@@ -505,7 +509,12 @@ Root function used to find the radius of a halo giving the correct bounding dens
end if
call Calculations_Reset(state(stateCount)%workNode)
! Compute density at the halo radius.
- densityHaloRadius=state(stateCount)%darkMatterProfileDMO_%density(state(stateCount)%workNode,haloRadiusTrial)
+ coordinates = [haloRadiusTrial,0.0d0,0.0d0]
+ massDistribution_ => state (stateCount)%darkMatterProfileDMO_%get (state(stateCount)%workNode )
+ densityHaloRadius = massDistribution_ %density( coordinates)
+ !![
+
+ !!]
! Find difference from target density.
haloRadiusRootFunction=state(stateCount)%boundingDensity-densityHaloRadius
return
diff --git a/source/tasks.evolve_forests.F90 b/source/tasks.evolve_forests.F90
index d8cb21f2ac..1b96f83092 100644
--- a/source/tasks.evolve_forests.F90
+++ b/source/tasks.evolve_forests.F90
@@ -508,7 +508,7 @@ subroutine evolveForestsPerform(self,status)
-
+
!!]
!$omp end critical(evolveForestsDeepCopy)
! Call routines to perform initialization which must occur for all threads if run in parallel.
diff --git a/source/tasks.halo_mass_function.F90 b/source/tasks.halo_mass_function.F90
index e9394da699..57ff4c787d 100644
--- a/source/tasks.halo_mass_function.F90
+++ b/source/tasks.halo_mass_function.F90
@@ -56,7 +56,6 @@ Implementation of a task which computes and outputs the halo mass function and r
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (linearGrowthClass ), pointer :: linearGrowth_ => null()
class (haloMassFunctionClass ), pointer :: haloMassFunction_ => null()
@@ -115,7 +114,6 @@ function haloMassFunctionConstructorParameters(parameters) result(self)
class (cosmologyParametersClass ), pointer :: cosmologyParameters_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (virialDensityContrastClass ), pointer :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (criticalOverdensityClass ), pointer :: criticalOverdensity_
class (linearGrowthClass ), pointer :: linearGrowth_
class (haloMassFunctionClass ), pointer :: haloMassFunction_
@@ -203,7 +201,6 @@ The HDF5 output group within which to write mass function data.
-
@@ -276,7 +273,6 @@ The HDF5 output group within which to write mass function data.The HDF5 output group within which to write mass function data.
-
@@ -351,7 +346,6 @@ function haloMassFunctionConstructorInternal(
& cosmologyParameters_ , &
& cosmologyFunctions_ , &
& virialDensityContrast_ , &
- & darkMatterProfileDMO_ , &
& criticalOverdensity_ , &
& linearGrowth_ , &
& haloMassFunction_ , &
@@ -379,7 +373,6 @@ function haloMassFunctionConstructorInternal(
class (cosmologyParametersClass ), intent(in ), target :: cosmologyParameters_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (virialDensityContrastClass ), intent(in ), target :: virialDensityContrast_
- class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
class (linearGrowthClass ), intent(in ), target :: linearGrowth_
class (haloMassFunctionClass ), intent(in ), target :: haloMassFunction_
@@ -405,7 +398,7 @@ function haloMassFunctionConstructorInternal(
type (inputParameters ), intent(in ), target :: parameters
integer :: i
!![
-
+
!!]
self%parameters=inputParameters(parameters)
@@ -433,7 +426,6 @@ subroutine haloMassFunctionDestructor(self)
-
@@ -480,8 +472,10 @@ subroutine haloMassFunctionPerform(self,status)
use :: Error , only : errorStatusSuccess
use :: Output_HDF5 , only : outputFile
use :: Galacticus_Nodes , only : mergerTree , nodeComponentBasic , nodeComponentDarkMatterProfile, treeNode
+ use :: Galactic_Structure_Options , only : componentTypeDarkMatterOnly , massTypeDark
use :: IO_HDF5 , only : hdf5Object
use, intrinsic :: ISO_C_Binding , only : c_size_t
+ use :: Mass_Distributions , only : massDistributionClass
use :: Node_Components , only : Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize
use :: Numerical_Constants_Astronomical , only : gigaYear , massSolar , megaParsec
use :: Numerical_Constants_Math , only : Pi
@@ -518,7 +512,6 @@ subroutine haloMassFunctionPerform(self,status)
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfileHalo
class (cosmologyParametersClass ), pointer :: cosmologyParameters_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (haloMassFunctionClass ), pointer :: haloMassFunction_ => null()
class (haloEnvironmentClass ), pointer :: haloEnvironment_ => null()
@@ -530,11 +523,15 @@ subroutine haloMassFunctionPerform(self,status)
class (darkMatterProfileShapeClass ), pointer :: darkMatterProfileShape_ => null()
class (darkMatterHaloMassAccretionHistoryClass), pointer :: darkMatterHaloMassAccretionHistory_ => null()
class (virialDensityContrastClass ), pointer :: virialDensityContrast_ => null()
- !$omp threadprivate(haloEnvironment_,cosmologyFunctions_,cosmologyParameters_,cosmologicalMassVariance_,haloMassFunction_,darkMatterHaloScale_,darkMatterProfileDMO_,unevolvedSubhaloMassFunction_,darkMatterHaloBias_,darkMatterProfileScaleRadius_,darkMatterProfileShape_,darkMatterHaloMassAccretionHistory_,virialDensityContrast_,criticalOverdensity_)
+ !$omp threadprivate(haloEnvironment_,cosmologyFunctions_,cosmologyParameters_,cosmologicalMassVariance_,haloMassFunction_,darkMatterHaloScale_,unevolvedSubhaloMassFunction_,darkMatterHaloBias_,darkMatterProfileScaleRadius_,darkMatterProfileShape_,darkMatterHaloMassAccretionHistory_,virialDensityContrast_,criticalOverdensity_)
+ class (massDistributionClass ), pointer , save :: massDistribution_ => null()
+ !$omp threadprivate(massDistribution_)
type (virialDensityContrastList ), allocatable , dimension(: ) :: virialDensityContrasts
type (mergerTree ), allocatable , target , save :: tree
!$omp threadprivate(tree)
type (integrator ), allocatable :: integrator_
+ type (inputParameters ), allocatable , save :: parameters
+ !$omp threadprivate(parameters)
integer (c_size_t ) :: iOutput , outputCount , &
& iMass , massCount , &
& iAlternate
@@ -678,7 +675,6 @@ subroutine haloMassFunctionPerform(self,status)
allocate(criticalOverdensity_ ,mold=self%criticalOverdensity_ )
allocate(haloMassFunction_ ,mold=self%haloMassFunction_ )
allocate(darkMatterHaloScale_ ,mold=self%darkMatterHaloScale_ )
- allocate(darkMatterProfileDMO_ ,mold=self%darkMatterProfileDMO_ )
allocate(unevolvedSubhaloMassFunction_ ,mold=self%unevolvedSubhaloMassFunction_ )
allocate(darkMatterHaloBias_ ,mold=self%darkMatterHaloBias_ )
allocate(darkMatterProfileScaleRadius_ ,mold=self%darkMatterProfileScaleRadius_ )
@@ -690,7 +686,7 @@ subroutine haloMassFunctionPerform(self,status)
end do
!$omp critical(taskHaloMassFunctionDeepCopy)
!![
-
+
@@ -699,13 +695,12 @@ subroutine haloMassFunctionPerform(self,status)
-
-
+
!!]
do iAlternate=1,size(self%virialDensityContrasts)
!![
@@ -715,6 +710,11 @@ subroutine haloMassFunctionPerform(self,status)
!!]
end do
!$omp end critical(taskHaloMassFunctionDeepCopy)
+ ! Call routines to perform initialization which must occur for all threads if run in parallel.
+ allocate(parameters)
+ parameters=inputParameters(self%parameters)
+ call parameters%parametersGroupCopy(self%parameters)
+ call Node_Components_Thread_Initialize(parameters)
! Build an integrator.
allocate(integrator_)
integrator_=integrator(subhaloMassFunctionIntegrand,toleranceRelative=1.0d-3,integrationRule=GSL_Integ_Gauss15)
@@ -759,6 +759,8 @@ subroutine haloMassFunctionPerform(self,status)
if (scaleIsSettable) call darkMatterProfileHalo%scaleSet(darkMatterProfileScaleRadius_%radius(tree%nodeBase))
! Set the node shape parameter.
if (shapeIsSettable) call darkMatterProfileHalo%shapeSet(darkMatterProfileShape_ %shape (tree%nodeBase))
+ ! Get the mass distribution.
+ massDistribution_ => tree%nodeBase%massDistribution(componentTypeDarkMatterOnly,massTypeDark)
! Compute bin interval.
massHaloBinMinimum=massHalo(iMass)*exp(-0.5*massHaloLogarithmicInterval)
massHaloBinMaximum=massHalo(iMass)*exp(+0.5*massHaloLogarithmicInterval)
@@ -793,13 +795,13 @@ subroutine haloMassFunctionPerform(self,status)
velocityVirial (iMass,iOutput)=darkMatterHaloScale_ %velocityVirial ( node=tree%nodeBase)
temperatureVirial (iMass,iOutput)=darkMatterHaloScale_ %temperatureVirial ( node=tree%nodeBase)
radiusVirial (iMass,iOutput)=darkMatterHaloScale_ %radiusVirial ( node=tree%nodeBase)
- velocityMaximum (iMass,iOutput)=darkMatterProfileDMO_ %circularVelocityMaximum ( node=tree%nodeBase)
+ velocityMaximum (iMass,iOutput)=massDistribution_ %velocityRotationCurveMaximum ( )
darkMatterProfileRadiusScale (iMass,iOutput)=darkMatterProfileHalo %scale ( )
if (self%includeMassAccretionRate) &
& massAccretionRate (iMass,iOutput)=darkMatterHaloMassAccretionHistory_%massAccretionRate ( time=outputTimes(iOutput),node=tree%nodeBase)
! Compute alternate mass definitions for halos.
do iAlternate=1,size(self%virialDensityContrasts)
- massAlternate(iAlternate,iMass,iOutput)=Dark_Matter_Profile_Mass_Definition(tree%nodeBase,virialDensityContrasts(iAlternate)%virialDensityContrast_%densityContrast(mass=massHalo(iMass),time=outputTimes(iOutput)),radius=radiusAlternate(iAlternate,iMass,iOutput),cosmologyParameters_=cosmologyParameters_,cosmologyFunctions_=cosmologyFunctions_,darkMatterProfileDMO_=darkMatterProfileDMO_,virialDensityContrast_=virialDensityContrast_)
+ massAlternate(iAlternate,iMass,iOutput)=Dark_Matter_Profile_Mass_Definition(tree%nodeBase,virialDensityContrasts(iAlternate)%virialDensityContrast_%densityContrast(mass=massHalo(iMass),time=outputTimes(iOutput)),radius=radiusAlternate(iAlternate,iMass,iOutput),cosmologyParameters_=cosmologyParameters_,cosmologyFunctions_=cosmologyFunctions_,virialDensityContrast_=virialDensityContrast_)
end do
! Integrate the unevolved subhalo mass function over the halo mass function to get the total subhalo mass function.
if (self%includeUnevolvedSubhaloMassFunction) &
@@ -807,6 +809,9 @@ subroutine haloMassFunctionPerform(self,status)
& log(massHalo(1)/subhaloMassMaximum ), &
& log( haloMassEffectiveInfinity) &
& )
+ !![
+
+ !!]
end do
!$omp end do
!$omp single
@@ -828,7 +833,6 @@ subroutine haloMassFunctionPerform(self,status)
-
@@ -841,6 +845,7 @@ subroutine haloMassFunctionPerform(self,status)
!!]
end do
deallocate(virialDensityContrasts)
+ call Node_Components_Thread_Uninitialize()
!$omp end parallel
! Open the group for output time information.
if (self%outputGroup == ".") then
diff --git a/source/tasks.halo_model_generate.F90 b/source/tasks.halo_model_generate.F90
index 560e164373..3fff1aec03 100644
--- a/source/tasks.halo_model_generate.F90
+++ b/source/tasks.halo_model_generate.F90
@@ -22,7 +22,6 @@
use :: Cosmology_Parameters , only : cosmologyParameters , cosmologyParametersClass
use :: Dark_Matter_Profile_Scales, only : darkMatterProfileScaleRadius, darkMatterProfileScaleRadiusClass
use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMO , darkMatterProfileDMOClass
- use :: Galactic_Structure , only : galacticStructureClass
use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
!![
@@ -40,7 +39,6 @@
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (conditionalMassFunctionClass ), pointer :: conditionalMassFunction_ => null()
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
class (randomNumberGeneratorClass ), pointer :: randomNumberGenerator_ => null()
double precision :: massMinimum , massMaximum
type (varying_string ) :: galaxyCatalogFileName , haloCatalogFileName
@@ -79,7 +77,6 @@ function haloModelGenerateConstructorParameters(parameters) result(self)
class (conditionalMassFunctionClass ), pointer :: conditionalMassFunction_
class (darkMatterProfileScaleRadiusClass), pointer :: darkMatterProfileScaleRadius_
class (randomNumberGeneratorClass ), pointer :: randomNumberGenerator_
- class (galacticStructureClass ), pointer :: galacticStructure_
type (inputParameters ), pointer :: parametersRoot
double precision :: massMinimum , massMaximum
type (varying_string ) :: galaxyCatalogFileName , haloCatalogFileName
@@ -126,9 +123,8 @@ function haloModelGenerateConstructorParameters(parameters) result(self)
-
!!]
- self=taskHaloModelGenerate(galaxyCatalogFileName,haloCatalogFileName,massMinimum,massMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,conditionalMassFunction_,darkMatterProfileScaleRadius_,randomNumberGenerator_,galacticStructure_,parametersRoot)
+ self=taskHaloModelGenerate(galaxyCatalogFileName,haloCatalogFileName,massMinimum,massMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,conditionalMassFunction_,darkMatterProfileScaleRadius_,randomNumberGenerator_,parametersRoot)
!![
@@ -137,12 +133,11 @@ function haloModelGenerateConstructorParameters(parameters) result(self)
-
!!]
return
end function haloModelGenerateConstructorParameters
- function haloModelGenerateConstructorInternal(galaxyCatalogFileName,haloCatalogFileName,massMinimum,massMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,conditionalMassFunction_,darkMatterProfileScaleRadius_,randomNumberGenerator_,galacticStructure_,parameters) result(self)
+ function haloModelGenerateConstructorInternal(galaxyCatalogFileName,haloCatalogFileName,massMinimum,massMaximum,cosmologyParameters_,cosmologyFunctions_,darkMatterProfileDMO_,conditionalMassFunction_,darkMatterProfileScaleRadius_,randomNumberGenerator_,parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily haloModelGenerate} task class which takes a parameter set as input.
!!}
@@ -156,10 +151,9 @@ function haloModelGenerateConstructorInternal(galaxyCatalogFileName,haloCatalogF
class (conditionalMassFunctionClass ), intent(in ), target :: conditionalMassFunction_
class (darkMatterProfileScaleRadiusClass), intent(in ), target :: darkMatterProfileScaleRadius_
class (randomNumberGeneratorClass ), intent(in ), target :: randomNumberGenerator_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
type (inputParameters ), intent(in ), target :: parameters
!![
-
+
!!]
self%parameters=inputParameters(parameters)
@@ -182,7 +176,6 @@ subroutine haloModelGenerateDestructor(self)
-
!!]
if(self%nodeComponentsInitialized) call Node_Components_Uninitialize()
return
@@ -196,14 +189,15 @@ subroutine haloModelGeneratePerform(self,status)
use :: Display , only : displayIndent , displayMessage , displayUnindent
use :: Galactic_Structure_Options, only : massTypeDark
use :: Calculations_Resets , only : Calculations_Reset
- use :: Error , only : errorStatusSuccess
+ use :: Error , only : errorStatusSuccess
use :: Galacticus_Nodes , only : nodeComponentBasic , nodeComponentDarkMatterProfile , treeNode
use :: IO_IRATE , only : irate
use :: ISO_Varying_String , only : varying_string
+ use :: Mass_Distributions , only : massDistributionClass
use :: Node_Components , only : Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize
use :: Numerical_Constants_Math , only : Pi
use :: Root_Finder , only : rangeExpandMultiplicative , rootFinder
- use :: String_Handling , only : operator(//)
+ use :: String_Handling , only : operator(//)
implicit none
class (taskHaloModelGenerate ), intent(inout), target :: self
integer , intent( out), optional :: status
@@ -215,6 +209,7 @@ subroutine haloModelGeneratePerform(self,status)
type (treeNode ), pointer :: node
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentDarkMatterProfile), pointer :: profile
+ class (massDistributionClass ), pointer :: massDistribution_
integer :: iHalo , galaxyCount , &
& numberSatelliteActual, iSatellite , &
& iAxis
@@ -292,15 +287,16 @@ subroutine haloModelGeneratePerform(self,status)
call Calculations_Reset( node )
call profile%scaleSet (self%darkMatterProfileScaleRadius_%radius(node ))
call Calculations_Reset( node )
+ massDistribution_ => self%darkMatterProfileDMO_%get(node)
do iSatellite=1,numberSatelliteActual
! Sample satellite galaxy mass.
- xSatellite =self%randomNumberGenerator_%uniformSample()*numberSatelliteMean
- massGalaxy_ =finderSatellite%find(rootGuess=self%massMinimum)
+ xSatellite = self%randomNumberGenerator_%uniformSample()*numberSatelliteMean
+ massGalaxy_ = finderSatellite%find(rootGuess=self%massMinimum)
! Sample galaxy radial position.
- xSatellite =self%randomNumberGenerator_%uniformSample ( )
- radiusSatellite =self%galacticStructure_ %radiusEnclosingMass(node,massFractional=xSatellite ,massType=massTypeDark)
+ xSatellite = self %randomNumberGenerator_%uniformSample ( )
+ radiusSatellite = massDistribution_ %radiusEnclosingMass(massFractional=xSatellite )
! Get circular velocity at this radius.
- velocitySatelliteCircular=self%darkMatterProfileDMO_ %circularVelocity (node, radiusSatellite )
+ velocitySatelliteCircular = massDistribution_%rotationCurve(radiusSatellite)
! Convert radial position to comoving coordinates.
radiusSatellite =radiusSatellite*(1.0d0+redshift)
! Sample galaxy angular position.
@@ -335,6 +331,9 @@ subroutine haloModelGeneratePerform(self,status)
! Store the satellite.
call galaxyAdd(massGalaxy_,positionSatellite,velocitySatellite)
end do
+ !![
+
+ !!]
end if
end do
message="Created "
diff --git a/source/tasks.halo_spin_distribution.F90 b/source/tasks.halo_spin_distribution.F90
index e09d1fcd4f..164770efb7 100644
--- a/source/tasks.halo_spin_distribution.F90
+++ b/source/tasks.halo_spin_distribution.F90
@@ -17,10 +17,10 @@
!! You should have received a copy of the GNU General Public License
!! along with Galacticus. If not, see .
- use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
- use :: Dark_Matter_Profiles_DMO, only : darkMatterProfileDMOClass
- use :: Halo_Spin_Distributions , only : haloSpinDistribution , haloSpinDistributionClass
- use :: Output_Times , only : outputTimes , outputTimesClass
+ use :: Cosmology_Functions , only : cosmologyFunctions , cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Halo_Spin_Distributions , only : haloSpinDistribution , haloSpinDistributionClass
+ use :: Output_Times , only : outputTimes , outputTimesClass
!![
@@ -35,7 +35,7 @@
class (haloSpinDistributionClass), pointer :: haloSpinDistribution_ => null()
class (outputTimesClass ), pointer :: outputTimes_ => null()
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_ => null()
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_ => null()
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
double precision :: spinMinimum , spinMaximum , &
& spinPointsPerDecade , haloMassMinimum
type (varying_string ) :: outputGroup
@@ -70,7 +70,7 @@ function haloSpinDistributionConstructorParameters(parameters) result(self)
class (haloSpinDistributionClass), pointer :: haloSpinDistribution_
class (outputTimesClass ), pointer :: outputTimes_
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
- class (darkMatterProfileDMOClass), pointer :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
type (inputParameters ), pointer :: parametersRoot
type (varying_string ) :: outputGroup
double precision :: spinMinimum , spinMaximum , &
@@ -128,20 +128,20 @@ Maximum spin for which the distribution function should be calculat
-
+
!!]
- self=taskHaloSpinDistribution(spinMinimum,spinMaximum,spinPointsPerDecade,haloMassMinimum,outputGroup,haloSpinDistribution_,outputTimes_,cosmologyFunctions_,darkMatterProfileDMO_,parametersRoot)
+ self=taskHaloSpinDistribution(spinMinimum,spinMaximum,spinPointsPerDecade,haloMassMinimum,outputGroup,darkMatterHaloScale_,haloSpinDistribution_,outputTimes_,cosmologyFunctions_,parametersRoot)
!![
-
+
!!]
return
end function haloSpinDistributionConstructorParameters
- function haloSpinDistributionConstructorInternal(spinMinimum,spinMaximum,spinPointsPerDecade,haloMassMinimum,outputGroup,haloSpinDistribution_,outputTimes_,cosmologyFunctions_,darkMatterProfileDMO_,parameters) result(self)
+ function haloSpinDistributionConstructorInternal(spinMinimum,spinMaximum,spinPointsPerDecade,haloMassMinimum,outputGroup,darkMatterHaloScale_,haloSpinDistribution_,outputTimes_,cosmologyFunctions_,parameters) result(self)
!!{
Constructor for the {\normalfont \ttfamily haloSpinDistribution} task class which takes a parameter set as input.
!!}
@@ -150,13 +150,13 @@ function haloSpinDistributionConstructorInternal(spinMinimum,spinMaximum,spinPoi
class (haloSpinDistributionClass), intent(in ), target :: haloSpinDistribution_
class (outputTimesClass ), intent(in ), target :: outputTimes_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
- class (darkMatterProfileDMOClass), intent(in ), target :: darkMatterProfileDMO_
+ class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
type (varying_string ), intent(in ) :: outputGroup
double precision , intent(in ) :: spinMinimum , spinMaximum , &
& spinPointsPerDecade , haloMassMinimum
type (inputParameters ), intent(in ), target :: parameters
!![
-
+
!!]
self%parameters=inputParameters(parameters)
@@ -176,7 +176,7 @@ subroutine haloSpinDistributionDestructor(self)
-
+
!!]
if (self%nodeComponentsInitialized) call Node_Components_Uninitialize()
return
@@ -236,7 +236,7 @@ subroutine haloSpinDistributionPerform(self,status)
! Iterate over spins.
do iSpin=1,spinCount
spin(iSpin)=exp(log(self%spinMinimum)+log(self%spinMaximum/self%spinMinimum)*dble(iSpin-1)/dble(spinCount-1))
- call nodeSpin%angularMomentumSet(spin(iSpin)*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterProfileDMO_))
+ call nodeSpin%angularMomentumSet(spin(iSpin)*Dark_Matter_Halo_Angular_Momentum_Scale(node,self%darkMatterHaloScale_))
! Evaluate the distribution.
if (self%haloMassMinimum <= 0.0d0) then
! No minimum halo mass specified - simply evaluate the spin distribution.
diff --git a/source/tasks.merging_halo_orbit_distribution.F90 b/source/tasks.merging_halo_orbit_distribution.F90
index 2763ae5021..0546c84161 100644
--- a/source/tasks.merging_halo_orbit_distribution.F90
+++ b/source/tasks.merging_halo_orbit_distribution.F90
@@ -17,14 +17,14 @@
!! You should have received a copy of the GNU General Public License
!! along with Galacticus. If not, see .
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
- use :: Virial_Orbits , only : virialOrbitClass
- use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
- use :: Spherical_Collapse_Accretion_Flows, only : accretionFlowsClass
- use :: Merger_Tree_Branching , only : mergerTreeBranchingProbabilityClass
- use :: Cosmological_Density_Field , only : criticalOverdensityClass , cosmologicalMassVarianceClass
- use :: Halo_Mass_Functions , only : haloMassFunctionClass
- use :: Cosmology_Functions , only : cosmologyFunctionsClass
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleClass
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass
+ use :: Virial_Orbits , only : virialOrbitClass
+ use :: Numerical_Random_Numbers , only : randomNumberGeneratorClass
+ use :: Merger_Tree_Branching , only : mergerTreeBranchingProbabilityClass
+ use :: Cosmological_Density_Field, only : criticalOverdensityClass , cosmologicalMassVarianceClass
+ use :: Halo_Mass_Functions , only : haloMassFunctionClass
+ use :: Cosmology_Functions , only : cosmologyFunctionsClass
!![
@@ -39,7 +39,7 @@
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_ => null()
class (virialOrbitClass ), pointer :: virialOrbit_ => null()
class (randomNumberGeneratorClass ), pointer :: randomNumberGenerator_ => null()
- class (accretionFlowsClass ), pointer :: accretionFlows_ => null()
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_ => null()
class (mergerTreeBranchingProbabilityClass), pointer :: mergerTreeBranchingProbability_ => null()
class (criticalOverdensityClass ), pointer :: criticalOverdensity_ => null()
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_ => null()
@@ -78,8 +78,8 @@ function mergingHaloOrbitDistributionConstructorParameters(parameters) result(se
class (cosmologyFunctionsClass ), pointer :: cosmologyFunctions_
class (virialOrbitClass ), pointer :: virialOrbit_
class (darkMatterHaloScaleClass ), pointer :: darkMatterHaloScale_
+ class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
class (randomNumberGeneratorClass ), pointer :: randomNumberGenerator_
- class (accretionFlowsClass ), pointer :: accretionFlows_
class (mergerTreeBranchingProbabilityClass), pointer :: mergerTreeBranchingProbability_
class (criticalOverdensityClass ), pointer :: criticalOverdensity_
class (cosmologicalMassVarianceClass ), pointer :: cosmologicalMassVariance_
@@ -156,24 +156,24 @@ function mergingHaloOrbitDistributionConstructorParameters(parameters) result(se
real
0..1
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
!!]
time=cosmologyFunctions_%cosmicTime(cosmologyFunctions_%expansionFactorFromRedshift(redshift))
- self=taskMergingHaloOrbitDistribution(time,velocityMinimum,velocityMaximum,countVelocitiesPerUnit,massMinimum,massMaximum,countMassesPerDecade,virialOrbit_,cosmologyFunctions_,darkMatterHaloScale_,accretionFlows_,mergerTreeBranchingProbability_,criticalOverdensity_,cosmologicalMassVariance_,haloMassFunction_,randomNumberGenerator_)
+ self=taskMergingHaloOrbitDistribution(time,velocityMinimum,velocityMaximum,countVelocitiesPerUnit,massMinimum,massMaximum,countMassesPerDecade,virialOrbit_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,mergerTreeBranchingProbability_,criticalOverdensity_,cosmologicalMassVariance_,haloMassFunction_,randomNumberGenerator_)
!![
-
+
@@ -183,7 +183,7 @@ function mergingHaloOrbitDistributionConstructorParameters(parameters) result(se
return
end function mergingHaloOrbitDistributionConstructorParameters
- function mergingHaloOrbitDistributionConstructorInternal(time,velocityMinimum,velocityMaximum,countVelocitiesPerUnit,massMinimum,massMaximum,countMassesPerDecade,virialOrbit_,cosmologyFunctions_,darkMatterHaloScale_,accretionFlows_,mergerTreeBranchingProbability_,criticalOverdensity_,cosmologicalMassVariance_,haloMassFunction_,randomNumberGenerator_) result(self)
+ function mergingHaloOrbitDistributionConstructorInternal(time,velocityMinimum,velocityMaximum,countVelocitiesPerUnit,massMinimum,massMaximum,countMassesPerDecade,virialOrbit_,cosmologyFunctions_,darkMatterHaloScale_,darkMatterProfileDMO_,mergerTreeBranchingProbability_,criticalOverdensity_,cosmologicalMassVariance_,haloMassFunction_,randomNumberGenerator_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily mergingHaloOrbitDistribution} task class.
!!}
@@ -192,7 +192,7 @@ function mergingHaloOrbitDistributionConstructorInternal(time,velocityMinimum,ve
class (virialOrbitClass ), intent(in ), target :: virialOrbit_
class (cosmologyFunctionsClass ), intent(in ), target :: cosmologyFunctions_
class (randomNumberGeneratorClass ), intent(in ), target :: randomNumberGenerator_
- class (accretionFlowsClass ), intent(in ), target :: accretionFlows_
+ class (darkMatterProfileDMOClass ), intent(in ), target :: darkMatterProfileDMO_
class (darkMatterHaloScaleClass ), intent(in ), target :: darkMatterHaloScale_
class (mergerTreeBranchingProbabilityClass), intent(in ), target :: mergerTreeBranchingProbability_
class (criticalOverdensityClass ), intent(in ), target :: criticalOverdensity_
@@ -203,7 +203,7 @@ function mergingHaloOrbitDistributionConstructorInternal(time,velocityMinimum,ve
& time
integer , intent(in ) :: countMassesPerDecade , countVelocitiesPerUnit
!![
-
+
!!]
self%redshift=self%cosmologyFunctions_%redshiftFromExpansionFactor(self%cosmologyFunctions_%expansionFactor(time))
@@ -223,7 +223,7 @@ subroutine mergingHaloOrbitDistributionDestructor(self)
-
+
@@ -248,12 +248,15 @@ subroutine mergingHaloOrbitDistributionPerform(self,status)
use :: HDF5_Access , only : hdf5Access
use :: IO_HDF5 , only : hdf5Object
use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic, rangeTypeLinear
+ use :: Mass_Distributions , only : massDistributionClass
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
implicit none
class (taskMergingHaloOrbitDistribution), intent(inout) , target :: self
integer , intent( out) , optional :: status
type (treeNode ) , pointer :: nodeHost , nodeSatellite
class (nodeComponentBasic ) , pointer :: basicHost , basicSatellite
type (mergerTree ) , pointer :: tree
+ class (massDistributionClass ) , pointer :: massDistribution_
double precision , dimension(: ), allocatable :: mass , velocity , &
& separation , density , &
& haloMassFunction
@@ -272,7 +275,8 @@ subroutine mergingHaloOrbitDistributionPerform(self,status)
& velocityWidthBin , overdensityCriticalGrowthRate , &
& overdensityCritical , rootVarianceLogarithmicGrowthRate , &
& barrierEffectiveGrowthRate
-
+ type (coordinateSpherical ) :: coordinates
+
call displayIndent('Begin task: merging halo orbit distribution')
! Build range of velocities.
countVelocities=int( (self%velocityMaximum-self%velocityMinimum)*dble(self%countVelocitiesPerUnit))+1
@@ -326,9 +330,14 @@ subroutine mergingHaloOrbitDistributionPerform(self,status)
call Calculations_Reset ( nodeHost)
! Compute the separation of the merging pairs (i.e. the virial radius of the primary halo), the density of the
! accretion flow at that separation, and the halo mass function.
- separation (iHost)=self%darkMatterHaloScale_%radiusVirial(nodeHost )
- density (iHost)=self%accretionFlows_ %density (nodeHost ,separation(iHost))
- haloMassFunction(iHost)=self%haloMassFunction_ %differential(self%time,massHost,nodeHost)
+ massDistribution_ => self %darkMatterProfileDMO_%get ( nodeHost)
+ separation (iHost) = self %darkMatterHaloScale_ %radiusVirial( nodeHost)
+ coordinates = [separation(iHost),0.0d0,0.0d0]
+ density (iHost) = massDistribution_ %density ( coordinates )
+ haloMassFunction (iHost) = self %haloMassFunction_ %differential(self%time,massHost ,nodeHost)
+ !![
+
+ !!]
! Iterate over satellite masses.
do iSatellite=1,countMasses
! Only consider satellites less (or equally) massive than their host.
diff --git a/source/tests.dark_matter_halo_radius_enclosing_mass.F90 b/source/tests.dark_matter_halo_radius_enclosing_mass.F90
index 7168b40fea..0d6391c0d8 100644
--- a/source/tests.dark_matter_halo_radius_enclosing_mass.F90
+++ b/source/tests.dark_matter_halo_radius_enclosing_mass.F90
@@ -27,28 +27,29 @@ program Test_Dark_Matter_Halo_Radius_Enclosing_Mass
!!{
Tests the calculation of dark matter halo radius enclosing a given mass.
!!}
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass , darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMOClass , darkMatterProfileDMOHeated , darkMatterProfileDMONFW , &
- & darkMatterProfileDMOTruncated , darkMatterProfileDMOTruncatedExponential, darkMatterProfileHeatingTidal
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversFallThrough
- use :: Display , only : displayVerbositySet , verbosityLevelStandard
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Error , only : Error_Report
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & nodeComponentSatellite , treeNode
- use :: ISO_Varying_String , only : assignment(=) , varying_string
- use :: Input_Parameters , only : inputParameters
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize , Node_Components_Uninitialize
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScale , darkMatterHaloScaleClass , darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMOClass , darkMatterProfileDMOHeated , darkMatterProfileDMONFW , &
+ & darkMatterProfileDMOTruncated , darkMatterProfileDMOTruncatedExponential, darkMatterProfileHeatingTidal
+ use :: Mass_Distributions , only : nonAnalyticSolversFallThrough , massDistributionClass
+ use :: Display , only : displayVerbositySet , verbosityLevelStandard
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities, only : Functions_Global_Set
+ use :: Error , only : Error_Report
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & nodeComponentSatellite , treeNode
+ use :: ISO_Varying_String , only : assignment(=) , varying_string
+ use :: Input_Parameters , only : inputParameters
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize , Node_Components_Uninitialize
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
type (treeNode ) :: node
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSatellite ), pointer :: satellite
class (nodeComponentDarkMatterProfile ), pointer :: dmProfile
+ class (massDistributionClass ), pointer :: massDistribution_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileDMO_
type (darkMatterProfileDMONFW ), target :: darkMatterProfileDMONFW_
type (darkMatterProfileDMOBurkert ), target :: darkMatterProfileDMOBurkert_
@@ -66,8 +67,7 @@ program Test_Dark_Matter_Halo_Radius_Enclosing_Mass
double precision , parameter :: radiusFractionalTruncateMinimum = 2.00d+00, radiusFractionalTruncateMaximum=8.0d0
double precision , parameter :: time =13.80d+00
double precision , parameter :: massVirial = 1.00d+10, concentration =8.0d0
- double precision :: radiusFractionalDecay = 0.06d+00, alpha =1.0d0 , &
- & beta = 3.00d+00, gamma =1.0d0
+ double precision :: radiusFractionalDecay = 0.06d+00
double precision , parameter :: heatingSpecific = 1.00d+06
double precision , parameter :: coefficientSecondOrder = 0.00d+00
double precision , parameter :: correlationVelocityRadius =-1.00d+00
@@ -78,7 +78,8 @@ program Test_Dark_Matter_Halo_Radius_Enclosing_Mass
type (varying_string ) :: parameterFile
type (inputParameters ) :: parameters
integer :: i , j
- logical , parameter :: velocityDispersionUseSeriesExpansion =.true. , velocityDispersionApproximate =.true.
+ logical , parameter :: velocityDispersionUseSeriesExpansion =.true. , velocityDispersionApproximate =.true., &
+ & tolerateVelocityMaximumFailure =.false.
logical :: limitToVirialRadius
! Set verbosity level.
@@ -133,15 +134,15 @@ program Test_Dark_Matter_Halo_Radius_Enclosing_Mass
!!]
- darkMatterProfileDMONFW_ = darkMatterProfileDMONFW (velocityDispersionUseSeriesExpansion, darkMatterHaloScale_ )
- darkMatterProfileDMOBurkert_ = darkMatterProfileDMOBurkert ( darkMatterHaloScale_ )
- darkMatterProfileDMOTruncated_ = darkMatterProfileDMOTruncated (radiusFractionalTruncateMinimum ,radiusFractionalTruncateMaximum, &
- & nonAnalyticSolversFallThrough , darkMatterProfileDMONFW_ ,darkMatterHaloScale_ )
- darkMatterProfileDMOTruncatedExponential_ = darkMatterProfileDMOTruncatedExponential(radiusFractionalDecay ,alpha ,beta ,gamma , &
- & nonAnalyticSolversFallThrough , darkMatterProfileDMONFW_ ,darkMatterHaloScale_ )
- darkMatterProfileHeatingTidal_ = darkMatterProfileHeatingTidal (coefficientSecondOrder ,coefficientSecondOrder ,coefficientSecondOrder ,correlationVelocityRadius )
- darkMatterProfileDMOHeated_ = darkMatterProfileDMOHeated (nonAnalyticSolversFallThrough ,velocityDispersionApproximate ,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMONFW_ ,darkMatterHaloScale_, &
- & darkMatterProfileHeatingTidal_ )
+ darkMatterProfileDMONFW_ = darkMatterProfileDMONFW (velocityDispersionUseSeriesExpansion, darkMatterHaloScale_ )
+ darkMatterProfileDMOBurkert_ = darkMatterProfileDMOBurkert ( darkMatterHaloScale_ )
+ darkMatterProfileDMOTruncated_ = darkMatterProfileDMOTruncated (radiusFractionalTruncateMinimum ,radiusFractionalTruncateMaximum, &
+ & nonAnalyticSolversFallThrough , darkMatterProfileDMONFW_ ,darkMatterHaloScale_ )
+ darkMatterProfileDMOTruncatedExponential_ = darkMatterProfileDMOTruncatedExponential(radiusFractionalDecay , &
+ & nonAnalyticSolversFallThrough , darkMatterProfileDMONFW_ ,darkMatterHaloScale_ )
+ darkMatterProfileHeatingTidal_ = darkMatterProfileHeatingTidal (coefficientSecondOrder ,coefficientSecondOrder ,coefficientSecondOrder ,correlationVelocityRadius )
+ darkMatterProfileDMOHeated_ = darkMatterProfileDMOHeated (nonAnalyticSolversFallThrough ,velocityDispersionApproximate ,tolerateVelocityMaximumFailure,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,darkMatterProfileDMONFW_, &
+ & darkMatterProfileHeatingTidal_ )
! Set up the node.
basic => node%basic (autoCreate=.true.)
satellite => node%satellite (autoCreate=.true.)
@@ -181,11 +182,15 @@ program Test_Dark_Matter_Halo_Radius_Enclosing_Mass
case default
call Error_Report('unknown profile'//{introspection:location})
end select
+ massDistribution_ => darkMatterProfileDMO_%get(node)
do j=1,7
- mass (j)=darkMatterProfileDMO_%enclosedMass (node, radius(j))
- radiusRoot(j)=darkMatterProfileDMO_%radiusEnclosingMass(node, mass (j))
+ mass (j)=massDistribution_%massEnclosedBySphere(radius(j))
+ radiusRoot(j)=massDistribution_%radiusEnclosingMass (mass (j))
if (limitToVirialRadius .and. radiusOverVirialRadius(j) > 1.0d0) radiusRoot(j)=radius(j)
end do
+ !![
+
+ !!]
call Assert('radius enclosing a given mass',radius,radiusRoot,relTol=toleranceRelative)
call Unit_Tests_End_Group()
end do
diff --git a/source/tests.dark_matter_profiles.F90 b/source/tests.dark_matter_profiles.F90
index 88a07dd4ee..68f70f4d82 100644
--- a/source/tests.dark_matter_profiles.F90
+++ b/source/tests.dark_matter_profiles.F90
@@ -25,26 +25,29 @@ program Test_Dark_Matter_Profiles
!!{
Tests dark matter profiles.
!!}
- use :: Calculations_Resets , only : Calculations_Reset
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter , darkMatterParticleCDM
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMONFW , darkMatterProfileDMOFiniteResolution, darkMatterProfileDMOSIDMCoreNFW, &
- & darkMatterProfileDMOSIDMIsothermal
- use :: Dark_Matter_Profiles , only : darkMatterProfileSIDMIsothermal , darkMatterProfileAdiabaticGnedin2004
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversNumerical
- use :: Galactic_Structure , only : galacticStructureStandard
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt, virialDensityContrastFixed , fixedDensityTypeCritical
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Display , only : displayVerbositySet , verbosityLevelStandard
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & treeNode , nodeComponentSpheroid
- use :: Input_Parameters , only : inputParameters
- use :: ISO_Varying_String , only : varying_string , assignment(=) , var_str
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize , Node_Components_Uninitialize
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Calculations_Resets , only : Calculations_Reset
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter , darkMatterParticleCDM
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMONFW , darkMatterProfileDMOFiniteResolution, darkMatterProfileDMOSIDMCoreNFW, &
+ & darkMatterProfileDMOSIDMIsothermal , darkMatterProfileDMOZhao1996
+ use :: Dark_Matter_Profiles , only : darkMatterProfileSIDMIsothermal , darkMatterProfileAdiabaticGnedin2004
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt, virialDensityContrastFixed , fixedDensityTypeCritical
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities , only : Functions_Global_Set
+ use :: Display , only : displayVerbositySet , verbosityLevelStandard
+ use :: Mass_Distributions , only : massDistributionClass , massDistributionSpherical , kinematicsDistributionClass , nonAnalyticSolversNumerical , &
+ & massDistributionSphericalSIDM
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & treeNode , nodeComponentSpheroid
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus, Mpc_per_km_per_s_To_Gyr
+ use :: Input_Parameters , only : inputParameters
+ use :: ISO_Varying_String , only : varying_string , assignment(=) , var_str
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize , Node_Components_Uninitialize
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
type (treeNode ), pointer :: node , &
& nodePippin , &
@@ -52,9 +55,11 @@ program Test_Dark_Matter_Profiles
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentDarkMatterProfile ), pointer :: dmProfile
class (nodeComponentSpheroid ), pointer :: spheroid
- double precision , parameter :: concentration = 8.0d0 , &
- & massVirial = 1.0d0 , &
- & massSmall = 1.0d-4 , &
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
+ double precision , parameter :: concentration = 8.0d+0 , &
+ & massVirial = 1.0d+0 , &
+ & massSmall = 1.0d-7 , &
! Mass and concentration of Pippin halos (Jiang et al. 2022).
& concentrationPippin =15.8d0 , &
& massVirialPippin =10.0d0**9.89d0 , &
@@ -65,10 +70,15 @@ program Test_Dark_Matter_Profiles
& fractionMassBaryonicJiang =1.0d-2 , &
& fractionRadiusHalfMassJiang =2.0d-2 , &
& radiusHalfMassDimensionlessHernquist=1.0d0/(sqrt(2.0d0)-1.0d0)
- double precision , dimension(7) :: radius =[0.125d0, 0.250d0, 0.500d0, 1.000d0, 2.000d0, 4.000d0, 8.000d0]
+ double precision , dimension(7) :: radius =[0.125000d0,0.25000d0,0.50000d0,1.00000d0,2.00000d0,4.00000d0,8.00000d0]
+ double precision , dimension(7) :: timeFreefall
double precision , dimension(7) :: mass , &
- & radiusEnclosingMass , &
& density , &
+ & radiusRecoveredFromMass , &
+ & radiusRecoveredFromDensity , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radiusRecoveredFromSpecificAngularMomentum , &
+ & potential , &
& fourier , &
& radialVelocityDispersion , &
& radialVelocityDispersionSeriesExpansion
@@ -76,6 +86,7 @@ program Test_Dark_Matter_Profiles
type (darkMatterParticleSelfInteractingDarkMatter ), pointer :: darkMatterParticleSelfInteractingDarkMatter_ , &
& darkMatterParticleSelfInteractingDarkMatterJiang_
type (darkMatterProfileDMOBurkert ), pointer :: darkMatterProfileDMOBurkert_
+ type (darkMatterProfileDMOZhao1996 ), pointer :: darkMatterProfileDMOZhao1996_
type (darkMatterProfileDMONFW ), pointer :: darkMatterProfileDMONFW_ , &
& darkMatterProfileDMONFWPippin_
type (darkMatterProfileDMONFW ), pointer :: darkMatterProfileDMONFWSeriesExpansion_
@@ -84,7 +95,6 @@ program Test_Dark_Matter_Profiles
type (darkMatterProfileDMOSIDMIsothermal ), pointer :: darkMatterProfileDMOSIDMIsothermal_
type (darkMatterProfileAdiabaticGnedin2004 ), pointer :: darkMatterProfileAdiabaticPippin_
type (darkMatterProfileSIDMIsothermal ), pointer :: darkMatterProfileSIDMIsothermal_
- type (galacticStructureStandard ), pointer :: galacticStructureStandard_
type (cosmologyParametersSimple ), pointer :: cosmologyParameters_ , &
& cosmologyParametersPippin_
type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctions_ , &
@@ -93,11 +103,23 @@ program Test_Dark_Matter_Profiles
& darkMatterHaloScalePippin_
type (virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt), pointer :: virialDensityContrast_
type (virialDensityContrastFixed ), pointer :: virialDensityContrastPippin_
+ type (coordinateSpherical ) :: coordinates
type (inputParameters ) :: parameters
integer :: i
double precision :: radiusScale , &
+ & timeScale , &
+ & radiusVelocityMaximum , &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & radiusVirial , &
+ & energyPotential , &
+ & energyKinetic , &
+ & energyPotentialNumerical , &
+ & energyKineticNumerical , &
+ & densityNormalization , &
& radiusSmall
+
! Set verbosity level.
call displayVerbositySet(verbosityLevelStandard)
! Initialize event hooks and global functions.
@@ -114,6 +136,7 @@ program Test_Dark_Matter_Profiles
allocate(virialDensityContrast_ )
allocate(darkMatterHaloScale_ )
allocate(darkMatterProfileDMOBurkert_ )
+ allocate(darkMatterProfileDMOZhao1996_ )
allocate(darkMatterProfileDMONFW_ )
allocate(darkMatterProfileDMONFWPippin_ )
allocate(darkMatterProfileDMONFWSeriesExpansion_ )
@@ -122,7 +145,6 @@ program Test_Dark_Matter_Profiles
allocate(darkMatterProfileDMOSIDMIsothermal_ )
allocate(darkMatterProfileSIDMIsothermal_ )
allocate(darkMatterProfileAdiabaticPippin_ )
- allocate(galacticStructureStandard_ )
allocate(darkMatterParticleSelfInteractingDarkMatter_ )
allocate(darkMatterParticleSelfInteractingDarkMatterJiang_)
allocate(darkMatterParticleCDM_ )
@@ -176,8 +198,15 @@ program Test_Dark_Matter_Profiles
call basic%timeSet(cosmologyFunctions_%cosmicTime(1.0d0))
call basic%massSet(massVirial )
! Compute scale radius.
- radiusScale=+darkMatterHaloScale_%radiusVirial(node) &
- & /concentration
+ radiusScale =+darkMatterHaloScale_%radiusVirial(node) &
+ & /concentration
+ densityNormalization=+massVirial &
+ & /radiusScale**3
+ timeScale =+1.0d0/sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *densityNormalization &
+ & ) &
+ & *Mpc_per_km_per_s_To_Gyr
call dmProfile%scaleSet(radiusScale)
! Build dark matter profiles.
!![
@@ -212,7 +241,6 @@ program Test_Dark_Matter_Profiles
& resolutionIsComoving =.false. , &
& nonAnalyticSolver =nonAnalyticSolversNumerical, &
& darkMatterProfileDMO_ =darkMatterProfileDMONFW_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
& cosmologyFunctions_ =cosmologyFunctions_ &
& )
@@ -220,13 +248,862 @@ program Test_Dark_Matter_Profiles
!!]
! Begin unit tests.
call Unit_Tests_Begin_Group('Dark matter profiles')
+ ! Test Zhao1996 profile.
+ call Unit_Tests_Begin_Group('Zhao1996 profile')
+ !! Special case: NFW: (α,β,γ) = (1,3,1).
+ call Unit_Tests_Begin_Group('(α,β,γ) = (1,3,1)')
+ !![
+
+
+ darkMatterProfileDMOZhao1996 ( &
+ & alpha =1.0d0 , &
+ & beta =3.0d0 , &
+ & gamma =1.0d0 , &
+ & darkMatterHaloScale_=darkMatterHaloScale_ &
+ & )
+
+
+ !!]
+ massDistribution_ => darkMatterProfileDMOZhao1996_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [0.864113d0,1.29807d0,2.04324d0,3.44421d0,6.32148d0,1.26727d1,2.74642d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusSmall =massDistribution_%radiusEnclosingMass (massSmall )
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ end select
+ !![
+
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
+ call Assert( &
+ & 'enclosed mass' , &
+ & mass , &
+ & [ &
+ & 5.099550982355504d-3 , &
+ & 1.768930674181593d-2 , &
+ & 5.513246746363203d-2 , &
+ & 1.476281525188409d-1 , &
+ & 3.301489257042704d-1 , &
+ & 6.186775455118112d-1 , &
+ & 1.000000000000000d+0 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass (at small radii)', &
+ & radiusSmall , &
+ & 1.6764798688849444d-9 , &
+ & relTol=1.0d-5 &
+ & )
+ call Assert( &
+ & 'density' , &
+ & density , &
+ & [ &
+ & 3.844641857939078d-1 , &
+ & 1.557079952465327d-1 , &
+ & 5.406527612726829d-2 , &
+ & 1.520585891079421d-2 , &
+ & 3.379079757954268d-3 , &
+ & 6.082343564317684d-4 , &
+ & 9.386332660984080d-5 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & 4.412912928902085d-2, &
+ & 8.210873989889300d-2, &
+ & 1.445116765163305d-1, &
+ & 2.345367646465509d-1, &
+ & 3.444787600350539d-1, &
+ & 4.567944810866741d-1, &
+ & 5.544042971829187d-1 &
+ & ] &
+ & -4.412912928902085d-2, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'fourier' , &
+ & fourier , &
+ & [ &
+ & 1.099052711906094d-2 , &
+ & 3.746284433831412d-2 , &
+ & 1.127728075593513d-1 , &
+ & 2.619030036621630d-1 , &
+ & 5.434560723827092d-1 , &
+ & 8.448608065763800d-1 , &
+ & 9.579597044271230d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 6.285734096346791d-1 , &
+ & 7.048421272327700d-1 , &
+ & 7.510776824829392d-1 , &
+ & 7.558757679348769d-1 , &
+ & 7.172187600402157d-1 , &
+ & 6.441447075213688d-1 , &
+ & 5.520559866965048d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Unit_Tests_End_Group()
+ !! Special case: cored NFW: (α,β,γ) = (1,3,0).
+ call Unit_Tests_Begin_Group('(α,β,γ) = (1,3,0)')
+ allocate(darkMatterProfileDMOZhao1996_)
+ !![
+
+
+ darkMatterProfileDMOZhao1996 ( &
+ & alpha =1.0d0 , &
+ & beta =3.0d0 , &
+ & gamma =0.0d0 , &
+ & darkMatterHaloScale_=darkMatterHaloScale_ &
+ & )
+
+
+ !!]
+ massDistribution_ => darkMatterProfileDMOZhao1996_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [2.912172466390932d0,3.227873348829691d0,3.870044363719088d0,5.196579302974257d0,8.00677623636939d0,1.417018715413697d1,2.823093711816523d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ end select
+ !![
+
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
+ call Assert( &
+ & 'enclosed mass' , &
+ & mass , &
+ & [ &
+ & 5.464789985591522d-4 , &
+ & 3.442068263974007d-3 , &
+ & 1.815032503316619d-2 , &
+ & 7.461855208928223d-2 , &
+ & 2.296390885460238d-1 , &
+ & 5.359157644285496d-1 , &
+ & 1.000000000000000d+0 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'density' , &
+ & density , &
+ & [ &
+ & 6.119719178912785d-2 , &
+ & 4.461275281427421d-2 , &
+ & 2.581756528603831d-2 , &
+ & 1.089178535504741d-2 , &
+ & 3.227195660754789d-3 , &
+ & 6.970742627230344d-4 , &
+ & 1.195257652131403d-4 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & 2.387190797876185d-3, &
+ & 8.130960771876030d-3, &
+ & 2.453055501081222d-2, &
+ & 6.225165933429317d-2, &
+ & 1.285052760355665d-1, &
+ & 2.164088001372156d-1, &
+ & 3.075774583263617d-1 &
+ & ] &
+ & -2.387190797876185d-3, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'fourier' , &
+ & fourier , &
+ & [ &
+ & 9.697536800190850d-4 , &
+ & 8.183597728241470d-3 , &
+ & 5.062326038988727d-2 , &
+ & 1.681631870688479d-1 , &
+ & 4.620457662739311d-1 , &
+ & 8.150847326475010d-1 , &
+ & 9.497559618451080d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 5.214290018440262d-1 , &
+ & 5.654096128890455d-1 , &
+ & 6.175226284000780d-1 , &
+ & 6.597522036704852d-1 , &
+ & 6.699313413583514d-1 , &
+ & 6.379354590281848d-1 , &
+ & 5.714635036404890d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Unit_Tests_End_Group()
+ !! Special case: cored NFW: (α,β,γ) = (1,3,½).
+ call Unit_Tests_Begin_Group('(α,β,γ) = (1,3,½)')
+ allocate(darkMatterProfileDMOZhao1996_)
+ !![
+
+
+ darkMatterProfileDMOZhao1996 ( &
+ & alpha =1.0d0 , &
+ & beta =3.0d0 , &
+ & gamma =0.5d0 , &
+ & darkMatterHaloScale_=darkMatterHaloScale_ &
+ & )
+
+
+ !!]
+ massDistribution_ => darkMatterProfileDMOZhao1996_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [1.596420780360787d0,2.059037739059005d0,2.826539348071723d0,4.248077776277237d0,7.134952215026308d0,1.342236886351155d1,2.785866097530184d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ end select
+ !![
+
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
+ call Assert( &
+ & 'enclosed mass' , &
+ & mass , &
+ & [ &
+ & 1.654826011427427d-3 , &
+ & 7.739711640396846d-3 , &
+ & 3.140778408165459d-2 , &
+ & 1.043599712384584d-1 , &
+ & 2.742860732070792d-1 , &
+ & 5.747348550741195d-1 , &
+ & 1.000000000000000d+0 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'density' , &
+ & density , &
+ & [ &
+ &1.550807828980169d-1,&
+ & 8.42653949330162d-2,&
+ & 3.777297120800491d-2,&
+ & 1.301125858993861d-2,&
+ & 3.338690510843061d-3,&
+ & 6.583233979141888d-4,&
+ & 1.070885480653384d-4&
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ &9.59892229601949d-3,&
+ & 2.419272545370556d-2,&
+ & 5.585172068801785d-2,&
+ & 1.136457588954521d-1,&
+ & 1.98498742003645d-1,&
+ & 2.975288623538137d-1,&
+ & 3.917543232803806d-1&
+ & ] &
+ & -9.59892229601949d-3, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'fourier' , &
+ & fourier , &
+ & [ &
+ &3.707401665292136d-3,&
+ & 1.826316873546129d-2,&
+ & 7.540534190396314d-2,&
+ & 2.092215103402299d-1,&
+ & 4.994927051818294d-1,&
+ & 8.28909754178526d-1,&
+ & 9.53572760138475d-1&
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 5.347095696836751d-1 , &
+ & 6.083588653620841d-1 , &
+ & 6.691204678148943d-1 , &
+ & 7.007128495278605d-1 , &
+ & 6.909164992407667d-1 , &
+ & 6.403334146398749d-1 , &
+ & 5.617664344971543d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Unit_Tests_End_Group()
+ !! Special case: cored NFW: (α,β,γ) = (1,3,1½).
+ call Unit_Tests_Begin_Group('(α,β,γ) = (1,3,1½)')
+ allocate(darkMatterProfileDMOZhao1996_)
+ !![
+
+
+ darkMatterProfileDMOZhao1996 ( &
+ & alpha =1.0d0 , &
+ & beta =3.0d0 , &
+ & gamma =1.5d0 , &
+ & darkMatterHaloScale_=darkMatterHaloScale_ &
+ & )
+
+
+ !!]
+ massDistribution_ => darkMatterProfileDMOZhao1996_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [4.596100900700435d-1,8.05108081560643d-1,1.455969425989372d0,2.760391236512836d0,5.555351793104961d0,1.191020177556312d1,2.703975097809943d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ end select
+ !![
+
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
+ call Assert( &
+ & 'enclosed mass' , &
+ & mass , &
+ & [ &
+ & 1.614787314131082d-2 , &
+ & 4.146438397463787d-2 , &
+ & 9.894487896260260d-2 , &
+ & 2.125365304218604d-1 , &
+ & 4.021269908070108d-1 , &
+ & 6.698167367007793d-1 , &
+ & 1.000000000000000d+0 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'density' , &
+ & density , &
+ & [ &
+ & 9.202064069700650d-1 , &
+ & 2.777819507076467d-1 , &
+ & 7.471144923386984d-2 , &
+ & 1.715671205314034d-2 , &
+ & 3.301810774096491d-3 , &
+ & 5.425428724758725d-4 , &
+ & 7.942922246824230d-5 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & 2.773517522337437d-1, &
+ & 3.795660488783141d-1, &
+ & 5.062490622313113d-1, &
+ & 6.498538783125577d-1, &
+ & 7.947391738552302d-1, &
+ & 9.233929853785370d-1, &
+ & 1.024853878312558d+0 &
+ & ] &
+ & -2.773517522337437d-1, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'fourier' , &
+ & fourier , &
+ & [ &
+ & 3.012898538414511d-2 , &
+ & 7.490502708109203d-2 , &
+ & 1.710991422211459d-1 , &
+ & 3.320442821775421d-1 , &
+ & 5.969264990169439d-1 , &
+ & 8.639011197411600d-1 , &
+ & 9.631744296899940d-1 &
+ & ] , &
+ & relTol=1.0d-5 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 8.363292318000940d-1 , &
+ & 8.746095866642690d-1 , &
+ & 8.733596657690690d-1 , &
+ & 8.296377525840130d-1 , &
+ & 7.505291252896317d-1 , &
+ & 6.497937689634748d-1 , &
+ & 5.421632188128948d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Unit_Tests_End_Group()
+ !! General case: (α,β,γ) = (1,2,1).
+ call Unit_Tests_Begin_Group('(α,β,γ) = (1,2,1)')
+ allocate(darkMatterProfileDMOZhao1996_)
+ !![
+
+
+ darkMatterProfileDMOZhao1996 ( &
+ & alpha =1.0d0 , &
+ & beta =2.0d0 , &
+ & gamma =1.0d0 , &
+ & darkMatterHaloScale_=darkMatterHaloScale_ &
+ & )
+
+
+ !!]
+ massDistribution_ => darkMatterProfileDMOZhao1996_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [1.760884513374176d0,2.56756519338419d0,3.83552259760776d0,5.9420179316033d0,9.64631439102321d0,1.646977873143257d1,2.944555422801916d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ end select
+ !![
+
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
+ call Assert( &
+ & 'enclosed mass' , &
+ & mass , &
+ & [ &
+ & 1.243709056088815d-3 , &
+ & 4.628207492038648d-3 , &
+ & 1.629132354883366d-2 , &
+ & 5.288035415632079d-2 , &
+ & 1.553373421641236d-1 , &
+ & 4.119687414110722d-1 , &
+ & 1.000000000000000d+0 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'density' , &
+ & density , &
+ & [ &
+ & 9.751958345559170d-2 , &
+ & 4.388381255501626d-2 , &
+ & 1.828492189792344d-2 , &
+ & 6.856845711721292d-3 , &
+ & 2.285615237240430d-3 , &
+ & 6.856845711721292d-4 , &
+ & 1.904679364367025d-4 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & 1.034803460994295d-2, &
+ & 1.994179476929133d-2, &
+ & 3.729169381246816d-2, &
+ & 6.657062060529668d-2, &
+ & 1.116566445896912d-1, &
+ & 1.743643889079128d-1, &
+ & 2.536506313435059d-1 &
+ & ] &
+ & -1.034803460994295d-2, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'fourier' , &
+ & fourier , &
+ & [ &
+ & 2.498947569002664d-3 , &
+ & 8.859191321495360d-3 , &
+ & 3.901409916856754d-2 , &
+ & 1.078443248271426d-1 , &
+ & 3.537730117747184d-1 , &
+ & 7.703987792232572d-1 , &
+ & 9.371207041223680d-1 &
+ & ] , &
+ & relTol=1.0d-5 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 3.790874753794872d-1 , &
+ & 4.539034405088716d-1 , &
+ & 5.289441877216294d-1 , &
+ & 5.997499368748768d-1 , &
+ & 6.624381831647858d-1 , &
+ & 7.141526014953340d-1 , &
+ & 7.536975187158942d-1 &
+ & ] , &
+ & relTol=1.0d-3 &
+ & )
+ call Unit_Tests_End_Group()
+ call Unit_Tests_End_Group()
! Test Burkert profile.
call Unit_Tests_Begin_Group('Burkert profile')
- do i=1,7
- mass (i)=darkMatterProfileDMOBurkert_%enclosedMass(node, radiusScale*radius(i))
- density(i)=darkMatterProfileDMOBurkert_%density (node, radiusScale*radius(i))*radiusScale**3
- fourier(i)=darkMatterProfileDMOBurkert_%kSpace (node,1.0d0/radiusScale/radius(i))
- end do
+ massDistribution_ => darkMatterProfileDMOBurkert_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [3.378196272398096d0,3.533466912782305d0,3.901024113095533d0,4.840727096354573d0,7.260451446070998d0,1.323150559822296d1,2.774028478732573d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum)
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ end select
+ !![
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
call Assert( &
& 'enclosed mass' , &
& mass , &
@@ -241,6 +1118,12 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ call Assert( &
+ & 'radius enclosing mass', &
+ & radiusRecoveredFromMass, &
+ & radius , &
+ & relTol=1.0d-6 &
+ & )
call Assert( &
& 'density' , &
& density , &
@@ -255,6 +1138,46 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & -5.517710030243666d-1, &
+ & -5.470649572158022d-1, &
+ & -5.313012274275472d-1, &
+ & -4.884797937826585d-1, &
+ & -4.072028257618435d-1, &
+ & -3.040428693416573d-1, &
+ & -2.075890931613922d-1 &
+ & ] &
+ & +5.517710030243666d-1, &
+ & relTol=1.0d-6 &
+ & )
call Assert( &
& 'fourier' , &
& fourier , &
@@ -269,21 +1192,61 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'radial velocity dispersion (analytic)', &
+ & radialVelocityDispersion , &
+ & [ &
+ & 6.395640553962476d-1 , &
+ & 6.595971570604928d-1 , &
+ & 6.818328396903716d-1 , &
+ & 6.957079957624708d-1 , &
+ & 6.836738566635967d-1 , &
+ & 6.307711855060676d-1 , &
+ & 5.466303194157120d-1 &
+ & ] , &
+ & relTol=1.0d-6 &
+ & )
call Unit_Tests_End_Group ()
! Test NFW profile.
call Unit_Tests_Begin_Group('NFW profile')
- do i=1,7
- mass (i)=darkMatterProfileDMONFW_ %enclosedMass (node, radiusScale*radius(i))
- radiusEnclosingMass (i)=darkMatterProfileDMONFW_ %radiusEnclosingMass (node, mass (i))
- density (i)=darkMatterProfileDMONFW_ %density (node, radiusScale*radius(i))*radiusScale**3
- fourier (i)=darkMatterProfileDMONFW_ %kSpace (node,1.0d0/radiusScale/radius(i))
- radialVelocityDispersion (i)=darkMatterProfileDMONFW_ %radialVelocityDispersion(node, radiusScale*radius(i))
- radialVelocityDispersionSeriesExpansion(i)=darkMatterProfileDMONFWSeriesExpansion_%radialVelocityDispersion(node, radiusScale*radius(i))
- end do
+ massDistribution_ => darkMatterProfileDMONFW_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ radiusVirial = darkMatterHaloScale_ %radiusVirial (node)
+ timeFreefall = [0.864113d0,1.29807d0,2.04324d0,3.44421d0,6.32148d0,1.26727d1,2.74642d1]
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_ %massEnclosedBySphere ( radiusScale *radius (i) )
+ density (i)=massDistribution_ %density ( coordinates )*radiusScale**3
+ radiusRecoveredFromMass (i)=massDistribution_ %radiusEnclosingMass ( mass(i) )/radiusScale
+ radiusRecoveredFromDensity (i)=massDistribution_ %radiusEnclosingDensity ( 3.0d0/4.0d0/Pi*mass(i)/radiusScale**3/radius (i)**3 )/radiusScale
+ radiusRecoveredFromSpecificAngularMomentum(i)=massDistribution_ %radiusFromSpecificAngularMomentum( sqrt(gravitationalConstantGalacticus*mass(i)*radiusScale *radius (i) ) )/radiusScale
+ radiusRecoveredFromTimeFreefall (i)=massDistribution_ %radiusFreefall ( timeScale *timeFreefall(i) )/radiusScale
+ potential (i)=massDistribution_ %potential ( coordinates )*radiusScale/gravitationalConstantGalacticus
+ fourier (i)=massDistribution_ %fourierTransform (radiusVirial,1.0d0 /radiusScale /radius (i) )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_)
+ end do
+ energyPotential =massDistribution_%energyPotential (radiusVirial )
+ energyPotentialNumerical=massDistribution_%energyPotentialNumerical (radiusVirial )
+ energyKinetic =massDistribution_%energyKinetic (radiusVirial ,massDistribution_)
+ energyKineticNumerical =massDistribution_%energyKineticNumerical (radiusVirial ,massDistribution_)
+ radiusVelocityMaximum =massDistribution_%radiusRotationCurveMaximum ( )
+ velocityMaximum =massDistribution_%velocityRotationCurveMaximum( )
+ velocityMaximumIndirect =massDistribution_%rotationCurve (radiusVelocityMaximum )
+ end select
+ !![
+
+
+ !!]
! Radial velocity dispersion in units of virial velocity.
- radialVelocityDispersion =radialVelocityDispersion /darkMatterHaloScale_%velocityVirial (node )
- radialVelocityDispersionSeriesExpansion=radialVelocityDispersionSeriesExpansion/darkMatterHaloScale_%velocityVirial (node )
- radiusSmall =darkMatterProfileDMONFW_ %radiusEnclosingMass(node,massSmall)
+ radialVelocityDispersion=radialVelocityDispersion/darkMatterHaloScale_%velocityVirial(node)
call Assert( &
& 'enclosed mass' , &
& mass , &
@@ -300,8 +1263,8 @@ program Test_Dark_Matter_Profiles
& )
call Assert( &
& 'radius enclosing mass', &
- & radiusEnclosingMass , &
- & radius*radiusScale , &
+ & radiusRecoveredFromMass, &
+ & radius , &
& relTol=1.0d-6 &
& )
call Assert( &
@@ -318,6 +1281,40 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ call Assert( &
+ & 'radius enclosing density', &
+ & radiusRecoveredFromDensity, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from specific angular momentum' , &
+ & radiusRecoveredFromSpecificAngularMomentum, &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'radius from freefall time' , &
+ & radiusRecoveredFromTimeFreefall , &
+ & radius , &
+ & relTol=2.0d-4 &
+ & )
+ call Assert( &
+ & 'potential' , &
+ & +potential &
+ & -potential(1) , &
+ & [ &
+ & 4.412912928902085d-2, &
+ & 8.210873989889300d-2, &
+ & 1.445116765163305d-1, &
+ & 2.345367646465509d-1, &
+ & 3.444787600350539d-1, &
+ & 4.567944810866741d-1, &
+ & 5.544042971829187d-1 &
+ & ] &
+ & -4.412912928902085d-2, &
+ & relTol=1.0d-6 &
+ & )
call Assert( &
& 'fourier' , &
& fourier , &
@@ -332,6 +1329,24 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ call Assert( &
+ & 'peak rotation velocity', &
+ & velocityMaximum , &
+ & velocityMaximumIndirect , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'potential energy' , &
+ & energyPotential , &
+ & energyPotentialNumerical, &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & 'kinetic energy' , &
+ & energyKinetic , &
+ & energyKineticNumerical , &
+ & relTol=1.0d-6 &
+ & )
call Assert( &
& 'radial velocity dispersion (analytic )', &
& radialVelocityDispersion , &
@@ -346,6 +1361,21 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-6 &
& )
+ massDistribution_ => darkMatterProfileDMONFWSeriesExpansion_%get (node)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,7
+ coordinates =[radiusScale*radius(i),0.0d0,0.0d0]
+ radialVelocityDispersionSeriesExpansion(i)=kinematicsDistribution_%velocityDispersion1D(coordinates,massDistribution_)
+ end do
+ end select
+ !![
+
+
+ !!]
+ ! Radial velocity dispersion in units of virial velocity.
+ radialVelocityDispersionSeriesExpansion=radialVelocityDispersionSeriesExpansion/darkMatterHaloScale_%velocityVirial(node)
call Assert( &
& 'radial velocity dispersion (series approximation)', &
& radialVelocityDispersionSeriesExpansion , &
@@ -360,20 +1390,20 @@ program Test_Dark_Matter_Profiles
& ] , &
& relTol=1.0d-5 &
& )
- call Assert( &
- & 'radius enclosing mass (at small radii )', &
- & radiusSmall , &
- & 5.357343922297839d-8 , &
- & relTol=1.0d-6 &
- & )
call Unit_Tests_End_Group ()
! Test finite resolution NFW profile.
call Unit_Tests_Begin_Group('Finite resolution NFW profile')
+ massDistribution_ => darkMatterProfileDMOFiniteResolution_%get (node)
+ radiusVirial = darkMatterHaloScale_ %radiusVirial(node)
do i=1,7
- mass (i)=darkMatterProfileDMOFiniteResolution_%enclosedMass(node, radiusScale*radius(i))
- density(i)=darkMatterProfileDMOFiniteResolution_%density (node, radiusScale*radius(i))*radiusScale**3
- fourier(i)=darkMatterProfileDMOFiniteResolution_%kSpace (node,1.0d0/radiusScale/radius(i))
+ coordinates=[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_%massEnclosedBySphere(radius = radiusScale*radius(i) )
+ density(i)=massDistribution_%density (coordinates= coordinates )*radiusScale**3
+ fourier(i)=massDistribution_%fourierTransform (wavenumber =1.0d0/radiusScale/radius(i),radiusOuter=radiusVirial)
end do
+ !![
+
+ !!]
call Assert( &
& 'enclosed mass' , &
& mass , &
@@ -453,18 +1483,9 @@ program Test_Dark_Matter_Profiles
& )
-
-
- galacticStructureStandard ( &
- & cosmologyFunctions_ =cosmologyFunctionsPippin_ , &
- & darkMatterHaloScale_ =darkMatterHaloScalePippin_ , &
- & darkMatterProfile_ =darkMatterProfileSIDMIsothermal_ &
- & )
-
-
+ offsetting the cross-section by this factor we get the correct interaction radius. -->
darkMatterParticleSelfInteractingDarkMatter ( &
& crossSectionSelfInteraction =+1.0d0 &
@@ -501,8 +1522,7 @@ program Test_Dark_Matter_Profiles
& nonAnalyticSolver =nonAnalyticSolversNumerical , &
& cosmologyParameters_ =cosmologyParametersPippin_ , &
& darkMatterHaloScale_ =darkMatterHaloScalePippin_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMONFWPippin_ , &
- & galacticStructure_ =galacticStructureStandard_ &
+ & darkMatterProfileDMO_ =darkMatterProfileDMONFWPippin_ &
& )
@@ -510,7 +1530,6 @@ program Test_Dark_Matter_Profiles
darkMatterProfileDMOSIDMIsothermal ( &
& darkMatterProfileDMO_ =darkMatterProfileDMONFWPippin_ , &
- & darkMatterHaloScale_ =darkMatterHaloScalePippin_ , &
& darkMatterParticle_ =darkMatterParticleSelfInteractingDarkMatterJiang_ &
& )
@@ -519,9 +1538,7 @@ program Test_Dark_Matter_Profiles
darkMatterProfileSIDMIsothermal ( &
& darkMatterProfile_ =darkMatterProfileAdiabaticPippin_ , &
- & darkMatterHaloScale_ =darkMatterHaloScalePippin_ , &
- & darkMatterParticle_ =darkMatterParticleSelfInteractingDarkMatterJiang_, &
- & galacticStructure_ =galacticStructureStandard_ &
+ & darkMatterParticle_ =darkMatterParticleSelfInteractingDarkMatterJiang_ &
& )
@@ -537,17 +1554,22 @@ program Test_Dark_Matter_Profiles
radiusScale=+darkMatterHaloScalePippin_%radiusVirial(nodePippin) &
& /concentrationPippin
call dmProfile%scaleSet(radiusScale)
+ massDistribution_ => darkMatterProfileDMOSIDMCoreNFW_%get(nodePippin)
do i=1,7
- mass (i)=darkMatterProfileDMOSIDMCoreNFW_%enclosedMass(nodePippin,radiusScale*radius(i))
- density(i)=darkMatterProfileDMOSIDMCoreNFW_%density (nodePippin,radiusScale*radius(i))
+ coordinates =[radiusScale*radius(i),0.0d0,0.0d0]
+ mass (i)=massDistribution_%massEnclosedBySphere(radiusScale*radius(i))
+ density (i)=massDistribution_%density (coordinates )
end do
! Interaction radius estimated from Figure 2 of Jiang et al. (2022).
- call Assert( &
- & 'interaction radius' , &
- & darkMatterProfileDMOSIDMCoreNFW_%radiusInteraction(nodePippin), &
- & 2.337664390096387d-3 , &
- & relTol=1.0d-2 &
- & )
+ select type (massDistribution_)
+ class is (massDistributionSphericalSIDM)
+ call Assert( &
+ & 'interaction radius' , &
+ & massDistribution_%radiusInteraction(), &
+ & 2.337664390096387d-3 , &
+ & relTol=1.0d-2 &
+ & )
+ end select
! Mass and density computed using Mathematica.
call Assert( &
& 'enclosed mass' , &
@@ -578,6 +1600,9 @@ program Test_Dark_Matter_Profiles
& relTol=1.0d-2 &
& )
call Unit_Tests_End_Group ()
+ !![
+
+ !!]
! Test isothermal self-interacting dark matter profile.
call Unit_Tests_Begin_Group('Isothermal self-interacting dark matter profile')
!! Set properties to match the example halo generated by Fangzhou Jiang (private communication).
@@ -589,26 +1614,36 @@ program Test_Dark_Matter_Profiles
radiusScale=+darkMatterHaloScalePippin_%radiusVirial(nodeJiang) &
& /concentrationJiang
call dmProfile%scaleSet(radiusScale)
+ massDistribution_ => darkMatterProfileDMOSIDMIsothermal_%get (nodeJiang)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
!! Target values were provided by Fangzhou Jiang (private communication).
- call Assert( &
- & 'interaction radius' , &
- & darkMatterProfileDMOSIDMIsothermal_%radiusInteraction (nodeJiang ), &
- & 6.9732d-3 , &
- & relTol=1.0d-2 &
- & )
- call Assert( &
- & 'central density' , &
- & darkMatterProfileDMOSIDMIsothermal_%density (nodeJiang,radius=0.0d0), &
- & 4.1168d16 , &
- & relTol=1.0d-1 &
- & )
- call Assert( &
- & 'central velocity dispersion' , &
- & darkMatterProfileDMOSIDMIsothermal_%radialVelocityDispersion(nodeJiang,radius=0.0d0), &
- & 54.9811d0 , &
- & relTol=1.0d-2 &
- & )
+ select type (massDistribution_)
+ class is (massDistributionSphericalSIDM)
+ coordinates=[0.0d0,0.0d0,0.0d0]
+ call Assert( &
+ & 'interaction radius' , &
+ & massDistribution_ %radiusInteraction ( ), &
+ & 6.9732d-3 , &
+ & relTol=1.0d-2 &
+ & )
+ call Assert( &
+ & 'central density' , &
+ & massDistribution_ %density (coordinates ), &
+ & 4.1168d16 , &
+ & relTol=1.0d-1 &
+ & )
+ call Assert( &
+ & 'central velocity dispersion' , &
+ & kinematicsDistribution_%velocityDispersion1D(coordinates,massDistribution_), &
+ & 54.9811d0 , &
+ & relTol=1.0d-2 &
+ & )
+ end select
call Unit_Tests_End_Group ()
+ !![
+
+
+ !!]
! Test isothermal self-interacting dark matter profile with adiabatic contraction.
call Unit_Tests_Begin_Group('Isothermal self-interacting dark matter profile (with adiabatic contraction)')
!! Set properties to match the example halo generated by Fangzhou Jiang (private communication).
@@ -620,52 +1655,72 @@ program Test_Dark_Matter_Profiles
radiusScale=+darkMatterHaloScalePippin_%radiusVirial(nodeJiang) &
& /concentrationJiang
call dmProfile%scaleSet(radiusScale)
+ massDistribution_ => darkMatterProfileSIDMIsothermal_%get (nodeJiang)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
!! Target values were provided by Fangzhou Jiang (private communication).
- call Assert( &
- & 'interaction radius' , &
- & darkMatterProfileSIDMIsothermal_%radiusInteraction (nodeJiang ), &
- & 6.9732d-3 , &
- & relTol=1.0d-2 &
- & )
- call Assert( &
- & 'central density' , &
- & darkMatterProfileSIDMIsothermal_%density (nodeJiang,radius=0.0d0), &
- & 4.1168d16 , &
- & relTol=1.0d-1 &
- & )
- call Assert( &
- & 'central velocity dispersion' , &
- & darkMatterProfileSIDMIsothermal_%radialVelocityDispersion(nodeJiang,radius=0.0d0), &
- & 54.9811d0 , &
- & relTol=1.0d-2 &
- & )
+ select type (massDistribution_)
+ class is (massDistributionSphericalSIDM)
+ coordinates=[0.0d0,0.0d0,0.0d0]
+ call Assert( &
+ & 'interaction radius' , &
+ & massDistribution_ %radiusInteraction ( ), &
+ & 6.9732d-3 , &
+ & relTol=1.0d-2 &
+ & )
+ call Assert( &
+ & 'central density' , &
+ & massDistribution_ %density (coordinates ), &
+ & 4.1168d16 , &
+ & relTol=1.0d-1 &
+ & )
+ call Assert( &
+ & 'central velocity dispersion' , &
+ & kinematicsDistribution_%velocityDispersion1D(coordinates,massDistribution_), &
+ & 54.9811d0 , &
+ & relTol=1.0d-2 &
+ & )
+ end select
call Unit_Tests_End_Group ()
+ !![
+
+
+ !!]
!! Insert a spheroid.
call Unit_Tests_Begin_Group('With baryons case')
spheroid => nodeJiang%spheroid (autoCreate=.true.)
call spheroid%massStellarSet(fractionMassBaryonicJiang * massVirialJiang )
call spheroid% radiusSet(fractionRadiusHalfMassJiang*darkMatterHaloScalePippin_%radiusVirial (nodeJiang)/radiusHalfMassDimensionlessHernquist)
call Calculations_Reset(nodeJiang)
+ massDistribution_ => darkMatterProfileSIDMIsothermal_%get (nodeJiang)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
!! Target values were measured from Figure A1 of Jiang et al. (2022).
- call Assert( &
- & 'interaction radius' , &
- & darkMatterProfileSIDMIsothermal_%radiusInteraction (nodeJiang ), &
- & 6.9732d-3 , &
- & relTol=1.0d-2 &
- & )
- call Assert( &
- & 'central density' , &
- & darkMatterProfileSIDMIsothermal_%density (nodeJiang,radius=0.0d0), &
- & 2.534d17 , &
- & relTol=2.0d-1 &
- & )
- call Assert( &
- & 'central velocity dispersion' , &
- & darkMatterProfileSIDMIsothermal_%radialVelocityDispersion(nodeJiang,radius=0.0d0), &
- & 59.139d0 , &
- & relTol=5.0d-2 &
- & )
+ select type (massDistribution_)
+ class is (massDistributionSphericalSIDM)
+ coordinates=[0.0d0,0.0d0,0.0d0]
+ call Assert( &
+ & 'interaction radius' , &
+ & massDistribution_ %radiusInteraction ( ), &
+ & 6.9732d-3 , &
+ & relTol=1.0d-2 &
+ & )
+ call Assert( &
+ & 'central density' , &
+ & massDistribution_ %density (coordinates ), &
+ & 2.534d17 , &
+ & relTol=2.0d-1 &
+ & )
+ call Assert( &
+ & 'central velocity dispersion' , &
+ & kinematicsDistribution_%velocityDispersion1D(coordinates,massDistribution_), &
+ & 59.139d0 , &
+ & relTol=5.0d-2 &
+ & )
+ end select
call Unit_Tests_End_Group()
+ !![
+
+
+ !!]
call Unit_Tests_End_Group()
! End unit tests.
call Unit_Tests_End_Group()
@@ -688,7 +1743,6 @@ program Test_Dark_Matter_Profiles
-
diff --git a/source/tests.dark_matter_profiles.Zhao1996.F90 b/source/tests.dark_matter_profiles.Zhao1996.F90
index eab42ea603..6ba929a795 100644
--- a/source/tests.dark_matter_profiles.Zhao1996.F90
+++ b/source/tests.dark_matter_profiles.Zhao1996.F90
@@ -25,24 +25,25 @@ program Test_Dark_Matter_Profiles_Zhao1996
!!{
Test calculations for Zhao1996 dark matter profiles.
!!}
- use :: Calculations_Resets , only : Calculations_Reset
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass , darkMatterProfileDMOZhao1996
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversNumerical
- use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & treeNode , nodeComponentSatellite
- use :: Input_Parameters , only : inputParameters
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Numerical_Constants_Math , only : Pi
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
- use :: Error , only : Error_Report
+ use :: Calculations_Resets , only : Calculations_Reset
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOClass , darkMatterProfileDMOZhao1996
+ use :: Mass_Distributions , only : nonAnalyticSolversNumerical , massDistributionClass , kinematicsDistributionClass , massDistributionSpherical
+ use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities, only : Functions_Global_Set
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & treeNode , nodeComponentSatellite
+ use :: Input_Parameters , only : inputParameters
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Error , only : Error_Report
implicit none
type (darkMatterHaloScaleVirialDensityContrastDefinition ) :: darkMatterHaloScale_
type (cosmologyParametersSimple ) :: cosmologyParameters_
@@ -52,6 +53,8 @@ program Test_Dark_Matter_Profiles_Zhao1996
& darkMatterProfileZhao1996CoredNFW_ , darkMatterProfileZhao1996Gamma0_5NFW_, &
& darkMatterProfileZhao1996Gamma1_5NFW_
class (darkMatterProfileDMOClass ), pointer :: darkMatterProfileZhao1996_
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
type (treeNode ), pointer :: node_
class (nodeComponentBasic ), pointer :: basic_
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile_
@@ -65,7 +68,8 @@ program Test_Dark_Matter_Profiles_Zhao1996
integer :: i , j
double precision :: radiusScale , radiusVirial , &
& potentialNumerical , potential
-
+ type (coordinateSpherical ) :: coordinates , coordinatesReference
+
call displayVerbositySet(verbosityLevelStandard)
call Unit_Tests_Begin_Group("Zhao1996 dark matter profiles")
parameters=inputParameters('testSuite/parameters/darkMatterProfilesZhao1996.xml')
@@ -195,35 +199,47 @@ program Test_Dark_Matter_Profiles_Zhao1996
case default
call Error_Report('unknown profile'//{introspection:location})
end select
- do j=1,countRadii
- mass (j)=+darkMatterProfileZhao1996_%enclosedMass (node_,radius = radii(j))
- massNumerical (j)=+darkMatterProfileZhao1996_%enclosedMassNumerical (node_,radius = radii(j))
- velocityDispersion (j)=+darkMatterProfileZhao1996_%radialVelocityDispersion (node_,radius = radii(j))
- velocityDispersionNumerical(j)=+darkMatterProfileZhao1996_%radialVelocityDispersionNumerical(node_,radius = radii(j))
- end do
- call Assert( &
- & "Enclosed mass" , &
- & mass , &
- & massNumerical , &
- & relTol=+2.0d-2 &
- & )
- call Assert( &
- & "Velocity dispersion" , &
- & velocityDispersion , &
- & velocityDispersionNumerical, &
- & relTol=+2.0d-2 &
- & )
- !! When comparing to the numerical calculation of potential we take the potential relative to 100 times the virial radius, as
- !! that is the radius to which the numerical solution is integrated.
- potential =+darkMatterProfileZhao1996_%potential (node_,radius=2.0d+0*radiusScale ) &
- & -darkMatterProfileZhao1996_%potential (node_,radius=1.0d+2*radiusVirial)
- potentialNumerical=+darkMatterProfileZhao1996_%potentialNumerical(node_,radius=2.0d+0*radiusScale )
- call Assert( &
- & "Potential" , &
- & potential , &
- & potentialNumerical, &
- & relTol=+2.0d-2 &
- & )
+ massDistribution_ => darkMatterProfileZhao1996_%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do j=1,countRadii
+ coordinates =[radii(j),0.0d0,0.0d0]
+ mass (j)=+massDistribution_ %massEnclosedBySphere (radius = radii(j) )
+ massNumerical (j)=+massDistribution_ %massEnclosedBySphereNumerical(radius = radii(j) )
+ velocityDispersion (j)=+kinematicsDistribution_%velocityDispersion1D (coordinates=coordinates ,massDistributionEmbedding=massDistribution_)
+ velocityDispersionNumerical(j)=+kinematicsDistribution_%velocityDispersion1DNumerical(coordinates=coordinates ,massDistributionEmbedding=massDistribution_)
+ end do
+ call Assert( &
+ & "Enclosed mass" , &
+ & mass , &
+ & massNumerical , &
+ & relTol=+2.0d-2 &
+ & )
+ call Assert( &
+ & "Velocity dispersion" , &
+ & velocityDispersion , &
+ & velocityDispersionNumerical, &
+ & relTol=+2.0d-2 &
+ & )
+ !! When comparing to the numerical calculation of potential we take the potential relative to 100 times the virial radius, as
+ !! that is the radius to which the numerical solution is integrated.
+ coordinates =[2.0d+0*radiusScale ,0.0d0,0.0d0]
+ coordinatesReference=[1.0d+2*radiusVirial,0.0d0,0.0d0]
+ potential =+massDistribution_%potential (coordinates=coordinates ) &
+ & -massDistribution_%potential (coordinates=coordinatesReference)
+ potentialNumerical =+massDistribution_%potentialNumerical(coordinates=coordinates )
+ call Assert( &
+ & "Potential" , &
+ & potential , &
+ & potentialNumerical, &
+ & relTol=+2.1d-2 &
+ & )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
end do
call Unit_Tests_End_Group ()
diff --git a/source/tests.dark_matter_profiles.adiabaticGnedin2004.F90 b/source/tests.dark_matter_profiles.adiabaticGnedin2004.F90
index e3e6d3a190..7ac25be90b 100644
--- a/source/tests.dark_matter_profiles.adiabaticGnedin2004.F90
+++ b/source/tests.dark_matter_profiles.adiabaticGnedin2004.F90
@@ -25,26 +25,25 @@ program Test_Dark_Matter_Profiles_Gnedin2004
!!{
Tests the implementation of the \cite{gnedin_response_2004} dark matter profile.
!!}
- use :: Calculations_Resets , only : Calculations_Reset
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMONFW
- use :: Dark_Matter_Profiles , only : darkMatterProfileAdiabaticGnedin2004
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversNumerical
- use :: File_Utilities , only : Count_Lines_in_File
- use :: Galactic_Structure , only : galacticStructureStandard
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Display , only : displayVerbositySet , verbosityLevelStandard
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & treeNode , nodeComponentSPheroid
- use :: Input_Parameters , only : inputParameters
- use :: Input_Paths , only : inputPath , pathTypeExec
- use :: ISO_Varying_String , only : varying_string , assignment(=) , char, var_str
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Calculations_Resets , only : Calculations_Reset
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMONFW
+ use :: Dark_Matter_Profiles , only : darkMatterProfileAdiabaticGnedin2004
+ use :: Mass_Distributions , only : nonAnalyticSolversNumerical , massDistributionClass , massDistributionSphericalAdiabaticGnedin2004
+ use :: File_Utilities , only : Count_Lines_in_File
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities, only : Functions_Global_Set
+ use :: Display , only : displayVerbositySet , verbosityLevelStandard
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & treeNode , nodeComponentSPheroid
+ use :: Input_Parameters , only : inputParameters
+ use :: Input_Paths , only : inputPath , pathTypeExec
+ use :: ISO_Varying_String , only : varying_string , assignment(=) , char, var_str
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize , Node_Components_Uninitialize
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
type (treeNode ), pointer :: node
class (nodeComponentBasic ), pointer :: basic
@@ -54,11 +53,11 @@ program Test_Dark_Matter_Profiles_Gnedin2004
& fractionBaryons =0.15d0, radiusFractionalBaryons =0.03d0
type (darkMatterProfileDMONFW ), pointer :: darkMatterProfileDMONFW_
type (darkMatterProfileAdiabaticGnedin2004 ), pointer :: darkMatterProfileAdiabaticGnedin2004_
- type (galacticStructureStandard ), pointer :: galacticStructureStandard_
type (cosmologyParametersSimple ), pointer :: cosmologyParameters_
type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctions_
type (darkMatterHaloScaleVirialDensityContrastDefinition ), pointer :: darkMatterHaloScale_
type (virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt), pointer :: virialDensityContrast_
+ class (massDistributionClass ), pointer :: massDistribution_
type (inputParameters ) :: parameters
double precision :: radiusScale , radiusVirial , &
& radiusFractionalFinalContra
@@ -81,7 +80,6 @@ program Test_Dark_Matter_Profiles_Gnedin2004
allocate(cosmologyParameters_ )
allocate(cosmologyFunctions_ )
allocate(virialDensityContrast_ )
- allocate(galacticStructureStandard_ )
allocate(darkMatterHaloScale_ )
allocate(darkMatterProfileDMONFW_ )
allocate(darkMatterProfileAdiabaticGnedin2004_)
@@ -121,15 +119,6 @@ program Test_Dark_Matter_Profiles_Gnedin2004
& )
-
-
- galacticStructureStandard ( &
- & cosmologyFunctions_ =cosmologyFunctions_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfile_ =darkMatterProfileAdiabaticGnedin2004_ &
- & )
-
-
darkMatterProfileDMONFW ( &
@@ -148,8 +137,7 @@ program Test_Dark_Matter_Profiles_Gnedin2004
& nonAnalyticSolver =nonAnalyticSolversNumerical , &
& cosmologyParameters_ =cosmologyParameters_ , &
& darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMONFW_ , &
- & galacticStructure_ =galacticStructureStandard_ &
+ & darkMatterProfileDMO_ =darkMatterProfileDMONFW_ &
& )
@@ -172,6 +160,8 @@ program Test_Dark_Matter_Profiles_Gnedin2004
! Construct spheroid.
call spheroid%massStellarSet(fractionBaryons *massVirial )
call spheroid%radiusSet (radiusFractionalBaryons*radiusVirial)
+ ! Get the mass distribution.
+ massDistribution_ => darkMatterProfileAdiabaticGnedin2004_%get(node)
! Begin unit tests.
call Unit_Tests_Begin_Group('Gnedin et al. (2004) dark matter profile')
! Read data from the reference "contra" file.
@@ -184,13 +174,19 @@ program Test_Dark_Matter_Profiles_Gnedin2004
do i=1,(countLines-countRadii)
read (fileUnit,*)
end do
- do i=1,countRadii
- read (fileUnit,*) radiusFractionalInitialContra(i),radiusFractionalFinalContra
- ! Evaluate the initial radius corresponding to this final radius.
- radiusFractionalInitial(i)=+darkMatterProfileAdiabaticGnedin2004_%radiusInitial(node,radiusFractionalFinalContra*radiusVirial) &
- & / radiusVirial
- end do
+ select type (massDistribution_)
+ type is (massDistributionSphericalAdiabaticGnedin2004)
+ do i=1,countRadii
+ read (fileUnit,*) radiusFractionalInitialContra(i),radiusFractionalFinalContra
+ ! Evaluate the initial radius corresponding to this final radius.
+ radiusFractionalInitial(i)=+massDistribution_%radiusInitial(radiusFractionalFinalContra*radiusVirial) &
+ & / radiusVirial
+ end do
+ end select
close(fileUnit)
+ !![
+
+ !!]
! Use a tolerance of 2% in the assertion, as our solver for the initial radius uses a tolerance of 1%.
call Assert('initial radii',radiusFractionalInitialContra,radiusFractionalInitial,relTol=2.0d-2)
! End unit tests.
@@ -207,6 +203,5 @@ program Test_Dark_Matter_Profiles_Gnedin2004
-
!!]
end program Test_Dark_Matter_Profiles_Gnedin2004
diff --git a/source/tests.dark_matter_profiles.finite_resolution.F90 b/source/tests.dark_matter_profiles.finite_resolution.F90
index fc4b3a4418..0a41e80b1b 100644
--- a/source/tests.dark_matter_profiles.finite_resolution.F90
+++ b/source/tests.dark_matter_profiles.finite_resolution.F90
@@ -25,23 +25,24 @@ program Test_Dark_Matter_Profiles_Finite_Resolution
!!{
Test calculations for finite resolution dark matter profiles.
!!}
- use :: Calculations_Resets , only : Calculations_Reset
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOFiniteResolution , darkMatterProfileDMOFiniteResolutionNFW, darkMatterProfileDMONFW
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversNumerical
- use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & treeNode , nodeComponentSatellite
- use :: Input_Parameters , only : inputParameters
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
- use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
- use :: Numerical_Constants_Math , only : Pi
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Calculations_Resets , only : Calculations_Reset
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOFiniteResolution , darkMatterProfileDMOFiniteResolutionNFW, darkMatterProfileDMONFW
+ use :: Mass_Distributions , only : nonAnalyticSolversNumerical , massDistributionClass , kinematicsDistributionClass
+ use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities, only : Functions_Global_Set
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & treeNode , nodeComponentSatellite
+ use :: Input_Parameters , only : inputParameters
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
+ use :: Numerical_Ranges , only : Make_Range , rangeTypeLogarithmic
+ use :: Numerical_Constants_Math , only : Pi
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
type (darkMatterHaloScaleVirialDensityContrastDefinition ) :: darkMatterHaloScale_
type (cosmologyParametersSimple ) :: cosmologyParameters_
@@ -53,20 +54,25 @@ program Test_Dark_Matter_Profiles_Finite_Resolution
type (treeNode ), pointer :: node_
class (nodeComponentBasic ), pointer :: basic_
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile_
+ class (massDistributionClass ), pointer :: massDistributionFiniteResolutionNFW_ , massDistributionFiniteResolution_ , &
+ & massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistributionFiniteResolutionNFW_ , kinematicsDistributionFiniteResolution_
type (inputParameters ) :: parameters
- double precision , parameter :: concentration =+8.0d0, massVirial =+1.0d12
- integer , parameter :: countRadii =10
- double precision , parameter :: radiiMinimum =1.0d-6, radiiMaximum =+1.0d01
- double precision , dimension(countRadii) :: massNumerical , mass , &
- & densityNumerical , density , &
- & velocityDispersionNumerical , velocityDispersion , &
- & radii , radiusEnclosingDensity , &
+ double precision , parameter :: concentration =+8.0d0, massVirial =+1.0d12
+ integer , parameter :: countRadii =10
+ double precision , parameter :: radiiMinimum =1.0d-6, radiiMaximum =+1.0d01
+ double precision , dimension(countRadii) :: massNumerical , mass , &
+ & densityNumerical , density , &
+ & velocityDispersionNumerical , velocityDispersion , &
+ & radii , radiusEnclosingDensity , &
& radiusEnclosingMass
integer :: i
- double precision :: radiusScale , radiusVirial , &
- & potentialNumerical , potential , &
- & energyNumerical , energy
-
+ double precision :: radiusScale , radiusVirial , &
+ & potentialNumerical , potential , &
+ & energyNumerical , energy
+ type (coordinateSpherical ) :: coordinates , coordinatesScale , &
+ & coordinatesVirial
+
call displayVerbositySet(verbosityLevelStandard)
call Unit_Tests_Begin_Group("Finite resolution dark matter profiles")
parameters=inputParameters('testSuite/parameters/darkMatterProfilesFiniteResolution.xml')
@@ -130,7 +136,6 @@ program Test_Dark_Matter_Profiles_Finite_Resolution
& resolutionIsComoving =.false. , &
& nonAnalyticSolver =nonAnalyticSolversNumerical, &
& darkMatterProfileDMO_ =darkMatterProfileNFW_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
& cosmologyFunctions_ =cosmologyFunctions_ &
& )
@@ -156,18 +161,24 @@ program Test_Dark_Matter_Profiles_Finite_Resolution
& /concentration
call darkMatterProfile_%scaleSet(radiusScale)
call Calculations_Reset(node_)
+ massDistribution_ => darkMatterProfileNFW_ %get (node_)
+ massDistributionFiniteResolutionNFW_ => darkMatterProfileFiniteResolutionNFW_%get (node_)
+ massDistributionFiniteResolution_ => darkMatterProfileFiniteResolution_ %get (node_)
+ kinematicsDistributionFiniteResolutionNFW_ => massDistributionFiniteResolutionNFW_ %kinematicsDistribution( )
+ kinematicsDistributionFiniteResolution_ => massDistributionFiniteResolution_ %kinematicsDistribution( )
! Begin tests.
call Unit_Tests_Begin_Group("Finite resolution NFW profile")
radii=Make_Range(radiiMinimum,radiiMaximum,countRadii,rangeTypeLogarithmic)*radiusScale
do i=1,countRadii
- mass (i)=+darkMatterProfileFiniteResolutionNFW_%enclosedMass (node_,radius = radii(i) )
- massNumerical (i)=+darkMatterProfileFiniteResolution_ %enclosedMass (node_,radius = radii(i) )
- density (i)=+darkMatterProfileFiniteResolutionNFW_%density (node_,radius = radii(i) )
- densityNumerical (i)=+darkMatterProfileFiniteResolution_ %density (node_,radius = radii(i) )
- velocityDispersion (i)=+darkMatterProfileFiniteResolutionNFW_%radialVelocityDispersion(node_,radius = radii(i) )
- velocityDispersionNumerical(i)=+darkMatterProfileFiniteResolution_ %radialVelocityDispersion(node_,radius = radii(i) )
- radiusEnclosingDensity (i)=+darkMatterProfileFiniteResolutionNFW_%radiusEnclosingDensity (node_,density=3.0d0*mass(i)/4.0d0/Pi/radii(i)**3)
- radiusEnclosingMass (i)=+darkMatterProfileFiniteResolutionNFW_%radiusEnclosingMass (node_,mass = mass(i) )
+ coordinates =[radii(i),0.0d0,0.0d0]
+ mass (i)=+massDistributionFiniteResolutionNFW_ %massEnclosedBySphere (radius = radii(i) )
+ massNumerical (i)=+massDistributionFiniteResolution_ %massEnclosedBySphere (radius = radii(i) )
+ density (i)=+massDistributionFiniteResolutionNFW_ %density (coordinates= coordinates )
+ densityNumerical (i)=+massDistributionFiniteResolution_ %density (coordinates= coordinates )
+ velocityDispersion (i)=+kinematicsDistributionFiniteResolutionNFW_%velocityDispersion1D (coordinates= coordinates,massDistributionEmbedding=massDistributionFiniteResolutionNFW_)
+ velocityDispersionNumerical(i)=+kinematicsDistributionFiniteResolution_ %velocityDispersion1D (coordinates= coordinates,massDistributionEmbedding=massDistributionFiniteResolution_ )
+ radiusEnclosingDensity (i)=+massDistributionFiniteResolutionNFW_ %radiusEnclosingDensity (density =3.0d0*mass(i)/4.0d0/Pi/radii(i)**3 )
+ radiusEnclosingMass (i)=+massDistributionFiniteResolutionNFW_ %radiusEnclosingMass (mass = mass(i) )
end do
call Assert( &
& "Density" , &
@@ -202,24 +213,34 @@ program Test_Dark_Matter_Profiles_Finite_Resolution
& )
!! When comparing to the numerical calculation of potential we take the potential relative to 100 times the virial radius, as
!! that is the radius to which the numerical solution is integrated.
- potential =+darkMatterProfileFiniteResolutionNFW_%potential(node_,radius=2.0d+0*radiusScale ) &
- & -darkMatterProfileFiniteResolutionNFW_%potential(node_,radius=1.0d+2*radiusVirial)
- potentialNumerical=+darkMatterProfileFiniteResolution_ %potential(node_,radius=2.0d+0*radiusScale )
+ coordinatesScale =[2.0d+0*radiusScale ,0.0d0,0.0d0]
+ coordinatesVirial =[1.0d+2*radiusVirial,0.0d0,0.0d0]
+ potential =+massDistributionFiniteResolutionNFW_%potential(coordinates=coordinatesScale ) &
+ & -massDistributionFiniteResolutionNFW_%potential(coordinates=coordinatesVirial)
+ potentialNumerical=+massDistributionFiniteResolution_ %potential(coordinates=coordinatesScale ) &
+ & +massDistributionFiniteResolution_ %potential(coordinates=coordinatesVirial)
call Assert( &
& "Potential" , &
& potential , &
& potentialNumerical, &
- & relTol=+2.0d-2 &
+ & relTol=+4.0d-2 &
& )
!! Total energy.
- energy =+darkMatterProfileFiniteResolutionNFW_%energy(node_)
- energyNumerical=+darkMatterProfileFiniteResolution_ %energy(node_)
+ energy =+massDistributionFiniteResolutionNFW_%energy(radiusVirial,massDistributionFiniteResolutionNFW_)
+ energyNumerical=+massDistributionFiniteResolution_ %energy(radiusVirial,massDistributionFiniteResolution_ )
call Assert( &
& "Energy" , &
& energy , &
& energyNumerical, &
& relTol=+3.0d-3 &
& )
+ !![
+
+
+
+
+
+ !!]
call Unit_Tests_End_Group ()
call Unit_Tests_Finish ()
call Node_Components_Thread_Uninitialize()
diff --git a/source/tests.dark_matter_profiles.generic.F90 b/source/tests.dark_matter_profiles.generic.F90
index 376636344d..c7096a67a9 100644
--- a/source/tests.dark_matter_profiles.generic.F90
+++ b/source/tests.dark_matter_profiles.generic.F90
@@ -25,23 +25,24 @@ program Test_Dark_Matter_Profiles_Generic
!!{
Tests calculations for generic dark matter profiles.
!!}
- use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Cosmology_Parameters , only : cosmologyParametersSimple
- use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
- use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles , only : darkMatterProfile , darkMatterProfileDarkMatterOnly
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMOEinasto , darkMatterProfileDMOIsothermal , darkMatterProfileDMONFW , &
- & darkMatterProfileDMOTruncated , darkMatterProfileDMOTruncatedExponential, darkMatterProfileDMOZhao1996
- use :: Dark_Matter_Profiles_Generic, only : nonAnalyticSolversNumerical
- use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
- use :: Events_Hooks , only : eventsHooksInitialize
- use :: Functions_Global_Utilities , only : Functions_Global_Set
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
- & treeNode
- use :: Input_Parameters , only : inputParameters
- use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
- use :: Unit_Tests , only : Assert , Skip , Unit_Tests_Begin_Group , Unit_Tests_End_Group , &
- & Unit_Tests_Finish
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
+ use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
+ use :: Cosmology_Parameters , only : cosmologyParametersSimple
+ use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
+ use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
+ use :: Dark_Matter_Profiles , only : darkMatterProfile , darkMatterProfileDarkMatterOnly
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOBurkert , darkMatterProfileDMOEinasto , darkMatterProfileDMOIsothermal , darkMatterProfileDMONFW , &
+ & darkMatterProfileDMOTruncated , darkMatterProfileDMOTruncatedExponential, darkMatterProfileDMOZhao1996
+ use :: Mass_Distributions , only : nonAnalyticSolversNumerical , massDistributionClass , kinematicsDistributionClass , massDistributionSpherical
+ use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
+ use :: Events_Hooks , only : eventsHooksInitialize
+ use :: Functions_Global_Utilities, only : Functions_Global_Set
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentDarkMatterProfile, &
+ & treeNode
+ use :: Input_Parameters , only : inputParameters
+ use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize , Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
+ use :: Unit_Tests , only : Assert , Skip , Unit_Tests_Begin_Group , Unit_Tests_End_Group , &
+ & Unit_Tests_Finish
implicit none
type (darkMatterHaloScaleVirialDensityContrastDefinition ) :: darkMatterHaloScale_
type (cosmologyParametersSimple ) :: cosmologyParameters_
@@ -61,6 +62,8 @@ program Test_Dark_Matter_Profiles_Generic
type (treeNode ), pointer :: node_
class (nodeComponentBasic ), pointer :: basic_
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile_
+ class (massDistributionClass ), pointer :: massDistribution_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistribution_
double precision , parameter :: concentration =8.0d+0, massVirial =1.0d12, &
& shapeProfile =1.8d-1
type (inputParameters ) :: parameters
@@ -77,10 +80,10 @@ program Test_Dark_Matter_Profiles_Generic
& radiusEnclosingDensityNumerical , radiusEnclosingDensity , &
& radiusEnclosingMassNumerical , radiusEnclosingMass , &
& radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum , &
- & densityLogSlopeNumerical , densityLogSlope , &
- & density , densityReference
+ & densityLogSlopeNumerical , densityLogSlope
double precision , dimension(9) :: scaleFractional=[0.01d0,0.03d0,0.10d0,0.30d0,1.00d0,3.00d0,10.0d0,30.0d0,100.0d0]
-
+ type (coordinateSpherical ) :: coordinates , coordinatesOuter
+
call displayVerbositySet(verbosityLevelStandard)
call Unit_Tests_Begin_Group("Generic dark matter profiles")
parameters=inputParameters('testSuite/parameters/darkMatterProfilesGeneric.xml')
@@ -168,312 +171,360 @@ program Test_Dark_Matter_Profiles_Generic
& )
darkMatterProfileTruncatedExponential_ = darkMatterProfileDMOTruncatedExponential( &
& radiusFractionalDecay = 3.0d0 , &
- & alpha = 1.0d0 , &
- & beta = 3.0d0 , &
- & gamma = 1.0d0 , &
& nonAnalyticSolver =nonAnalyticSolversNumerical, &
& darkMatterProfileDMO_ =darkMatterProfileNFW_ , &
& darkMatterHaloScale_ =darkMatterHaloScale_ &
& )
- darkMatterProfileIsothermal__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileIsothermal_ )
- darkMatterProfileNFW__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileNFW_ )
- darkMatterProfileEinasto__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileEinasto_ )
- darkMatterProfileBurkert__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileBurkert_ )
- darkMatterProfileZhao1996__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileZhao1996_ )
- darkMatterProfileTruncated__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileTruncated_ )
- darkMatterProfileTruncatedExponential__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterHaloScale_,darkMatterProfileTruncatedExponential_ )
+ darkMatterProfileIsothermal__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileIsothermal_ )
+ darkMatterProfileNFW__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileNFW_ )
+ darkMatterProfileEinasto__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileEinasto_ )
+ darkMatterProfileBurkert__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileBurkert_ )
+ darkMatterProfileZhao1996__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileZhao1996_ )
+ darkMatterProfileTruncated__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileTruncated_ )
+ darkMatterProfileTruncatedExponential__ = darkMatterProfileDarkMatterOnly(cosmologyParameters_,darkMatterProfileTruncatedExponential_)
node_ => treeNode ( )
basic_ => node_ %basic (autoCreate=.true.)
darkMatterProfile_ => node_ %darkMatterProfile(autoCreate=.true.)
call basic_ %timeSet (cosmologyFunctions_%cosmicTime(1.0d0))
call basic_ %timeLastIsolatedSet(cosmologyFunctions_%cosmicTime(1.0d0))
call basic_ %massSet (massVirial )
- radiusVirial =+darkMatterHaloScale_%radiusVirial (node_)
- timeDynamical=+darkMatterHaloScale_%timescaleDynamical(node_)
- radiusScale =+radiusVirial &
- & /concentration
+ radiusVirial =+darkMatterHaloScale_%radiusVirial (node_)
+ timeDynamical =+darkMatterHaloScale_%timescaleDynamical(node_)
+ radiusScale =+radiusVirial &
+ & /concentration
+ coordinatesOuter=[radiusVirial,0.0d0,0.0d0]
call darkMatterProfile_%scaleSet(radiusScale )
call darkMatterProfile_%shapeSet(shapeProfile)
call Unit_Tests_Begin_Group("Isothermal profile" )
- do i=1,size(scaleFractional)
- enclosedMass (i)=darkMatterProfileIsothermal__%enclosedMass (node_, scaleFractional(i)*radiusScale )
- enclosedMassNumerical (i)=darkMatterProfileIsothermal__%enclosedMassNumerical (node_, scaleFractional(i)*radiusScale )
- potential (i)=darkMatterProfileIsothermal__%potential (node_, scaleFractional(i)*radiusScale )
- potentialNumerical (i)=darkMatterProfileIsothermal__%potentialNumerical (node_, scaleFractional(i)*radiusScale )
- velocityCircular (i)=darkMatterProfileIsothermal__%circularVelocity (node_, scaleFractional(i)*radiusScale )
- velocityCircularNumerical (i)=darkMatterProfileIsothermal__%circularVelocityNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileIsothermal__%radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileIsothermal__%radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- kSpace (i)=darkMatterProfileIsothermal__%kSpace (node_, scaleFractional(i)/radiusScale )
- kSpaceNumerical (i)=darkMatterProfileIsothermal__%kSpaceNumerical (node_, scaleFractional(i)/radiusScale )
- freefallRadius (i)=darkMatterProfileIsothermal__%freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusNumerical (i)=darkMatterProfileIsothermal__%freefallRadiusNumerical (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRate (i)=darkMatterProfileIsothermal__%freefallRadiusIncreaseRate (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRateNumerical (i)=darkMatterProfileIsothermal__%freefallRadiusIncreaseRateNumerical (node_, scaleFractional(i)*timeDynamical)
- radiusEnclosingDensity (i)=darkMatterProfileIsothermal__%radiusEnclosingDensity (node_,darkMatterProfileIsothermal__%density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingDensityNumerical (i)=darkMatterProfileIsothermal__%radiusEnclosingDensityNumerical (node_,darkMatterProfileIsothermal__%density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMass (i)=darkMatterProfileIsothermal__%radiusEnclosingMass (node_,darkMatterProfileIsothermal__%enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMassNumerical (i)=darkMatterProfileIsothermal__%radiusEnclosingMassNumerical (node_,darkMatterProfileIsothermal__%enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusFromSpecificAngularMomentum (i)=darkMatterProfileIsothermal__%radiusFromSpecificAngularMomentum (node_,darkMatterProfileIsothermal__%circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- radiusFromSpecificAngularMomentumNumerical(i)=darkMatterProfileIsothermal__%radiusFromSpecificAngularMomentumNumerical(node_,darkMatterProfileIsothermal__%circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- densityLogSlope (i)=darkMatterProfileIsothermal__%densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileIsothermal__%densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- potential =potential -potential (1)
- potentialNumerical=potentialNumerical-potentialNumerical(1)
- call Skip ("Energy , E ","isothermal profile assumes virial equilibrium")
- call Skip ("Energy growth rate , ̇E ","isothermal profile assumes virial equilibrium")
- call Skip ("Radial moment , ℛ₁ ","1ˢᵗ moment diverges for isothermal profile" )
- call Assert("Radial moment , ℛ₂ ",darkMatterProfileIsothermal__%radialMomentNumerical (node_,2.0d0,0.0d0,radiusVirial),darkMatterProfileIsothermal__%radialMoment (node_,2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₃ ",darkMatterProfileIsothermal__%radialMomentNumerical (node_,3.0d0,0.0d0,radiusVirial),darkMatterProfileIsothermal__%radialMoment (node_,3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Rotation normalization , A ",darkMatterProfileIsothermal__%rotationNormalizationNumerical (node_ ),darkMatterProfileIsothermal__%rotationNormalization (node_ ),relTol=1.0d-3 )
- call Assert("Peak circular velocity , Vmax",darkMatterProfileIsothermal__%circularVelocityMaximumNumerical(node_ ),darkMatterProfileIsothermal__%circularVelocityMaximum(node_ ),relTol=1.0d-3 )
- call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
- call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
- call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
- call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
- call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
- call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=1.0d-3 )
- call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
- call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
- call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileIsothermal__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ enclosedMass (i)=massDistribution_ %massEnclosedBySphere ( scaleFractional(i)*radiusScale )
+ enclosedMassNumerical (i)=massDistribution_ %massEnclosedBySphereNumerical ( scaleFractional(i)*radiusScale )
+ potential (i)=massDistribution_ %potentialDifference ( coordinates ,coordinatesOuter )
+ potentialNumerical (i)=massDistribution_ %potentialDifferenceNumerical ( coordinates ,coordinatesOuter )
+ velocityCircular (i)=massDistribution_ %rotationCurve ( scaleFractional(i)*radiusScale )
+ velocityCircularNumerical (i)=massDistribution_ %rotationCurveNumerical ( scaleFractional(i)*radiusScale )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ kSpace (i)=massDistribution_ %fourierTransform (radiusVirial, scaleFractional(i)/radiusScale )
+ kSpaceNumerical (i)=massDistribution_ %fourierTransformNumerical (radiusVirial, scaleFractional(i)/radiusScale )
+ freefallRadius (i)=massDistribution_ %radiusFreeFall ( scaleFractional(i)*timeDynamical )
+ freefallRadiusNumerical (i)=massDistribution_ %radiusFreefallNumerical ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRate (i)=massDistribution_ %radiusFreefallIncreaseRate ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRateNumerical (i)=massDistribution_ %radiusFreefallIncreaseRateNumerical ( scaleFractional(i)*timeDynamical )
+ radiusEnclosingDensity (i)=massDistribution_ %radiusEnclosingDensity ( massDistribution_%density (coordinates ) )
+ radiusEnclosingDensityNumerical (i)=massDistribution_ %radiusEnclosingDensityNumerical ( massDistribution_%density (coordinates ) )
+ radiusEnclosingMass (i)=massDistribution_ %radiusEnclosingMass ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusEnclosingMassNumerical (i)=massDistribution_ %radiusEnclosingMassNumerical ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusFromSpecificAngularMomentum (i)=massDistribution_ %radiusFromSpecificAngularMomentum ( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ radiusFromSpecificAngularMomentumNumerical(i)=massDistribution_ %radiusFromSpecificAngularMomentumNumerical( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Skip ("Energy , E ","isothermal profile assumes virial equilibrium")
+ call Skip ("Radial moment , ℛ₁ " ,"1ˢᵗ moment diverges for isothermal profile" )
+ call Assert("Radial moment , ℛ₂ " ,massDistribution_%densityRadialMomentNumerical (2.0d0,0.0d0,radiusVirial),massDistribution_%densityRadialMoment (2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₃ " ,massDistribution_%densityRadialMomentNumerical (3.0d0,0.0d0,radiusVirial),massDistribution_%densityRadialMoment (3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
+ call Assert("Radius of peak circular velocity, Rmax",massDistribution_%radiusRotationCurveMaximumNumerical( ),massDistribution_%radiusRotationCurveMaximum( ),relTol=1.0d-3 )
+ call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
+ call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
+ call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
+ call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
+ call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=5.0d-3 )
+ call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
+ call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
+ call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ( )
call Unit_Tests_Begin_Group("NFW profile" )
- do i=1,size(scaleFractional)
- enclosedMass (i)=darkMatterProfileNFW__ %enclosedMass (node_, scaleFractional(i)*radiusScale )
- enclosedMassNumerical (i)=darkMatterProfileNFW__ %enclosedMassNumerical (node_, scaleFractional(i)*radiusScale )
- potential (i)=darkMatterProfileNFW__ %potential (node_, scaleFractional(i)*radiusScale )
- potentialNumerical (i)=darkMatterProfileNFW__ %potentialNumerical (node_, scaleFractional(i)*radiusScale )
- velocityCircular (i)=darkMatterProfileNFW__ %circularVelocity (node_, scaleFractional(i)*radiusScale )
- velocityCircularNumerical (i)=darkMatterProfileNFW__ %circularVelocityNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileNFW__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileNFW__ %radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- kSpace (i)=darkMatterProfileNFW__ %kSpace (node_, scaleFractional(i)/radiusScale )
- kSpaceNumerical (i)=darkMatterProfileNFW__ %kSpaceNumerical (node_, scaleFractional(i)/radiusScale )
- freefallRadius (i)=darkMatterProfileNFW__ %freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusNumerical (i)=darkMatterProfileNFW__ %freefallRadiusNumerical (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRate (i)=darkMatterProfileNFW__ %freefallRadiusIncreaseRate (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRateNumerical (i)=darkMatterProfileNFW__ %freefallRadiusIncreaseRateNumerical (node_, scaleFractional(i)*timeDynamical)
- radiusEnclosingDensity (i)=darkMatterProfileNFW__ %radiusEnclosingDensity (node_,darkMatterProfileNFW__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingDensityNumerical (i)=darkMatterProfileNFW__ %radiusEnclosingDensityNumerical (node_,darkMatterProfileNFW__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMass (i)=darkMatterProfileNFW__ %radiusEnclosingMass (node_,darkMatterProfileNFW__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMassNumerical (i)=darkMatterProfileNFW__ %radiusEnclosingMassNumerical (node_,darkMatterProfileNFW__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusFromSpecificAngularMomentum (i)=darkMatterProfileNFW__ %radiusFromSpecificAngularMomentum (node_,darkMatterProfileNFW__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- radiusFromSpecificAngularMomentumNumerical(i)=darkMatterProfileNFW__ %radiusFromSpecificAngularMomentumNumerical(node_,darkMatterProfileNFW__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- densityLogSlope (i)=darkMatterProfileNFW__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileNFW__ %densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- potential =potential -potential (1)
- potentialNumerical=potentialNumerical-potentialNumerical(1)
- call Assert("Energy , E ",darkMatterProfileNFW__ %energyNumerical (node_ ),darkMatterProfileNFW__ %energy (node_ ),relTol=1.0d-3 )
- call Assert("Radial moment , ℛ₁ ",darkMatterProfileNFW__ %radialMomentNumerical (node_,1.0d0,0.0d0,radiusVirial),darkMatterProfileNFW__ %radialMoment (node_,1.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₂ ",darkMatterProfileNFW__ %radialMomentNumerical (node_,2.0d0,0.0d0,radiusVirial),darkMatterProfileNFW__ %radialMoment (node_,2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₃ ",darkMatterProfileNFW__ %radialMomentNumerical (node_,3.0d0,0.0d0,radiusVirial),darkMatterProfileNFW__ %radialMoment (node_,3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Rotation normalization , A ",darkMatterProfileNFW__ %rotationNormalizationNumerical (node_ ),darkMatterProfileNFW__ %rotationNormalization (node_ ),relTol=1.0d-3 )
- call Assert("Peak circular velocity , Vmax",darkMatterProfileNFW__ %circularVelocityMaximumNumerical(node_ ),darkMatterProfileNFW__ %circularVelocityMaximum(node_ ),relTol=1.0d-3 )
- call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=1.0d-2 )
- call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
- call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
- call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
- call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
- call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=1.0d-2 )
- call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
- call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
- call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileNFW__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ enclosedMass (i)=massDistribution_ %massEnclosedBySphere ( scaleFractional(i)*radiusScale )
+ enclosedMassNumerical (i)=massDistribution_ %massEnclosedBySphereNumerical ( scaleFractional(i)*radiusScale )
+ potential (i)=massDistribution_ %potentialDifference ( coordinates ,coordinatesOuter )
+ potentialNumerical (i)=massDistribution_ %potentialDifferenceNumerical ( coordinates ,coordinatesOuter )
+ velocityCircular (i)=massDistribution_ %rotationCurve ( scaleFractional(i)*radiusScale )
+ velocityCircularNumerical (i)=massDistribution_ %rotationCurveNumerical ( scaleFractional(i)*radiusScale )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ kSpace (i)=massDistribution_ %fourierTransform (radiusVirial, scaleFractional(i)/radiusScale )
+ kSpaceNumerical (i)=massDistribution_ %fourierTransformNumerical (radiusVirial, scaleFractional(i)/radiusScale )
+ freefallRadius (i)=massDistribution_ %radiusFreeFall ( scaleFractional(i)*timeDynamical )
+ freefallRadiusNumerical (i)=massDistribution_ %radiusFreefallNumerical ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRate (i)=massDistribution_ %radiusFreefallIncreaseRate ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRateNumerical (i)=massDistribution_ %radiusFreefallIncreaseRateNumerical ( scaleFractional(i)*timeDynamical )
+ radiusEnclosingDensity (i)=massDistribution_ %radiusEnclosingDensity ( massDistribution_%density (coordinates ) )
+ radiusEnclosingDensityNumerical (i)=massDistribution_ %radiusEnclosingDensityNumerical ( massDistribution_%density (coordinates ) )
+ radiusEnclosingMass (i)=massDistribution_ %radiusEnclosingMass ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusEnclosingMassNumerical (i)=massDistribution_ %radiusEnclosingMassNumerical ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusFromSpecificAngularMomentum (i)=massDistribution_ %radiusFromSpecificAngularMomentum ( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ radiusFromSpecificAngularMomentumNumerical(i)=massDistribution_ %radiusFromSpecificAngularMomentumNumerical( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Assert("Energy , E ",massDistribution_%energyNumerical ( radiusVirial,massDistribution_),massDistribution_%energy ( radiusVirial,massDistribution_),relTol=1.0d-3 )
+ call Assert("Radial moment , ℛ₁ ",massDistribution_%densityRadialMomentNumerical (1.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (1.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₂ ",massDistribution_%densityRadialMomentNumerical (2.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (2.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₃ ",massDistribution_%densityRadialMomentNumerical (3.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (3.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radius of peak circular velocity, Rmax",massDistribution_%radiusRotationCurveMaximumNumerical( ),massDistribution_%radiusRotationCurveMaximum( ),relTol=1.0d-3 )
+ call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
+ call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
+ call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
+ call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
+ call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=5.0d-3 )
+ call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
+ call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
+ call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ( )
call Unit_Tests_Begin_Group("Einasto profile" )
- do i=1,size(scaleFractional)
- enclosedMass (i)=darkMatterProfileEinasto__ %enclosedMass (node_, scaleFractional(i)*radiusScale )
- enclosedMassNumerical (i)=darkMatterProfileEinasto__ %enclosedMassNumerical (node_, scaleFractional(i)*radiusScale )
- potential (i)=darkMatterProfileEinasto__ %potential (node_, scaleFractional(i)*radiusScale )
- potentialNumerical (i)=darkMatterProfileEinasto__ %potentialNumerical (node_, scaleFractional(i)*radiusScale )
- velocityCircular (i)=darkMatterProfileEinasto__ %circularVelocity (node_, scaleFractional(i)*radiusScale )
- velocityCircularNumerical (i)=darkMatterProfileEinasto__ %circularVelocityNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileEinasto__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileEinasto__ %radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- kSpace (i)=darkMatterProfileEinasto__ %kSpace (node_, scaleFractional(i)/radiusScale )
- kSpaceNumerical (i)=darkMatterProfileEinasto__ %kSpaceNumerical (node_, scaleFractional(i)/radiusScale )
- freefallRadius (i)=darkMatterProfileEinasto__ %freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusNumerical (i)=darkMatterProfileEinasto__ %freefallRadiusNumerical (node_, scaleFractional(i)*timeDynamical)
- radiusEnclosingDensity (i)=darkMatterProfileEinasto__ %radiusEnclosingDensity (node_,darkMatterProfileEinasto__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingDensityNumerical (i)=darkMatterProfileEinasto__ %radiusEnclosingDensityNumerical (node_,darkMatterProfileEinasto__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMass (i)=darkMatterProfileEinasto__ %radiusEnclosingMass (node_,darkMatterProfileEinasto__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMassNumerical (i)=darkMatterProfileEinasto__ %radiusEnclosingMassNumerical (node_,darkMatterProfileEinasto__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusFromSpecificAngularMomentum (i)=darkMatterProfileEinasto__ %radiusFromSpecificAngularMomentum (node_,darkMatterProfileEinasto__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- radiusFromSpecificAngularMomentumNumerical(i)=darkMatterProfileEinasto__ %radiusFromSpecificAngularMomentumNumerical(node_,darkMatterProfileEinasto__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- densityLogSlope (i)=darkMatterProfileEinasto__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileEinasto__ %densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- potential =potential -potential (1)
- potentialNumerical=potentialNumerical-potentialNumerical(1)
- call Assert("Energy , E ",darkMatterProfileEinasto__ %energyNumerical (node_ ),darkMatterProfileEinasto__ %energy (node_ ),relTol=1.0d-3 )
- call Assert("Radial moment , ℛ₁ ",darkMatterProfileEinasto__ %radialMomentNumerical (node_,1.0d0,0.0d0,radiusVirial),darkMatterProfileEinasto__ %radialMoment (node_,1.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₂ ",darkMatterProfileEinasto__ %radialMomentNumerical (node_,2.0d0,0.0d0,radiusVirial),darkMatterProfileEinasto__ %radialMoment (node_,2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₃ ",darkMatterProfileEinasto__ %radialMomentNumerical (node_,3.0d0,0.0d0,radiusVirial),darkMatterProfileEinasto__ %radialMoment (node_,3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Rotation normalization , A ",darkMatterProfileEinasto__ %rotationNormalizationNumerical (node_ ),darkMatterProfileEinasto__ %rotationNormalization (node_ ),relTol=1.0d-3 )
- call Assert("Peak circular velocity , Vmax",darkMatterProfileEinasto__ %circularVelocityMaximumNumerical(node_ ),darkMatterProfileEinasto__ %circularVelocityMaximum(node_ ),relTol=1.0d-3 )
- call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=1.0d-2 )
- call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
- call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=1.0d-2 )
- call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
- call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
- call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
- call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
- call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileEinasto__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ enclosedMass (i)=massDistribution_ %massEnclosedBySphere ( scaleFractional(i)*radiusScale )
+ enclosedMassNumerical (i)=massDistribution_ %massEnclosedBySphereNumerical ( scaleFractional(i)*radiusScale )
+ potential (i)=massDistribution_ %potentialDifference ( coordinates ,coordinatesOuter )
+ potentialNumerical (i)=massDistribution_ %potentialDifferenceNumerical ( coordinates ,coordinatesOuter )
+ velocityCircular (i)=massDistribution_ %rotationCurve ( scaleFractional(i)*radiusScale )
+ velocityCircularNumerical (i)=massDistribution_ %rotationCurveNumerical ( scaleFractional(i)*radiusScale )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ kSpace (i)=massDistribution_ %fourierTransform (radiusVirial, scaleFractional(i)/radiusScale )
+ kSpaceNumerical (i)=massDistribution_ %fourierTransformNumerical (radiusVirial, scaleFractional(i)/radiusScale )
+ freefallRadius (i)=massDistribution_ %radiusFreeFall ( scaleFractional(i)*timeDynamical )
+ freefallRadiusNumerical (i)=massDistribution_ %radiusFreefallNumerical ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRate (i)=massDistribution_ %radiusFreefallIncreaseRate ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRateNumerical (i)=massDistribution_ %radiusFreefallIncreaseRateNumerical ( scaleFractional(i)*timeDynamical )
+ radiusEnclosingDensity (i)=massDistribution_ %radiusEnclosingDensity ( massDistribution_%density (coordinates ) )
+ radiusEnclosingDensityNumerical (i)=massDistribution_ %radiusEnclosingDensityNumerical ( massDistribution_%density (coordinates ) )
+ radiusEnclosingMass (i)=massDistribution_ %radiusEnclosingMass ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusEnclosingMassNumerical (i)=massDistribution_ %radiusEnclosingMassNumerical ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusFromSpecificAngularMomentum (i)=massDistribution_ %radiusFromSpecificAngularMomentum ( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ radiusFromSpecificAngularMomentumNumerical(i)=massDistribution_ %radiusFromSpecificAngularMomentumNumerical( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Assert("Energy , E ",massDistribution_%energyNumerical ( radiusVirial,massDistribution_),massDistribution_%energy ( radiusVirial,massDistribution_),relTol=1.0d-3 )
+ call Assert("Radial moment , ℛ₁ ",massDistribution_%densityRadialMomentNumerical (1.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (1.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₂ ",massDistribution_%densityRadialMomentNumerical (2.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (2.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₃ ",massDistribution_%densityRadialMomentNumerical (3.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (3.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radius of peak circular velocity, Rmax",massDistribution_%radiusRotationCurveMaximumNumerical( ),massDistribution_%radiusRotationCurveMaximum( ),relTol=1.0d-3 )
+ call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
+ call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
+ call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
+ call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
+ call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=6.0d-3 )
+ call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
+ call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
+ call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ( )
call Unit_Tests_Begin_Group("Burkert profile" )
- do i=1,size(scaleFractional)
- enclosedMass (i)=darkMatterProfileBurkert__ %enclosedMass (node_, scaleFractional(i)*radiusScale )
- enclosedMassNumerical (i)=darkMatterProfileBurkert__ %enclosedMassNumerical (node_, scaleFractional(i)*radiusScale )
- potential (i)=darkMatterProfileBurkert__ %potential (node_, scaleFractional(i)*radiusScale )
- potentialNumerical (i)=darkMatterProfileBurkert__ %potentialNumerical (node_, scaleFractional(i)*radiusScale )
- velocityCircular (i)=darkMatterProfileBurkert__ %circularVelocity (node_, scaleFractional(i)*radiusScale )
- velocityCircularNumerical (i)=darkMatterProfileBurkert__ %circularVelocityNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileBurkert__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileBurkert__ %radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- kSpace (i)=darkMatterProfileBurkert__ %kSpace (node_, scaleFractional(i)/radiusScale )
- kSpaceNumerical (i)=darkMatterProfileBurkert__ %kSpaceNumerical (node_, scaleFractional(i)/radiusScale )
- ! Freefall radius is only evaluated for sufficiently large radii, as this profile is cored, which means that the freefall
- ! radius-time curve becomes asymptotic and difficult to evaluate numerically.
- if (i >= 4) then
- freefallRadius (i)=darkMatterProfileBurkert__ %freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusNumerical (i)=darkMatterProfileBurkert__ %freefallRadiusNumerical (node_, scaleFractional(i)*timeDynamical)
- else
- freefallRadius (i)=0.0d0
- freefallRadiusNumerical (i)=0.0d0
- end if
- radiusEnclosingDensity (i)=darkMatterProfileBurkert__ %radiusEnclosingDensity (node_,darkMatterProfileBurkert__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingDensityNumerical (i)=darkMatterProfileBurkert__ %radiusEnclosingDensityNumerical (node_,darkMatterProfileBurkert__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMass (i)=darkMatterProfileBurkert__ %radiusEnclosingMass (node_,darkMatterProfileBurkert__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMassNumerical (i)=darkMatterProfileBurkert__ %radiusEnclosingMassNumerical (node_,darkMatterProfileBurkert__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusFromSpecificAngularMomentum (i)=darkMatterProfileBurkert__ %radiusFromSpecificAngularMomentum (node_,darkMatterProfileBurkert__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- radiusFromSpecificAngularMomentumNumerical(i)=darkMatterProfileBurkert__ %radiusFromSpecificAngularMomentumNumerical(node_,darkMatterProfileBurkert__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- densityLogSlope (i)=darkMatterProfileBurkert__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileBurkert__ %densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- potential =potential -potential (1)
- potentialNumerical=potentialNumerical-potentialNumerical(1)
- call Assert("Energy , E ",darkMatterProfileBurkert__ %energyNumerical (node_ ),darkMatterProfileBurkert__ %energy (node_ ),relTol=1.0d-3 )
- call Assert("Radial moment , ℛ₁ ",darkMatterProfileBurkert__ %radialMomentNumerical (node_,1.0d0,0.0d0,radiusVirial),darkMatterProfileBurkert__ %radialMoment (node_,1.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₂ ",darkMatterProfileBurkert__ %radialMomentNumerical (node_,2.0d0,0.0d0,radiusVirial),darkMatterProfileBurkert__ %radialMoment (node_,2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₃ ",darkMatterProfileBurkert__ %radialMomentNumerical (node_,3.0d0,0.0d0,radiusVirial),darkMatterProfileBurkert__ %radialMoment (node_,3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Rotation normalization , A ",darkMatterProfileBurkert__ %rotationNormalizationNumerical (node_ ),darkMatterProfileBurkert__ %rotationNormalization (node_ ),relTol=1.0d-3 )
- call Assert("Peak circular velocity , Vmax",darkMatterProfileBurkert__ %circularVelocityMaximumNumerical(node_ ),darkMatterProfileBurkert__ %circularVelocityMaximum(node_ ),relTol=1.0d-3 )
- call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=1.0d-2 )
- call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
- call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
- call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
- call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
- call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
- call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
- call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileBurkert__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=size(scaleFractional),1,-1
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ enclosedMass (i)=massDistribution_ %massEnclosedBySphere ( scaleFractional(i)*radiusScale )
+ enclosedMassNumerical (i)=massDistribution_ %massEnclosedBySphereNumerical ( scaleFractional(i)*radiusScale )
+ potential (i)=massDistribution_ %potentialDifference ( coordinates ,coordinatesOuter )
+ potentialNumerical (i)=massDistribution_ %potentialDifferenceNumerical ( coordinates ,coordinatesOuter )
+ velocityCircular (i)=massDistribution_ %rotationCurve ( scaleFractional(i)*radiusScale )
+ velocityCircularNumerical (i)=massDistribution_ %rotationCurveNumerical ( scaleFractional(i)*radiusScale )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ kSpace (i)=massDistribution_ %fourierTransform (radiusVirial, scaleFractional(i)/radiusScale )
+ kSpaceNumerical (i)=massDistribution_ %fourierTransformNumerical (radiusVirial, scaleFractional(i)/radiusScale )
+ freefallRadius (i)=massDistribution_ %radiusFreeFall ( scaleFractional(i)*timeDynamical )
+ freefallRadiusNumerical (i)=massDistribution_ %radiusFreefallNumerical ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRate (i)=massDistribution_ %radiusFreefallIncreaseRate ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRateNumerical (i)=massDistribution_ %radiusFreefallIncreaseRateNumerical ( scaleFractional(i)*timeDynamical )
+ radiusEnclosingDensity (i)=massDistribution_ %radiusEnclosingDensity ( massDistribution_%density (coordinates ) )
+ radiusEnclosingDensityNumerical (i)=massDistribution_ %radiusEnclosingDensityNumerical ( massDistribution_%density (coordinates ) )
+ radiusEnclosingMass (i)=massDistribution_ %radiusEnclosingMass ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusEnclosingMassNumerical (i)=massDistribution_ %radiusEnclosingMassNumerical ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusFromSpecificAngularMomentum (i)=massDistribution_ %radiusFromSpecificAngularMomentum ( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ radiusFromSpecificAngularMomentumNumerical(i)=massDistribution_ %radiusFromSpecificAngularMomentumNumerical( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Assert("Energy , E ",massDistribution_%energyNumerical ( radiusVirial,massDistribution_),massDistribution_%energy ( radiusVirial,massDistribution_),relTol=1.0d-3 )
+ call Assert("Radial moment , ℛ₁ ",massDistribution_%densityRadialMomentNumerical (1.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (1.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₂ ",massDistribution_%densityRadialMomentNumerical (2.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (2.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₃ ",massDistribution_%densityRadialMomentNumerical (3.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (3.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radius of peak circular velocity, Rmax",massDistribution_%radiusRotationCurveMaximumNumerical( ),massDistribution_%radiusRotationCurveMaximum( ),relTol=1.0d-3 )
+ call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
+ call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
+ call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
+ call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
+ call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=5.0d-3 )
+ call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
+ call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
+ call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
call Unit_Tests_Begin_Group("Truncated NFW profile")
- do i=1,size(scaleFractional)
- densityLogSlope (i)=darkMatterProfileTruncated__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileTruncated__ %densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileTruncated__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileTruncated__ %radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- call Skip ("Energy , E ","implemented numerically" )
- call Skip ("Energy growth rate , ̇E ","implemented numerically" )
- call Skip ("Radial moment , ℛ₁ ","implemented numerically" )
- call Skip ("Radial moment , ℛ₂ ","implemented numerically" )
- call Skip ("Radial moment , ℛ₃ ","implemented numerically" )
- call Skip ("Rotation normalization , A ","implemented numerically" )
- call Skip ("Peak circular velocity , Vmax","implemented numerically" )
- call Skip ("Potential , Φ(r)","implemented numerically" )
- call Skip ("Circular velocity , V(r)","implemented numerically" )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=1.0d-2 )
- call Skip ("Fourier transform , u(k)","implemented numerically" )
- call Skip ("Freefall radius , r(t)","implemented numerically" )
- call Skip ("Freefall radius increase rate , ̇r(t)","implemented numerically" )
- call Skip ("Radius enclosing density , r(ρ)","implemented numerically" )
- call Skip ("Radius enclosing mass , r(M)","implemented numerically" )
- call Skip ("Radius-specific angular momentum, r(j)","implemented numerically" )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileTruncated__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Skip ("Energy , E ","implemented numerically" )
+ call Skip ("Energy growth rate , ̇E ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₁ ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₂ ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₃ ","implemented numerically" )
+ call Skip ("Rotation normalization , A ","implemented numerically" )
+ call Skip ("Peak circular velocity , Vmax","implemented numerically" )
+ call Skip ("Potential , Φ(r)","implemented numerically" )
+ call Skip ("Circular velocity , V(r)","implemented numerically" )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=1.0d-2 )
+ call Skip ("Fourier transform , u(k)","implemented numerically" )
+ call Skip ("Freefall radius , r(t)","implemented numerically" )
+ call Skip ("Freefall radius increase rate , ̇r(t)","implemented numerically" )
+ call Skip ("Radius enclosing density , r(ρ)","implemented numerically" )
+ call Skip ("Radius enclosing mass , r(M)","implemented numerically" )
+ call Skip ("Radius-specific angular momentum, r(j)","implemented numerically" )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
call Unit_Tests_Begin_Group("Exponentially-truncated NFW profile")
- do i=1,size(scaleFractional)
- densityLogSlope (i)=darkMatterProfileTruncatedExponential__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileTruncatedExponential__ %densityLogSlopeNumerical (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileTruncatedExponential__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileTruncatedExponential__ %radialVelocityDispersionNumerical (node_, scaleFractional(i)*radiusScale )
- end do
- call Skip ("Energy , E ","implemented numerically" )
- call Skip ("Energy growth rate , ̇E ","implemented numerically" )
- call Skip ("Radial moment , ℛ₁ ","implemented numerically" )
- call Skip ("Radial moment , ℛ₂ ","implemented numerically" )
- call Skip ("Radial moment , ℛ₃ ","implemented numerically" )
- call Skip ("Rotation normalization , A ","implemented numerically" )
- call Skip ("Peak circular velocity , Vmax","implemented numerically" )
- call Skip ("Potential , Φ(r)","implemented numerically" )
- call Skip ("Circular velocity , V(r)","implemented numerically" )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
- call Skip ("Fourier transform , u(k)","implemented numerically" )
- call Skip ("Freefall radius , r(t)","implemented numerically" )
- call Skip ("Freefall radius increase rate , ̇r(t)","implemented numerically" )
- call Skip ("Radius enclosing density , r(ρ)","implemented numerically" )
- call Skip ("Radius enclosing mass , r(M)","implemented numerically" )
- call Skip ("Radius-specific angular momentum, r(j)","implemented numerically" )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileTruncatedExponential__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Skip ("Energy , E ","implemented numerically" )
+ call Skip ("Energy growth rate , ̇E ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₁ ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₂ ","implemented numerically" )
+ call Skip ("Radial moment , ℛ₃ ","implemented numerically" )
+ call Skip ("Rotation normalization , A ","implemented numerically" )
+ call Skip ("Peak circular velocity , Vmax","implemented numerically" )
+ call Skip ("Potential , Φ(r)","implemented numerically" )
+ call Skip ("Circular velocity , V(r)","implemented numerically" )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Skip ("Fourier transform , u(k)","implemented numerically" )
+ call Skip ("Freefall radius , r(t)","implemented numerically" )
+ call Skip ("Freefall radius increase rate , ̇r(t)","implemented numerically" )
+ call Skip ("Radius enclosing density , r(ρ)","implemented numerically" )
+ call Skip ("Radius enclosing mass , r(M)","implemented numerically" )
+ call Skip ("Radius-specific angular momentum, r(j)","implemented numerically" )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
! The Zhao1996 profile is a generalized NFW-type profile. So, compare to the NFW profile.
call Unit_Tests_Begin_Group("Zhao1996 profile" )
- do i=1,size(scaleFractional)
- density (i)=darkMatterProfileZhao1996__ %density (node_, scaleFractional(i)*radiusScale )
- densityReference (i)=darkMatterProfileNFW__ %density (node_, scaleFractional(i)*radiusScale )
- enclosedMass (i)=darkMatterProfileZhao1996__ %enclosedMass (node_, scaleFractional(i)*radiusScale )
- enclosedMassNumerical (i)=darkMatterProfileNFW__ %enclosedMass (node_, scaleFractional(i)*radiusScale )
- potential (i)=darkMatterProfileZhao1996__ %potential (node_, scaleFractional(i)*radiusScale )
- potentialNumerical (i)=darkMatterProfileNFW__ %potential (node_, scaleFractional(i)*radiusScale )
- velocityCircular (i)=darkMatterProfileZhao1996__ %circularVelocity (node_, scaleFractional(i)*radiusScale )
- velocityCircularNumerical (i)=darkMatterProfileNFW__ %circularVelocity (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersion (i)=darkMatterProfileZhao1996__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- radialVelocityDispersionNumerical (i)=darkMatterProfileNFW__ %radialVelocityDispersion (node_, scaleFractional(i)*radiusScale )
- kSpace (i)=darkMatterProfileZhao1996__ %kSpace (node_, scaleFractional(i)/radiusScale )
- kSpaceNumerical (i)=darkMatterProfileNFW__ %kSpace (node_, scaleFractional(i)/radiusScale )
- freefallRadius (i)=darkMatterProfileZhao1996__ %freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusNumerical (i)=darkMatterProfileNFW__ %freefallRadius (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRate (i)=darkMatterProfileZhao1996__ %freefallRadiusIncreaseRate (node_, scaleFractional(i)*timeDynamical)
- freefallRadiusIncreaseRateNumerical (i)=darkMatterProfileNFW__ %freefallRadiusIncreaseRate (node_, scaleFractional(i)*timeDynamical)
- radiusEnclosingDensity (i)=darkMatterProfileZhao1996__ %radiusEnclosingDensity (node_,darkMatterProfileZhao1996__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingDensityNumerical (i)=darkMatterProfileNFW__ %radiusEnclosingDensityNumerical (node_,darkMatterProfileNFW__ %density (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMass (i)=darkMatterProfileZhao1996__ %radiusEnclosingMass (node_,darkMatterProfileZhao1996__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusEnclosingMassNumerical (i)=darkMatterProfileNFW__ %radiusEnclosingMass (node_,darkMatterProfileNFW__ %enclosedMass (node_,scaleFractional(i)*radiusScale) )
- radiusFromSpecificAngularMomentum (i)=darkMatterProfileZhao1996__ %radiusFromSpecificAngularMomentum(node_,darkMatterProfileZhao1996__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- radiusFromSpecificAngularMomentumNumerical(i)=darkMatterProfileNFW__ %radiusFromSpecificAngularMomentum(node_,darkMatterProfileNFW__ %circularVelocity(node_,scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
- densityLogSlope (i)=darkMatterProfileZhao1996__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- densityLogSlopeNumerical (i)=darkMatterProfileNFW__ %densityLogSlope (node_, scaleFractional(i)*radiusScale )
- end do
- potential =potential -potential (1)
- potentialNumerical=potentialNumerical-potentialNumerical(1)
- call Assert("Energy , E ",darkMatterProfileNFW__%energy (node_ ),darkMatterProfileZhao1996__ %energy (node_ ),relTol=1.0d-3 )
- call Assert("Radial moment , ℛ₁ ",darkMatterProfileNFW__%radialMoment (node_,1.0d0,0.0d0,radiusVirial),darkMatterProfileZhao1996__ %radialMoment (node_,1.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₂ ",darkMatterProfileNFW__%radialMoment (node_,2.0d0,0.0d0,radiusVirial),darkMatterProfileZhao1996__ %radialMoment (node_,2.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Radial moment , ℛ₃ ",darkMatterProfileNFW__%radialMoment (node_,3.0d0,0.0d0,radiusVirial),darkMatterProfileZhao1996__ %radialMoment (node_,3.0d0,0.0d0,radiusVirial),relTol=1.0d-6 )
- call Assert("Rotation normalization , A ",darkMatterProfileNFW__%rotationNormalization (node_ ),darkMatterProfileZhao1996__ %rotationNormalization (node_ ),relTol=1.0d-3 )
- call Assert("Peak circular velocity , Vmax",darkMatterProfileNFW__%circularVelocityMaximum (node_ ),darkMatterProfileZhao1996__ %circularVelocityMaximum(node_ ),relTol=1.0d-3 )
- call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=1.0d-2 )
- call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
- call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
- call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
- call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
- call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
- call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=1.0d-2 )
- call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
- call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
- call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
- call Assert("Density , ρ(r)", densityReference , density ,relTol=1.0d-3 )
- call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ massDistribution_ => darkMatterProfileBurkert__%get (node_)
+ kinematicsDistribution_ => massDistribution_ %kinematicsDistribution( )
+ select type (massDistribution_)
+ class is (massDistributionSpherical)
+ do i=1,size(scaleFractional)
+ coordinates =[scaleFractional(i)*radiusScale,0.0d0,0.0d0]
+ enclosedMass (i)=massDistribution_ %massEnclosedBySphere ( scaleFractional(i)*radiusScale )
+ enclosedMassNumerical (i)=massDistribution_ %massEnclosedBySphereNumerical ( scaleFractional(i)*radiusScale )
+ potential (i)=massDistribution_ %potentialDifference ( coordinates ,coordinatesOuter )
+ potentialNumerical (i)=massDistribution_ %potentialDifferenceNumerical ( coordinates ,coordinatesOuter )
+ velocityCircular (i)=massDistribution_ %rotationCurve ( scaleFractional(i)*radiusScale )
+ velocityCircularNumerical (i)=massDistribution_ %rotationCurveNumerical ( scaleFractional(i)*radiusScale )
+ radialVelocityDispersion (i)=kinematicsDistribution_%velocityDispersion1D ( coordinates ,massDistribution_ )
+ radialVelocityDispersionNumerical (i)=kinematicsDistribution_%velocityDispersion1DNumerical ( coordinates ,massDistribution_ )
+ kSpace (i)=massDistribution_ %fourierTransform (radiusVirial, scaleFractional(i)/radiusScale )
+ kSpaceNumerical (i)=massDistribution_ %fourierTransformNumerical (radiusVirial, scaleFractional(i)/radiusScale )
+ freefallRadius (i)=massDistribution_ %radiusFreeFall ( scaleFractional(i)*timeDynamical )
+ freefallRadiusNumerical (i)=massDistribution_ %radiusFreefallNumerical ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRate (i)=massDistribution_ %radiusFreefallIncreaseRate ( scaleFractional(i)*timeDynamical )
+ freefallRadiusIncreaseRateNumerical (i)=massDistribution_ %radiusFreefallIncreaseRateNumerical ( scaleFractional(i)*timeDynamical )
+ radiusEnclosingDensity (i)=massDistribution_ %radiusEnclosingDensity ( massDistribution_%density (coordinates ) )
+ radiusEnclosingDensityNumerical (i)=massDistribution_ %radiusEnclosingDensityNumerical ( massDistribution_%density (coordinates ) )
+ radiusEnclosingMass (i)=massDistribution_ %radiusEnclosingMass ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusEnclosingMassNumerical (i)=massDistribution_ %radiusEnclosingMassNumerical ( massDistribution_%massEnclosedBySphere(scaleFractional(i)*radiusScale) )
+ radiusFromSpecificAngularMomentum (i)=massDistribution_ %radiusFromSpecificAngularMomentum ( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ radiusFromSpecificAngularMomentumNumerical(i)=massDistribution_ %radiusFromSpecificAngularMomentumNumerical( massDistribution_%rotationCurve (scaleFractional(i)*radiusScale)*scaleFractional(i)*radiusScale )
+ densityLogSlope (i)=massDistribution_ %densityGradientRadial ( coordinates ,logarithmic=.true.)
+ densityLogSlopeNumerical (i)=massDistribution_ %densityGradientRadialNumerical ( coordinates ,logarithmic=.true.)
+ end do
+ call Assert("Energy , E ",massDistribution_%energyNumerical ( radiusVirial,massDistribution_),massDistribution_%energy ( radiusVirial,massDistribution_),relTol=1.0d-3 )
+ call Assert("Radial moment , ℛ₁ ",massDistribution_%densityRadialMomentNumerical (1.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (1.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₂ ",massDistribution_%densityRadialMomentNumerical (2.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (2.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radial moment , ℛ₃ ",massDistribution_%densityRadialMomentNumerical (3.0d0,0.0d0,radiusVirial ),massDistribution_%densityRadialMoment (3.0d0,0.0d0,radiusVirial ),relTol=1.0d-6 )
+ call Assert("Radius of peak circular velocity, Rmax",massDistribution_%radiusRotationCurveMaximumNumerical( ),massDistribution_%radiusRotationCurveMaximum( ),relTol=1.0d-3 )
+ call Assert("Enclosed mass , M(r)", enclosedMassNumerical , enclosedMass ,relTol=2.0d-3 )
+ call Assert("Potential , Φ(r)", potentialNumerical , potential ,relTol=1.0d-3 )
+ call Assert("Circular velocity , V(r)", velocityCircularNumerical , velocityCircular ,relTol=1.0d-3 )
+ call Assert("Radial velocity dispersion , σ(r)", radialVelocityDispersionNumerical , radialVelocityDispersion ,relTol=3.0d-3 )
+ call Assert("Fourier transform , u(k)", kSpaceNumerical , kSpace ,relTol=1.0d-3,absTol=1.0d-4)
+ call Assert("Freefall radius , r(t)", freefallRadiusNumerical , freefallRadius ,relTol=1.0d-3 )
+ call Assert("Freefall radius increase rate , ̇r(t)", freefallRadiusIncreaseRateNumerical , freefallRadiusIncreaseRate ,relTol=5.0d-3 )
+ call Assert("Radius enclosing density , r(ρ)", radiusEnclosingDensityNumerical , radiusEnclosingDensity ,relTol=1.0d-3 )
+ call Assert("Radius enclosing mass , r(M)", radiusEnclosingMassNumerical , radiusEnclosingMass ,relTol=1.0d-3 )
+ call Assert("Radius-specific angular momentum, r(j)", radiusFromSpecificAngularMomentumNumerical , radiusFromSpecificAngularMomentum ,relTol=1.0d-3 )
+ call Assert("Density log gradient , α(r)", densityLogSlopeNumerical , densityLogSlope ,relTol=1.0d-3 )
+ end select
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
call Unit_Tests_End_Group ()
call Unit_Tests_Finish ()
diff --git a/source/tests.dark_matter_profiles.heated.F90 b/source/tests.dark_matter_profiles.heated.F90
index a1d3c9b03b..e848722e7b 100644
--- a/source/tests.dark_matter_profiles.heated.F90
+++ b/source/tests.dark_matter_profiles.heated.F90
@@ -23,32 +23,43 @@
program Test_Dark_Matter_Profiles_Heated
!!{
- Tests heated dark matter profile implementations. An isothermal dark matter halo is used, since analytic solutions are
- available for this case. Specifically, the initial radius in the unheated profile is given by $r_\mathrm{i}(r) =
- (r_\mathrm{h}^4/4 r^2 + r_\mathrm{h}^2)^{1/2}-r_\mathrm{h}^2/2 r$ where $r$ is the radius in the heated profile, and
- $r_\mathrm{h}=(\mathrm{G} M_\mathrm{v} / 2 Q r_\mathrm{v})^{1/2}$ is a characteristic heating radius. Here, $M_\mathrm{v}$,
- and $r_\mathrm{v}$ are the virial mass and radius of the halo respectively, and $Q r_\mathrm{i}^2$ is the specific heat input
- to the density profile, with $Q$ assumed to be a constant (as expected for tidal heating). Assuming no shell crossing, the
- enclosed mass in the final profile is simply $M(r) = M_\mathrm{v} r_\mathrm{i}(r)/r_\mathrm{v}$, from which the density of
- the final profile is found as $\rho(r) = (4 \pi r^2)^{-1} \mathrm{d} M(r) / \mathrm{d} r$.
+ Tests heated dark matter profile implementations. An isothermal dark matter halo is used, since analytic solutions are available
+ for this case. Specifically, the initial radius in the unheated profile is given by $r_\mathrm{i}(r) = (r_\mathrm{h}^4/4 r^2 +
+ r_\mathrm{h}^2)^{1/2}-r_\mathrm{h}^2/2 r$ where $r$ is the radius in the heated profile, and $r_\mathrm{h}=(\mathrm{G}
+ M_\mathrm{v} / 2 Q r_\mathrm{v})^{1/2}$ is a characteristic heating radius. Here, $M_\mathrm{v}$, and $r_\mathrm{v}$ are the
+ virial mass and radius of the halo respectively, and $Q r_\mathrm{i}^2$ is the specific heat input to the density profile, with
+ $Q$ assumed to be a constant (as expected for tidal heating). Assuming no shell crossing, the enclosed mass in the final profile
+ is simply $M(r) = M_\mathrm{v} r_\mathrm{i}(r)/r_\mathrm{v}$, from which the density of the final profile is found as $\rho(r) =
+ (4 \pi r^2)^{-1} \mathrm{d} M(r) / \mathrm{d} r$. The velocity dispersion can also be found analytically in this
+ case. Integrating the Jeans equation in the variables of the initial profile gives a result:
+ \begin{eqnarray}
+ \rho(r) \sigma^2(r) &=& \int_{r_\mathrm{i}(r)}^{r_\mathrm{h}} \mathrm{d}r^\prime_\mathrm{i} \frac{\mathrm{G} M(r^\prime_\mathrm{i})}{r^{\prime 2}_\mathrm{i}} \rho_\mathrm{i}(r^\prime_\mathrm{i}) \left(\frac{r^\prime_\mathrm{i}}{r}\right)^4 \nonumber \\
+ &=& \frac{10}{6} \frac{\mathrm{G} M(r_\mathrm{h}) \rho_\mathrm{i}(r_\mathrm{h}}{r_\mathrm{h}} - \frac{(-3+18 y^4 - 6 y^6 + y^8 - 24 y^2 \log(y))}{6} \frac{\mathrm{G} M(r_\mathrm{i}) \rho_\mathrm{i}(r_\mathrm{i}}{r_\mathrm{i}},
+ \end{eqnarray}
+ where $y=r_\mathrm{i}/r_\mathrm{h}$.
!!}
+ use :: Coordinates , only : coordinateSpherical
use :: Cosmology_Parameters , only : cosmologyParametersSimple
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
- use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter , darkMatterParticleCDM
+ use :: Dark_Matter_Particles , only : darkMatterParticleSelfInteractingDarkMatter , darkMatterParticleCDM
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOHeated , darkMatterProfileDMOHeatedMonotonic, darkMatterProfileDMOIsothermal, darkMatterProfileHeatingTidal, &
+ use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOHeated , darkMatterProfileDMOHeatedMonotonic, darkMatterProfileDMOIsothermal , darkMatterProfileHeatingTidal , &
& darkMatterProfileDMOClass
- use :: Dark_Matter_Profiles_Generic , only : nonAnalyticSolversFallThrough
- use :: Display , only : displayVerbositySet , verbosityLevelStandard
+ use :: Display , only : displayVerbositySet , verbosityLevelStandard
use :: Events_Hooks , only : eventsHooksInitialize
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentSatellite , &
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , nodeComponentSatellite , &
& treeNode
- use :: ISO_Varying_String , only : assignment(=) , varying_string
+ use :: ISO_Varying_String , only : varying_string
use :: Input_Parameters , only : inputParameters
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
- use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
+ use :: Mass_Distributions , only : massDistributionSphericalHeated , massDistributionClass , massDistributionSpherical , massDistributionHeatingClass , &
+ & nonAnalyticSolversNumerical , kinematicsDistributionHeated , massDistributionSphericalHeatedMonotonic, kinematicsDistributionCollisionless, &
+ & kinematicsDistributionClass , &
+ & nonAnalyticSolversFallThroughDMO => nonAnalyticSolversFallThrough , &
+ & nonAnalyticSolversNumericalDMO => nonAnalyticSolversNumerical
+ use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
double precision , parameter :: time =13.8d+00
double precision , parameter :: massVirial = 1.0d+10
@@ -58,7 +69,8 @@ program Test_Dark_Matter_Profiles_Heated
double precision , parameter :: toleranceRelativeVelocityDispersion = 1.0d-06
double precision , parameter :: toleranceRelativeVelocityDispersionMaximum = 1.0d-03
double precision , parameter :: toleranceRelativePotential = 1.0d-03
- logical , parameter :: velocityDispersionApproximate =.true.
+ logical , parameter :: velocityDispersionApproximate =.false.
+ logical , parameter :: tolerateVelocityMaximumFailure =.false.
class (nodeComponentBasic ), pointer :: basic
class (nodeComponentSatellite ), pointer :: satellite
double precision , dimension(3) :: radiusVirialFractional =[0.1d0,0.5d0,1.0d0]
@@ -76,11 +88,19 @@ program Test_Dark_Matter_Profiles_Heated
type (darkMatterProfileHeatingTidal ) :: darkMatterProfileHeatingTidal_
type (darkMatterParticleCDM ) :: darkMatterParticleCDM_
type (darkMatterParticleSelfInteractingDarkMatter ) :: darkMatterParticleSelfInteractingDarkMatter_
+ class (massDistributionSpherical ), pointer :: massDistributionSphericalHeated_
+ class (kinematicsDistributionClass ), pointer :: kinematicsDistributionHeated_
+ class (massDistributionHeatingClass ), pointer :: massDistributionHeatingTidal_
+ class (massDistributionClass ), pointer :: massDistributionIsothermal_
+ type (coordinateSpherical ) :: coordinates , coordinatesHeated , &
+ & coordinatesInitial
double precision :: radiusVirial , radiusHeated , &
- & density , densityAnalytic , &
+ & massEnclosedAnalytic , densityAnalytic , &
& radiusInitial , radiusInitialAnalytic, &
- & massEnclosed , massEnclosedAnalytic , &
- & radius , toleranceRelative
+ & radius , toleranceRelative , &
+ & massEnclosed_ , density_ , &
+ & velocityDispersionAnalytic , radiusScaled , &
+ & toleranceRelativeVelocityDispersionAssert , velocityDispersion_
integer :: i , profileType
character (len=5 ) :: radiusLabel
character (len=128 ) :: profileName
@@ -144,10 +164,10 @@ program Test_Dark_Matter_Profiles_Heated
!!]
- darkMatterProfileHeatingTidal_ =darkMatterProfileHeatingTidal (coefficientSecondOrder ,coefficientSecondOrder ,coefficientSecondOrder ,correlationVelocityRadius )
- darkMatterProfileDMOIsothermal_ =darkMatterProfileDMOIsothermal ( darkMatterHaloScale_ )
- darkMatterProfileDMOHeated_ =darkMatterProfileDMOHeated (nonAnalyticSolversFallThrough,velocityDispersionApproximate,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum ,darkMatterProfileDMOIsothermal_,darkMatterHaloScale_,darkMatterProfileHeatingTidal_)
- darkMatterProfileDMOHeatedMonotonic_=darkMatterProfileDMOHeatedMonotonic(nonAnalyticSolversFallThrough ,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum,toleranceRelativePotential,darkMatterProfileDMOIsothermal_,darkMatterHaloScale_,darkMatterProfileHeatingTidal_)
+ darkMatterProfileHeatingTidal_ =darkMatterProfileHeatingTidal (coefficientSecondOrder ,coefficientSecondOrder ,coefficientSecondOrder ,correlationVelocityRadius )
+ darkMatterProfileDMOIsothermal_ =darkMatterProfileDMOIsothermal ( darkMatterHaloScale_ )
+ darkMatterProfileDMOHeated_ =darkMatterProfileDMOHeated (nonAnalyticSolversNumericalDMO,velocityDispersionApproximate,tolerateVelocityMaximumFailure,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum ,darkMatterProfileDMOIsothermal_ ,darkMatterProfileHeatingTidal_)
+ darkMatterProfileDMOHeatedMonotonic_=darkMatterProfileDMOHeatedMonotonic(nonAnalyticSolversNumericalDMO ,toleranceRelativeVelocityDispersion,toleranceRelativeVelocityDispersionMaximum ,darkMatterProfileDMOIsothermal_,darkMatterHaloScale_,darkMatterProfileHeatingTidal_)
! Set up the node.
basic => node%basic (autoCreate=.true.)
satellite => node%satellite(autoCreate=.true.)
@@ -155,35 +175,76 @@ program Test_Dark_Matter_Profiles_Heated
call basic %timeSet (time )
call basic %timeLastIsolatedSet (time )
call satellite%tidalHeatingNormalizedSet(heatingSpecific)
- radiusVirial=darkMatterHaloScale_%radiusVirial(node)
+ ! Get the associated mass distribution.
+ massDistributionIsothermal_ => darkMatterProfileDMOIsothermal_%get(node )
+ massDistributionHeatingTidal_ => darkMatterProfileHeatingTidal_ %get(node)
! Compute the characteristic radius for heating.
+ radiusVirial=darkMatterHaloScale_%radiusVirial(node)
radiusHeated=sqrt(gravitationalConstantGalacticus*massVirial/2.0d0/heatingSpecific/radiusVirial)
! Iterate over heated profile classes.
do profileType=1,2
select case (profileType)
case (1)
- darkMatterProfileDMO_ => darkMatterProfileDMOHeated_
- profileName = "no shell crossing"
- toleranceRelative = 1.0d-6
+ darkMatterProfileDMO_ => darkMatterProfileDMOHeated_
+ profileName = "no shell crossing"
+ toleranceRelative = 1.0d-6
+ toleranceRelativeVelocityDispersionAssert = 1.0d-2
+ select type (massDistributionIsothermal_)
+ class is (massDistributionSpherical)
+ allocate(massDistributionSphericalHeated :: massDistributionSphericalHeated_)
+ allocate(kinematicsDistributionHeated :: kinematicsDistributionHeated_ )
+ select type (massDistributionSphericalHeated_)
+ type is (massDistributionSphericalHeated )
+ !![
+
+ !!]
+ end select
+ select type (kinematicsDistributionHeated_)
+ type is (kinematicsDistributionHeated )
+ !![
+
+ !!]
+ end select
+ end select
case (2)
- darkMatterProfileDMO_ => darkMatterProfileDMOHeatedMonotonic_
- profileName = "monotonic perturbation"
- toleranceRelative = 7.0d-2
+ darkMatterProfileDMO_ => darkMatterProfileDMOHeatedMonotonic_
+ profileName = "monotonic perturbation"
+ toleranceRelative = 7.0d-2
+ toleranceRelativeVelocityDispersionAssert = 1.0d-1
+ select type (massDistributionIsothermal_)
+ class is (massDistributionSpherical)
+ allocate(massDistributionSphericalHeatedMonotonic :: massDistributionSphericalHeated_)
+ allocate(kinematicsDistributionCollisionless :: kinematicsDistributionHeated_ )
+ select type (massDistributionSphericalHeated_)
+ type is (massDistributionSphericalHeatedMonotonic)
+ !![
+
+ !!]
+ end select
+ select type (kinematicsDistributionHeated_)
+ type is (kinematicsDistributionCollisionless)
+ !![
+
+ !!]
+ end select
+ end select
end select
call Unit_Tests_Begin_Group("Heated dark matter profiles ("//trim(profileName)//")")
! Compute initial radius, enclosed mass, and density for a variety of radii and compare to the analytic solutions.
do i=1,size(radiusVirialFractional)
write (radiusLabel,'(f4.2)') radiusVirialFractional(i)
- radius =+ radiusVirial &
- & * radiusVirialFractional( i )
- select type (darkMatterProfileDMO_)
- class is (darkMatterProfileDMOHeated)
- radiusInitial =+darkMatterProfileDMO_%radiusInitial (node,radius)
+ radius =+radiusVirial &
+ & *radiusVirialFractional(i)
+ coordinates = [radius,0.0d0,0.0d0]
+ select type (massDistributionSphericalHeated_)
+ class is (massDistributionSphericalHeated)
+ radiusInitial =+massDistributionSphericalHeated_%radiusInitial (radius )
class default
radiusInitial =-huge(0.0d0)
end select
- massEnclosed =+darkMatterProfileDMO_%enclosedMass (node,radius)
- density =+darkMatterProfileDMO_%density (node,radius)
+ massEnclosed_ =+massDistributionSphericalHeated_%massEnclosedBySphere (radius )
+ density_ =+massDistributionSphericalHeated_%density (coordinates )
+ velocityDispersion_ =+kinematicsDistributionHeated_ %velocityDispersion1D (coordinates,massDistributionSphericalHeated_)
radiusInitialAnalytic=+sqrt( &
& + radiusHeated **4 &
& /4.0d0 &
@@ -193,6 +254,10 @@ program Test_Dark_Matter_Profiles_Heated
& - radiusHeated **2 &
& /2.0d0 &
& / radius
+ radiusScaled =+radiusInitialAnalytic &
+ & /radiusHeated
+ coordinatesHeated = [radiusHeated ,0.0d0,0.0d0]
+ coordinatesInitial = [radiusInitialAnalytic,0.0d0,0.0d0]
massEnclosedAnalytic =+massVirial &
& * radiusInitialAnalytic &
& / radiusVirial
@@ -215,12 +280,28 @@ program Test_Dark_Matter_Profiles_Heated
& /2.0d0 &
& / radius **2 &
& )
+ velocityDispersionAnalytic=+sqrt( &
+ & +gravitationalConstantGalacticus &
+ & *( &
+ & +massDistributionIsothermal_%massEnclosedBySphere(radiusHeated )*massDistributionIsothermal_%density(coordinatesHeated )/radiusHeated &
+ & *(+10.0d0 ) &
+ & -massDistributionIsothermal_%massEnclosedBySphere(radiusInitialAnalytic)*massDistributionIsothermal_%density(coordinatesInitial)/radiusInitialAnalytic &
+ & *(- 3.0d0+18.0d0*radiusScaled**4-6.0d0*radiusScaled**6+radiusScaled**8-24.0d0*radiusScaled**2*log(radiusScaled)) &
+ & ) &
+ & /6.0d0 &
+ & /densityAnalytic &
+ & )
if (profileType == 1) &
- & call Assert('r='//trim(radiusLabel)//'rᵥ; initial radius',radiusInitial,radiusInitialAnalytic,relTol=toleranceRelative)
- call Assert('r='//trim(radiusLabel)//'rᵥ; enclosed mass' ,massEnclosed ,massEnclosedAnalytic ,relTol=toleranceRelative)
- call Assert('r='//trim(radiusLabel)//'rᵥ; density' ,density ,densityAnalytic ,relTol=toleranceRelative)
+ & call Assert('r='//trim(radiusLabel)//'rᵥ; initial radius' ,radiusInitial ,radiusInitialAnalytic ,relTol=toleranceRelative )
+ call Assert('r='//trim(radiusLabel)//'rᵥ; enclosed mass' ,massEnclosed_ ,massEnclosedAnalytic ,relTol=toleranceRelative )
+ call Assert('r='//trim(radiusLabel)//'rᵥ; density' ,density_ ,densityAnalytic ,relTol=toleranceRelative )
+ call Assert('r='//trim(radiusLabel)//'rᵥ; velocity dispersion',velocityDispersion_,velocityDispersionAnalytic,relTol=toleranceRelativeVelocityDispersionAssert)
end do
call Unit_Tests_End_Group()
+ !![
+
+
+ !!]
end do
call nodeClassHierarchyFinalize()
call Unit_Tests_Finish ()
diff --git a/source/tests.dark_matter_profiles.projected.F90 b/source/tests.dark_matter_profiles.projected.F90
index 682289a919..78085e4360 100644
--- a/source/tests.dark_matter_profiles.projected.F90
+++ b/source/tests.dark_matter_profiles.projected.F90
@@ -31,10 +31,8 @@ program Test_Dark_Matter_Profiles_Projected
use :: Cosmology_Parameters , only : cosmologyParametersSimple
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
- use :: Dark_Matter_Profiles , only : darkMatterProfileDarkMatterOnly
use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMONFW
use :: Node_Property_Extractors , only : nodePropertyExtractorProjectedMass , nodePropertyExtractorProjectedDensity
- use :: Galactic_Structure , only : galacticStructureStandard
use :: Display , only : displayMessage , displayVerbositySet , verbosityLevelStandard
use :: Events_Hooks , only : eventsHooksInitialize
use :: Functions_Global_Utilities, only : Functions_Global_Set
@@ -51,8 +49,6 @@ program Test_Dark_Matter_Profiles_Projected
type (cosmologyParametersSimple ) :: cosmologyParameters_
type (cosmologyFunctionsMatterLambda ) :: cosmologyFunctions_
type (virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt) :: virialDensityContrast_
- type (darkMatterProfileDarkMatterOnly ) :: darkMatterProfile_
- type (galacticStructureStandard ) :: galacticStructure_
type (darkMatterProfileDMONFW ) :: darkMatterProfileDMO_
type (nodePropertyExtractorProjectedMass ) :: nodePropertyExtractorProjectedMass_
type (nodePropertyExtractorProjectedDensity ) :: nodePropertyExtractorProjectedDensity_
@@ -68,7 +64,7 @@ program Test_Dark_Matter_Profiles_Projected
type (multiCounter ) :: instance
call displayVerbositySet(verbosityLevelStandard)
- call Unit_Tests_Begin_Group("Tidal track dark matter profiles")
+ call Unit_Tests_Begin_Group("Projected dark matter profiles")
parameters=inputParameters(var_str('testSuite/parameters/darkMatterProfilesProjected.xml'))
call eventsHooksInitialize()
call Functions_Global_Set ( )
@@ -134,32 +130,13 @@ program Test_Dark_Matter_Profiles_Projected
& darkMatterHaloScale_ =darkMatterHaloScale_ &
& )
-
-
-
- darkMatterProfileDarkMatterOnly ( &
- & cosmologyParameters_ =cosmologyParameters_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMO_ &
- & )
-
-
-
- galacticStructureStandard ( &
- & cosmologyFunctions_ =cosmologyFunctions_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfile_ =darkMatterProfile_ &
- & )
-
-
-
+
nodePropertyExtractorProjectedMass ( &
& radiusSpecifiers =radiusSpecifiers , &
& includeRadii =.false. , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & galacticStructure_ =galacticStructure_ &
+ & darkMatterHaloScale_ =darkMatterHaloScale_ &
& )
@@ -168,14 +145,12 @@ program Test_Dark_Matter_Profiles_Projected
nodePropertyExtractorProjectedDensity( &
& radiusSpecifiers =radiusSpecifiers , &
& includeRadii =.false. , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & galacticStructure_ =galacticStructure_ &
+ & darkMatterHaloScale_ =darkMatterHaloScale_ &
& )
!!]
! Begin tests.
- call Unit_Tests_Begin_Group("Projected density" )
instance =multiCounter([1_c_size_t])
densityProjected=nodePropertyExtractorProjectedDensity_%extract(node__,basic__%time(),instance)
massProjected =nodePropertyExtractorProjectedMass_ %extract(node__,basic__%time(),instance)
diff --git a/source/tests.dark_matter_profiles.tidal_tracks.F90 b/source/tests.dark_matter_profiles.tidal_tracks.F90
index 3e01512a99..60d3db66e4 100644
--- a/source/tests.dark_matter_profiles.tidal_tracks.F90
+++ b/source/tests.dark_matter_profiles.tidal_tracks.F90
@@ -26,6 +26,7 @@ program Test_Dark_Matter_Profiles_Tidal_Tracks
Test calculations for tidal track dark matter profiles.
!!}
use :: Calculations_Resets , only : Calculations_Reset
+ use :: Coordinates , only : coordinateSpherical , assignment(=)
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
use :: Cosmology_Parameters , only : cosmologyParametersSimple
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
@@ -38,6 +39,7 @@ program Test_Dark_Matter_Profiles_Tidal_Tracks
& treeNode , nodeComponentSatellite
use :: Input_Parameters , only : inputParameters
use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
+ use :: Mass_Distributions , only : massDistributionClass
use :: Unit_Tests , only : Assert , Skip , Unit_Tests_Begin_Group , Unit_Tests_End_Group , &
& Unit_Tests_Finish
implicit none
@@ -51,14 +53,16 @@ program Test_Dark_Matter_Profiles_Tidal_Tracks
class (nodeComponentBasic ), pointer :: basic_
class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile_
class (nodeComponentSatellite ), pointer :: satellite_
- double precision , parameter :: concentration =+8.0d0, massVirial =+1.0d12, &
- & muRadius =-0.3d0, etaRadius =+0.4d-0, &
- & muVelocity =+0.4d0, etaVelocity =+0.3d-0
+ class (massDistributionClass ), pointer :: massDistributionNFW_ , massDistributionPenarrubia2010_
+ double precision , parameter :: concentration =+8.0d0, massVirial =+1.0d12, &
+ & muRadius =-0.3d0, etaRadius =+0.4d-0, &
+ & muVelocity =+0.4d0, etaVelocity =+0.3d-0
type (inputParameters ) :: parameters
- double precision :: radiusScale , radiusVirial , &
- & velocityMaximumInitial , radiusMaximumInitial , &
- & velocityMaximumTidalTrack , radiusMaximumTidalTrack , &
+ double precision :: radiusScale , radiusVirial , &
+ & velocityMaximumInitial , radiusMaximumInitial , &
+ & velocityMaximumTidalTrack , radiusMaximumTidalTrack , &
& fractionMassBound
+ type (coordinateSpherical ) :: coordinatesScale , coordinatesVirial
call displayVerbositySet(verbosityLevelStandard)
call Unit_Tests_Begin_Group("Tidal track dark matter profiles")
@@ -139,36 +143,56 @@ program Test_Dark_Matter_Profiles_Tidal_Tracks
radiusVirial =+darkMatterHaloScale_%radiusVirial(node_)
radiusScale =+radiusVirial &
& /concentration
+ coordinatesScale =[radiusScale ,0.0d0,0.0d0]
+ coordinatesVirial=[radiusVirial,0.0d0,0.0d0]
call darkMatterProfile_%scaleSet(radiusScale)
call Calculations_Reset(node_)
! Store the initial values of rmax and Vmax.
- radiusMaximumInitial =darkMatterProfileNFW_%radiusCircularVelocityMaximum(node_)
- velocityMaximumInitial=darkMatterProfileNFW_% circularVelocityMaximum(node_)
+ massDistributionNFW_ => darkMatterProfileNFW_ %get (node_)
+ massDistributionPenarrubia2010_ => darkMatterProfilePenarrubia2010_%get (node_)
+ radiusMaximumInitial = massDistributionNFW_ % radiusRotationCurveMaximum( )
+ velocityMaximumInitial = massDistributionNFW_ %velocityRotationCurveMaximum( )
! Begin tests.
call Unit_Tests_Begin_Group("Unstripped profile matches NFW" )
- call Assert("Density at scale radius" ,darkMatterProfileNFW_%density(node_,radius=radiusVirial),darkMatterProfilePenarrubia2010_%density(node_,radius=radiusVirial),relTol=1.0d-6)
- call Assert("Density at virial radius",darkMatterProfileNFW_%density(node_,radius=radiusVirial),darkMatterProfilePenarrubia2010_%density(node_,radius=radiusVirial),relTol=1.0d-6)
+ call Assert("Density at scale radius" ,massDistributionNFW_%density(coordinates=coordinatesScale ),massDistributionPenarrubia2010_%density(coordinates=coordinatesScale ),relTol=1.0d-6)
+ call Assert("Density at virial radius",massDistributionNFW_%density(coordinates=coordinatesVirial),massDistributionPenarrubia2010_%density(coordinates=coordinatesVirial),relTol=1.0d-6)
call Unit_Tests_End_Group ( )
+ !![
+
+
+ !!]
call Unit_Tests_Begin_Group("Stripped profile (95%) tidal track")
call satellite_%boundMassSet(0.95d0*massVirial)
call Calculations_Reset(node_)
+ massDistributionNFW_ => darkMatterProfileNFW_ %get (node_)
+ massDistributionPenarrubia2010_ => darkMatterProfilePenarrubia2010_%get (node_)
fractionMassBound =+satellite_%boundMass() &
& /basic_ % mass()
velocityMaximumTidalTrack=velocityMaximumInitial*2.0d0**muVelocity*fractionMassBound**etaVelocity/(1.0d0+fractionMassBound)**muVelocity
radiusMaximumTidalTrack = radiusMaximumInitial*2.0d0**muRadius *fractionMassBound**etaRadius /(1.0d0+fractionMassBound)**muRadius
- call Assert("Tidal track rmax",radiusMaximumTidalTrack ,darkMatterProfilePenarrubia2010_%radiusCircularVelocityMaximum(node_),relTol=1.0d-6)
- call Assert("Tidal track Vmax",velocityMaximumTidalTrack,darkMatterProfilePenarrubia2010_% circularVelocityMaximum(node_),relTol=1.0d-6)
+ call Assert("Tidal track rmax",radiusMaximumTidalTrack ,massDistributionPenarrubia2010_% radiusRotationCurveMaximum(),relTol=1.0d-6)
+ call Assert("Tidal track Vmax",velocityMaximumTidalTrack,massDistributionPenarrubia2010_%velocityRotationCurveMaximum(),relTol=1.0d-6)
call Unit_Tests_End_Group ()
+ !![
+
+
+ !!]
call Unit_Tests_Begin_Group("Stripped profile (30%) tidal track")
call satellite_%boundMassSet(0.30d0*massVirial)
call Calculations_Reset(node_)
+ massDistributionNFW_ => darkMatterProfileNFW_ %get (node_)
+ massDistributionPenarrubia2010_ => darkMatterProfilePenarrubia2010_%get (node_)
fractionMassBound =+satellite_%boundMass() &
& /basic_ % mass()
velocityMaximumTidalTrack=velocityMaximumInitial*2.0d0**muVelocity*fractionMassBound**etaVelocity/(1.0d0+fractionMassBound)**muVelocity
radiusMaximumTidalTrack = radiusMaximumInitial*2.0d0**muRadius *fractionMassBound**etaRadius /(1.0d0+fractionMassBound)**muRadius
- call Assert("Tidal track rmax",radiusMaximumTidalTrack ,darkMatterProfilePenarrubia2010_%radiusCircularVelocityMaximum(node_),relTol=1.0d-6)
- call Assert("Tidal track Vmax",velocityMaximumTidalTrack,darkMatterProfilePenarrubia2010_% circularVelocityMaximum(node_),relTol=1.0d-6)
+ call Assert("Tidal track rmax",radiusMaximumTidalTrack ,massDistributionPenarrubia2010_% radiusRotationCurveMaximum(),relTol=1.0d-6)
+ call Assert("Tidal track Vmax",velocityMaximumTidalTrack,massDistributionPenarrubia2010_%velocityRotationCurveMaximum(),relTol=1.0d-6)
call Unit_Tests_End_Group ()
+ !![
+
+
+ !!]
call Unit_Tests_End_Group ()
call Unit_Tests_Finish ()
call Node_Components_Thread_Uninitialize()
diff --git a/source/tests.kepler_orbits.F90 b/source/tests.kepler_orbits.F90
index 176e52541c..b7e9f5a420 100644
--- a/source/tests.kepler_orbits.F90
+++ b/source/tests.kepler_orbits.F90
@@ -22,28 +22,20 @@ program Tests_Kepler_Orbits
Tests for orbital parameter conversions.
!!}
use :: Display , only : displayVerbositySet , verbosityLevelStandard
- use :: ISO_Varying_String , only : assignment(=) , varying_string
- use :: Input_Parameters , only : inputParameters
use :: Kepler_Orbits , only : keplerOrbit
use :: Numerical_Constants_Astronomical, only : gravitationalConstantGalacticus
use :: Numerical_Constants_Math , only : Pi
use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group, Unit_Tests_End_Group, Unit_Tests_Finish, &
& compareEquals
implicit none
- type (varying_string ) :: parameterFile
- type (keplerOrbit ) :: orbit
- double precision :: valueActual , valueExpected, velocityScale
- type (inputParameters) :: parameters
+ type (keplerOrbit) :: orbit
+ double precision :: valueActual, valueExpected, velocityScale
! Set verbosity level.
call displayVerbositySet(verbosityLevelStandard)
! Begin unit tests.
call Unit_Tests_Begin_Group("Orbital parameter conversions")
- ! Open the parameter file.
- parameterFile='parameters.xml'
- parameters=inputParameters(parameterFile)
-
! Compute velocity scale for unit mass and radius.
velocityScale=sqrt(gravitationalConstantGalacticus)
diff --git a/source/tests.mass_distributions.F90 b/source/tests.mass_distributions.F90
index 31ace3f5cf..dca5074cbd 100644
--- a/source/tests.mass_distributions.F90
+++ b/source/tests.mass_distributions.F90
@@ -32,16 +32,20 @@ program Test_Mass_Distributions
use :: Events_Hooks , only : eventsHooksInitialize
use :: Galactic_Structure_Options, only : componentTypeSpheroid , componentTypeDisk
use :: Linear_Algebra , only : assignment(=) , vector
- use :: Mass_Distributions , only : massDistributionBetaProfile , massDistributionClass , massDistributionExponentialDisk , massDistributionGaussianEllipsoid, &
- & massDistributionHernquist , massDistributionSersic , massDistributionSpherical , massDistributionComposite , &
- & massDistributionList , massDistributionSymmetryCylindrical, enumerationMassDistributionSymmetryType, massDistributionSphericalScaler , &
- & massDistributionCylindricalScaler, massDistributionCylindrical
+ use :: Mass_Distributions , only : massDistributionBetaProfile , massDistributionClass , massDistributionExponentialDisk , massDistributionGaussianEllipsoid , &
+ & massDistributionHernquist , massDistributionSersic , massDistributionSpherical , massDistributionComposite , &
+ & massDistributionList , massDistributionSymmetryCylindrical, enumerationMassDistributionSymmetryType, massDistributionSphericalScaler , &
+ & massDistributionCylindricalScaler, massDistributionCylindrical , massDistributionPatejLoeb2015 , massDistributionNFW , &
+ & massDistributionIsothermal , kinematicsDistributionClass , kinematicsDistributionLocal
use :: Numerical_Constants_Math , only : Pi
use :: Tensors , only : assignment(=)
use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
- class (massDistributionClass ) , allocatable :: massDistribution_ , massDistributionRotated , &
- & massDistributionDisk , massDistributionSpheroid
+ class (massDistributionClass ) , allocatable :: massDistribution_ , massDistributionRotated , &
+ & massDistributionDisk , massDistributionSpheroid , &
+ & massDistributionDMO
+ class (massDistributionClass ) , pointer :: massDistributionDisk_ , massDistributionSpheroid_
+ class (kinematicsDistributionClass ) , allocatable :: kinematicsDistribution_
type (massDistributionList ) , pointer :: massDistributions
integer , parameter :: sersicTableCount =8
double precision , dimension(sersicTableCount) :: sersicTableRadius =[1.0000d-06,1.0000d-5,1.0000d-4,1.0000d-3,1.0000d-2,1.0000d-1,1.0000d+0,1.0000d+1]
@@ -51,19 +55,34 @@ program Test_Mass_Distributions
double precision , dimension(sersicTableCount) :: sersicTableDensityTarget =[2.5553d+06,3.5797d+5,4.2189d+4,3.7044d+3,1.9679d+2,4.4047d+0,2.1943d-2,7.8166d-6]
! Potential targets for Sersic profile from Young (1976).
double precision , dimension(sersicTableCount) :: sersicTablePotentialTarget =[1.0000d+00,9.9993d-1,9.9908d-1,9.9027d-1,9.2671d-1,6.7129d-1,2.4945d-1,3.7383d-2]
- double precision , dimension(sersicTableCount) :: sersicTableDensity , sersicTableMass , &
+ double precision , dimension(sersicTableCount) :: sersicTableDensity , sersicTableMass , &
& sersicTablePotential
- type (coordinateSpherical ) :: position , positionZero
+ type (coordinateSpherical ) :: position , positionZero , &
+ & positionReference
type (coordinateCartesian ) :: positionCartesian
type (enumerationMassDistributionSymmetryType) :: symmetry_
integer :: i
- double precision :: radiusInProjection , radius , &
- & rotationCurveGradientAnalytic , rotationCurveGradientNumerical, &
- & massFraction
+ double precision :: radiusInProjection , radius , &
+ & rotationCurveGradientAnalytic , rotationCurveGradientNumerical , &
+ & massFraction , time
double precision , parameter :: epsilonFiniteDifference =0.01d0
character (len=4 ) :: label
double precision , dimension(3,3) :: tidalTensorComponents , tidalTensorSphericalComponents
double precision , dimension(3 ) :: acceleration
+ double precision , dimension(4 ) :: massPatejLoeb , densityPatejLoeb , &
+ & densitySlopePatejLoeb , densityMomentPatejLoeb , &
+ & potentialPatejLoeb
+ double precision , dimension(4 ) :: massIsothermal , densityIsothermal , &
+ & densitySlopeIsothermal , densityMomentIsothermal , &
+ & potentialIsothermal , fourierTransformIsothermal , &
+ & radiusFreeFallIsothermal , radiusFreeFallGrowthRateIsothermal , &
+ & massIsothermalNumerical , potentialIsothermalDifferenceNumerical , &
+ & fourierTransformIsothermalNumerical , radiusFreeFallIsothermalNumerical , &
+ & radiusFreeFallGrowthRateIsothermalNumerical , densitySlopeIsothermalNumerical , &
+ & radiusEnclosingMassIsothermal , radiusEnclosingMassIsothermalNumerical , &
+ & radiusEnclosingDensityIsothermal , radiusEnclosingDensityIsothermalNumerical , &
+ & radiiIsothermal , radiusFromSpecificAngularMomentumIsothermal, &
+ & radiusFromSpecificAngularMomentumIsothermalNumerical , velocityCircularIsothermal
type (vector ), dimension(: ) , allocatable :: axes
! Set verbosity level.
@@ -184,6 +203,19 @@ program Test_Mass_Distributions
& +1.793209546954886d0 , &
& absTol=1.0d-6 &
& )
+ call Assert( &
+ & "Density gradient (logarithmic)" , &
+ & +massDistribution_%densityGradientRadial(position,.true. ) , &
+ & -1.000000000000000d0 , &
+ & absTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "Density gradient (non-logarithmic)" , &
+ & +massDistribution_%densityGradientRadial(position,.false. ) , &
+ & +massDistribution_%densityGradientRadial(position,.true. ) &
+ & *massDistribution_%density (position ) , &
+ & absTol=1.0d-6 &
+ & )
end select
call Unit_Tests_End_Group()
deallocate(massDistribution_)
@@ -455,13 +487,19 @@ program Test_Mass_Distributions
massDistribution_=massDistributionComposite(massDistributions)
end select
symmetry_=massDistribution_%symmetry()
- call Assert("Maximal symmetry [cylindrical]" ,symmetry_ %ID ,massDistributionSymmetryCylindrical%ID )
- positionCartesian=[1.0d-3,0.0d0,0.0d0]
- call Assert("Density at (x,y,z)=(1,0,0) kpc" ,massDistribution_%density(positionCartesian ),1.47756308872d18 ,relTol=1.0d-6)
- call Assert("Spheroid density at (x,y,z)=(1,0,0) kpc",massDistribution_%density(positionCartesian,componentType=componentTypeSpheroid),2.04086330446d17 ,relTol=1.0d-6)
- call Assert("Disk density at (x,y,z)=(1,0,0) kpc" ,massDistribution_%density(positionCartesian,componentType=componentTypeDisk ),1.27347675828d18 ,relTol=1.0d-6)
+ call Assert("Maximal symmetry [cylindrical]" ,symmetry_ %ID ,massDistributionSymmetryCylindrical%ID )
+ massDistributionDisk_ => massDistribution_%subset(componentType=componentTypeDisk )
+ massDistributionSpheroid_ => massDistribution_%subset(componentType=componentTypeSpheroid)
+ positionCartesian = [1.0d-3,0.0d0,0.0d0]
+ call Assert("Density at (x,y,z)=(1,0,0) kpc" ,massDistribution_ %density(positionCartesian),1.47756308872d18 ,relTol=1.0d-6)
+ call Assert("Spheroid density at (x,y,z)=(1,0,0) kpc",massDistributionSpheroid_%density(positionCartesian),2.04086330446d17 ,relTol=1.0d-6)
+ call Assert("Disk density at (x,y,z)=(1,0,0) kpc" ,massDistributionDisk_ %density(positionCartesian),1.27347675828d18 ,relTol=1.0d-6)
nullify (massDistributions)
deallocate(massDistribution_)
+ !![
+
+
+ !!]
call Unit_Tests_End_Group()
! Composite profile with scaled components.
@@ -506,6 +544,230 @@ program Test_Mass_Distributions
nullify (massDistributions)
deallocate(massDistribution_)
call Unit_Tests_End_Group()
+
+ ! Patej & Loeb (2015) profile.
+ call Unit_Tests_Begin_Group("Patej-Loeb (2015) profile")
+ allocate(massDistributionNFW :: massDistributionDMO)
+ select type (massDistributionDMO)
+ type is (massDistributionNFW)
+ massDistributionDMO=massDistributionNFW(scaleLength=30.0d-3,virialRadius=300.0d-3,mass=1.0d12,dimensionless=.false.)
+ end select
+ allocate(massDistributionPatejLoeb2015 :: massDistribution_)
+ select type (massDistribution_)
+ type is (massDistributionPatejLoeb2015)
+ massDistribution_=massDistributionPatejLoeb2015(gamma=1.15d0,massDistribution_=massDistributionDMO,mass=1.0d11,radiusOuter=450.0d-3,radiusShock=450.0d-3)
+ end select
+ do i=1,4
+ radius =450.0d-3/2.0d0**(4-i)
+ position =[radius,0.0d0,0.0d0]
+ massPatejLoeb (i)=massDistribution_%massEnclosedBySphere (radius )
+ densityPatejLoeb (i)=massDistribution_%density (position )
+ densitySlopePatejLoeb (i)=massDistribution_%densityGradientRadial(position,logarithmic=.true.)
+ potentialPatejLoeb (i)=massDistribution_%potential (position )
+ densityMomentPatejLoeb(i)=massDistribution_%densityRadialMoment (-dble(i-1),450.0d-3/8.0d0,450.0d-3/1.0d0)
+ end do
+ call Assert( &
+ & "M(r) at r=[⅛,¼,½,1]rₛ" , &
+ & massPatejLoeb , &
+ & [+1.555565950166512d10,+3.514143268178815d10,+6.418101338664305d10,+1.0000000000000000d11], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "ρ(r) at r=[⅛,¼,½,1]rₛ" , &
+ & densityPatejLoeb , &
+ & [+9.377714853155970d12,+1.985078164671267d12,+3.3223318757716390d11,+4.809898607847662d10], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "α(r) at r=[⅛,¼,½,1]rₛ" , &
+ & densitySlopePatejLoeb , &
+ & [-2.030591309692208d00,-2.431529802045667d00,-2.7035845062827400d00,-2.856250000000000d00], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "φ(r) at r=[⅛,¼,½,1]rₛ" , &
+ & potentialPatejLoeb , &
+ & [-4.079034939868978d03,-3.184928656647174d03,-2.280711782065997d03,-1.5198632412001940d03], &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "ℛᵢ(⅛rₛ,rₛ)" , &
+ & densityMomentPatejLoeb , &
+ & [+3.765562121061061d11,+4.129007833522932d12,+5.196937885662541d13,+7.1052241045332330d14], &
+ & relTol=1.0d-6 &
+ & )
+ deallocate(massDistribution_)
+ call Unit_Tests_End_Group()
+
+ ! Isothermal profile.
+ call Unit_Tests_Begin_Group("Isothermal profile")
+ allocate(massDistributionIsothermal :: massDistribution_ )
+ allocate(kinematicsDistributionLocal :: kinematicsDistribution_)
+ select type (kinematicsDistribution_)
+ type is (kinematicsDistributionLocal)
+ kinematicsDistribution_=kinematicsDistributionLocal(alpha=1.0d0/sqrt(2.0d0))
+ end select
+ select type (massDistribution_)
+ type is (massDistributionIsothermal)
+ massDistribution_=massDistributionIsothermal(mass=1.0d12,lengthReference=300.0d-3)
+ call massDistribution_%setKinematicsDistribution(kinematicsDistribution_)
+ do i=1,4
+ radius =300.0d-3/2.0d0**(4-i)
+ position =[radius,0.0d0,0.0d0]
+ positionReference =[300.0d-3,0.0d0,0.0d0]
+ time = 2.0d0**(i-1)
+ radiiIsothermal (i)=radius
+ massIsothermal (i)=massDistribution_%massEnclosedBySphere (radius )
+ massIsothermalNumerical (i)=massDistribution_%massEnclosedBySphereNumerical (radius )
+ densityIsothermal (i)=massDistribution_%density (position )
+ velocityCircularIsothermal (i)=massDistribution_%rotationCurve (radius )
+ radiusEnclosingMassIsothermal (i)=massDistribution_%radiusEnclosingMass ( massIsothermal(i) )
+ radiusEnclosingMassIsothermalNumerical (i)=massDistribution_%radiusEnclosingMassNumerical ( massIsothermal(i) )
+ radiusEnclosingDensityIsothermal (i)=massDistribution_%radiusEnclosingDensity (3.0d0*massIsothermal(i)/4.0d0/Pi/radius**3)
+ radiusEnclosingDensityIsothermalNumerical (i)=massDistribution_%radiusEnclosingDensityNumerical (3.0d0*massIsothermal(i)/4.0d0/Pi/radius**3)
+ radiusFromSpecificAngularMomentumIsothermal (i)=massDistribution_%radiusFromSpecificAngularMomentum (velocityCircularIsothermal(i)*radius )
+ radiusFromSpecificAngularMomentumIsothermalNumerical(i)=massDistribution_%radiusFromSpecificAngularMomentumNumerical(velocityCircularIsothermal(i)*radius )
+ densitySlopeIsothermal (i)=massDistribution_%densityGradientRadial (position,logarithmic=.true. )
+ densitySlopeIsothermalNumerical (i)=massDistribution_%densityGradientRadialNumerical (position,logarithmic=.true. )
+ potentialIsothermal (i)=massDistribution_%potential (position )
+ potentialIsothermalDifferenceNumerical (i)=massDistribution_%potentialDifferenceNumerical (position,positionReference )
+ densityMomentIsothermal (i)=massDistribution_%densityRadialMoment (-dble(i-1),300.0d-3/8.0d0,300.0d-3/1.0d0 )
+ fourierTransformIsothermal (i)=massDistribution_%fourierTransform (300.0d-3,1.0d0/radius )
+ fourierTransformIsothermalNumerical (i)=massDistribution_%fourierTransformNumerical (300.0d-3,1.0d0/radius )
+ radiusFreefallIsothermal (i)=massDistribution_%radiusFreefall (time )
+ radiusFreefallIsothermalNumerical (i)=massDistribution_%radiusFreefallNumerical (time )
+ radiusFreefallGrowthRateIsothermal (i)=massDistribution_%radiusFreefallIncreaseRate (time )
+ radiusFreefallGrowthRateIsothermalNumerical (i)=massDistribution_%radiusFreefallIncreaseRateNumerical (time )
+ end do
+ end select
+ call Assert( &
+ & "M(r) at r=[⅛,¼,½,1]rₗ" , &
+ & massIsothermal , &
+ & [+1.250000000000000d+11,+2.50000000000000d+11,+5.00000000000000d+11,+1.000000000000000d+12], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "M(r) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & massIsothermal , &
+ & massIsothermalNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "ρ(r) at r=[⅛,¼,½,1]rₗ" , &
+ & densityIsothermal , &
+ & [+1.886280807015056d+14,+4.71570201753764d+13,+1.17892550438441d+13,+2.947313760961025d+12], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "V(r) at r=[⅛,¼,½,1]rₗ" , &
+ & velocityCircularIsothermal , &
+ & [+1.19735820315671d+02,+1.19735820315671d+02,+1.197358203156711d+02,+1.197358203156711d+02], &
+ & relTol=1.0d-4 &
+ & )
+ call Assert( &
+ & "r(M) at r=[⅛,¼,½,1]rₗ" , &
+ & radiusEnclosingMassIsothermal , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "r(M) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & radiusEnclosingMassIsothermalNumerical , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "r(ρ) at r=[⅛,¼,½,1]rₗ" , &
+ & radiusEnclosingDensityIsothermal , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "r(ρ) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & radiusEnclosingDensityIsothermalNumerical , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "r(j) at r=[⅛,¼,½,1]rₗ" , &
+ & radiusFromSpecificAngularMomentumIsothermal , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "r(j) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & radiusFromSpecificAngularMomentumIsothermalNumerical , &
+ & radiiIsothermal , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "α(r) at r=[⅛,¼,½,1]rₗ" , &
+ & densitySlopeIsothermal , &
+ & [-2.000000000000000d+00,-2.00000000000000d+00,-2.00000000000000d+00,-2.000000000000000d+00], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "α(r) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & densitySlopeIsothermal , &
+ & densitySlopeIsothermalNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "φ(r) at r=[⅛,¼,½,1]rₗ" , &
+ & potentialIsothermal , &
+ & [-2.981226023588325d+04,-1.98748401572555d+04,-9.93742007862775d+03,+0.000000000000000d+00], &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "φ(r)-φ(rₗ) at r=[⅛,¼,½,1]rₗ numerical" , &
+ & potentialIsothermal -potentialIsothermal(4) , &
+ & potentialIsothermalDifferenceNumerical , &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "ℛᵢ(⅛rₗ,rₗ)" , &
+ & densityMomentIsothermal , &
+ & [+6.189358898018186d+12,+9.28403834702773d+13,+1.67341925761232d+15,+3.352569403093177d+16], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "ℱ(rₗ,k) at k=[8,4,2,1]/rₗ" , &
+ & fourierTransformIsothermal , &
+ & fourierTransformIsothermalNumerical , &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "ℱ(rₗ,k) at k=[8,4,2,1]/rₗ numerical" , &
+ & fourierTransformIsothermalNumerical , &
+ & [+1.967733527133679d-01,+4.395507847372633d-01,+8.02706488401347d-01,+9.46083070367183d-01], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert( &
+ & "rᵩ(t) at t=[1,2,4,8]Gyr" , &
+ & radiusFreefallIsothermal , &
+ & [+9.769496930104140d-02,+1.953899386020827d-01,+3.907798772041654d-01,+7.8155975440833d-01], &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "rᵩ(t) at t=[1,2,4,8]Gyr numerical" , &
+ & radiusFreefallIsothermal , &
+ & radiusFreefallIsothermalNumerical , &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "drᵩ/dt(t) at t=[1,2,4,8]Gyr numerical" , &
+ & radiusFreefallGrowthRateIsothermal , &
+ & radiusFreefallGrowthRateIsothermalNumerical , &
+ & relTol=1.0d-3 &
+ & )
+ call Assert( &
+ & "E" , &
+ & massDistribution_%energy(300.0d-3,massDistribution_) , &
+ & -3.58417d15 , &
+ & relTol=1.0d-3 &
+ & )
+ call Unit_Tests_End_Group()
+
! End unit tests.
call Unit_Tests_End_Group()
call Unit_Tests_Finish()
diff --git a/source/tests.math_special_functions.F90 b/source/tests.math_special_functions.F90
index c50791d322..48519044b8 100644
--- a/source/tests.math_special_functions.F90
+++ b/source/tests.math_special_functions.F90
@@ -29,6 +29,7 @@ program Test_Math_Special_Functions
& Bessel_Function_J1 , Bessel_Function_J1_Zero , Bessel_Function_Jn , Bessel_Function_Jn_Zero , &
& Bessel_Function_K0 , Bessel_Function_K1 , Bessel_Function_In
use :: Binomial_Coefficients , only : Binomial_Coefficient
+ use :: Dilogarithms , only : Dilogarithm
use :: Display , only : displayVerbositySet , verbosityLevelStandard
use :: Error_Functions , only : Error_Function
use :: Exponential_Integrals , only : Cosine_Integral , Sine_Integral
@@ -37,6 +38,7 @@ program Test_Math_Special_Functions
& Inverse_Gamma_Function_Incomplete, Inverse_Gamma_Function_Incomplete_Complementary
use :: Hypergeometric_Functions, only : Hypergeometric_1F1 , Hypergeometric_2F1 , Hypergeometric_pFq , Hypergeometric_pFq_Regularized
use :: Polylogarithms , only : Polylogarithm_2 , Polylogarithm_3
+ use :: Numerical_Constants_Math, only : Pi
use :: Unit_Tests , only : Assert , Unit_Tests_Begin_Group , Unit_Tests_End_Group , Unit_Tests_Finish
implicit none
double precision, dimension(10) :: argument =[1.0d0,2.0d0,3.0d0,4.0d0,5.0d0,6.0d0,7.0d0,8.0d0,9.0d0,10.0d0]
@@ -51,6 +53,7 @@ program Test_Math_Special_Functions
& factorials , gammaFunction , &
& hypergeometric1F1 , hypergeometric2F1 , &
& incompleteComplementaryGammaFunction , incompleteGammaFunction , &
+ & incompleteComplementaryGammaFunction2 , incompleteGammaFunction2 , &
& inverseGammaFunctionIncomplete , inverseGammaFunctionIncompleteComplementary, &
& logGammaFunction , sineIntegral , &
& hypergeometric1F2 , hypergeometric2F1approx , &
@@ -58,7 +61,7 @@ program Test_Math_Special_Functions
& hypergeometric3F2NegativeArgument , hypergeometric3F2Accelerated , &
& polylogarithm2 , polylogarithm3 , &
& hypergeometric1F2Regularized , BesselI2 , &
- & BesselIHalf
+ & BesselIHalf , dilogarithm_
double complex , dimension(17) :: errorFunctionComplex
integer :: i
@@ -88,8 +91,10 @@ program Test_Math_Special_Functions
doubleFactorial (i)=Logarithmic_Double_Factorial ( i )
gammaFunction (i)=Gamma_Function ( argument(i) )
logGammaFunction (i)=Gamma_Function_Logarithmic ( argument(i) )
- incompleteGammaFunction (i)=Gamma_Function_Incomplete ( argument(i),2.0d0 )
- incompleteComplementaryGammaFunction (i)=Gamma_Function_Incomplete_Complementary ( argument(i),2.0d0 )
+ incompleteGammaFunction (i)=Gamma_Function_Incomplete ( argument(i), 2.0d0 )
+ incompleteComplementaryGammaFunction (i)=Gamma_Function_Incomplete_Complementary ( argument(i), 2.0d0 )
+ incompleteGammaFunction2 (i)=Gamma_Function_Incomplete ( argument(i),16.67d0 )
+ incompleteComplementaryGammaFunction2 (i)=Gamma_Function_Incomplete_Complementary ( argument(i),16.67d0 )
inverseGammaFunctionIncomplete (i)=Inverse_Gamma_Function_Incomplete ( argument(i),P(i) )
inverseGammaFunctionIncompleteComplementary(i)=Inverse_Gamma_Function_Incomplete_Complementary( argument(i),Q(i) )
hypergeometric1F1 (i)=Hypergeometric_1F1 ([1.0d0 ],[2.0d0 ], argument(i) )
@@ -103,6 +108,7 @@ program Test_Math_Special_Functions
hypergeometric1F2Regularized (i)=Hypergeometric_pFq_Regularized ([1.5d0 ],[1.5d0,0.5d0], argument(i) )
polylogarithm2 (i)=Polylogarithm_2 ( -1.0d0/ argument(i) )
polylogarithm3 (i)=Polylogarithm_3 ( -1.0d0/ argument(i) )
+ dilogarithm_ (i)=Dilogarithm ( -1.0d0/ argument(i) )
end do
! Test Bessel function results.
@@ -431,6 +437,39 @@ program Test_Math_Special_Functions
& ], &
& relTol=1.0d-6 &
& )
+ call Assert("incomplete gamma function, Γ(x,16.67)" , &
+ & incompleteGammaFunction2, &
+ & [ &
+ & 5.758521420655205d-8, &
+ & 1.017530735029775d-6, &
+ & 9.018676651091340d-6, &
+ & 5.347837745800675d-5, &
+ & 2.387641805708268d-4, &
+ & 8.565070481489690d-4, &
+ & 2.572802648570240d-3, &
+ & 6.660038028430611d-3, &
+ & 1.517681475121466d-2, &
+ & 3.095177785886023d-2 &
+ & ], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert("complementary gamma function, 1-Γ(x,16.67)", &
+ & incompleteComplementaryGammaFunction2 , &
+ & [ &
+ & 9.99999942414786d-1, &
+ & 9.99998982469265d-1, &
+ & 9.99990981323349d-1, &
+ & 9.99946521622542d-1, &
+ & 9.99761235819429d-1, &
+ & 9.99143492951851d-1, &
+ & 9.97427197351430d-1, &
+ & 9.93339961971569d-1, &
+ & 9.84823185248785d-1, &
+ & 9.69048222141140d-1 &
+ & ], &
+ & &
+ & relTol=1.0d-6 &
+ & )
call Assert("inverse incomplete gamma function, Γ⁻¹(x,2)", &
& inverseGammaFunctionIncomplete, &
& [ &
@@ -626,7 +665,7 @@ program Test_Math_Special_Functions
& ], &
& relTol=1.0d-6 &
& )
-
+
! Test polylogarithm functions.
call Assert("polylogarithm, Li₂(x)" , &
& polylogarithm2 , &
@@ -644,7 +683,7 @@ program Test_Math_Special_Functions
& ], &
& relTol=1.0d-6 &
& )
- call Assert("polylogarithm, Li₃(x)", &
+ call Assert("polylogarithm, Li₃(x)" , &
& polylogarithm3 , &
& [ &
& -0.90154267736969570d0 , &
@@ -660,6 +699,29 @@ program Test_Math_Special_Functions
& ], &
& relTol=1.0d-6 &
& )
+
+ ! Test dilogarithm functions.
+ call Assert("dilogarithm, Li₂(x)" , &
+ & dilogarithm_ , &
+ & [ &
+ & -0.82246703342411320d0, &
+ & -0.44841420692364620d0, &
+ & -0.30903312648780850d0, &
+ & -0.23590029768626350d0, &
+ & -0.19080013777753560d0, &
+ & -0.16019301354439550d0, &
+ & -0.13805517651807560d0, &
+ & -0.12129662872272650d0, &
+ & -0.10816821022923270d0, &
+ & -0.09760523522932158d0 &
+ & ], &
+ & relTol=1.0d-6 &
+ & )
+ call Assert("dilogarithm, Li₂(2) (complex)" , &
+ & [real(Dilogarithm(dcmplx(2.0d0,0.0d0))),imag(Dilogarithm(dcmplx(2.0d0,0.0d0)))], &
+ & [Pi**2/4.0d0 ,-Pi*log(2.0d0)] , &
+ & relTol=1.0d-6 &
+ & )
! Test error function with complex argument.
errorFunctionComplex=Error_Function( &
diff --git a/source/tests.merger_tree_branching.F90 b/source/tests.merger_tree_branching.F90
index a3c5a068bb..8430f47499 100644
--- a/source/tests.merger_tree_branching.F90
+++ b/source/tests.merger_tree_branching.F90
@@ -353,8 +353,7 @@ program Tests_Merger_Tree_Branching
call Assert('Smooth accretion rate' ,smoothAccretionRate ,smoothAccretionRateTargetGeneral ,relTol=2.5d-2)
call Unit_Tests_End_Group ( )
call Unit_Tests_End_Group ( )
- call Unit_Tests_Begin_Group("First crossing distribution (numerical)" )
- write (400,*) "variance , uncon_num, con_num, uncon_alytc, con_alytc"
+ call Unit_Tests_Begin_Group("First crossing distribution (numerical)" )
varianceParent =cosmologicalMassVarianceFilteredPower_%rootVariance(massParent ,time)**2
varianceResolution=cosmologicalMassVarianceFilteredPower_%rootVariance(massResolution,time)**2
do j=1,countVariance
@@ -369,7 +368,6 @@ program Tests_Merger_Tree_Branching
branchingRateUnconstrainedNumerical(j)=excursionSetFirstCrossingFarahiMidpoint_ %rate(varianceParent,variance_(j),time,node)
branchingRateConstrainedNumerical (j)=excursionSetFirstCrossingFarahiMidpointBrownianBridge_%rate(varianceParent,variance_(j),time,node)
branchingRateConstrainedAnalytic (j)=excursionSetFirstCrossingLinearBarrierBrownianBridge_ %rate(varianceParent,variance_(j),time,node)
- write (400,*) variance_(j),",",branchingRateUnconstrainedNumerical(j),",",branchingRateConstrainedNumerical(j),",",branchingRateUnconstrainedAnalytic(j),",",branchingRateConstrainedAnalytic(j)
end do
errorMaximumUnconstrained=maxval( &
& abs(branchingRateUnconstrainedNumerical-branchingRateUnconstrainedAnalytic)/branchingRateUnconstrainedAnalytic &
diff --git a/source/tests.nodes.task.F90 b/source/tests.nodes.task.F90
index adecdd55a8..f0aac8dfd1 100644
--- a/source/tests.nodes.task.F90
+++ b/source/tests.nodes.task.F90
@@ -39,14 +39,12 @@ subroutine Test_Node_Task(node)
Implements simple tests of mapping functions over all components in a \gls{node}.
!!}
use :: Display , only : displayVerbositySet, verbosityLevelStandard
- use :: Galacticus_Nodes, only : nodeComponent , nodeComponentBlackHole, reductionSummation, treeNode
+ use :: Galacticus_Nodes, only : nodeComponent , nodeComponentBlackHole, treeNode
use :: Unit_Tests , only : Assert
implicit none
- type (treeNode ), intent(inout) :: node
- procedure (testVoidFunc ), pointer :: myFuncVoid => testVoidFunc
- procedure (testFuncDouble0), pointer :: myFuncDouble0 => testFuncDouble0
- class (nodeComponent ), pointer :: component
- double precision :: mapResult
+ type (treeNode ), intent(inout) :: node
+ procedure (testVoidFunc ), pointer :: myFuncVoid => testVoidFunc
+ class (nodeComponent), pointer :: component
! Set verbosity level.
call displayVerbositySet(verbosityLevelStandard)
@@ -57,13 +55,6 @@ subroutine Test_Node_Task(node)
! Map a void function (subroutine) over all components.
call node%mapVoid(myFuncVoid)
call Assert('Map void function over all components',all([componentBasicStandardSeen,componentBlackHoleStandardSeen]),.true.)
-
- ! Map a scalar double function over all components, with summation reduction
- mapResult=node%mapDouble0(myFuncDouble0,reduction=reductionSummation)
- select type (component)
- class is (nodeComponentBlackHole)
- call Assert('Summation reduction map over all components',component%mass(),mapResult)
- end select
return
end subroutine Test_Node_Task
@@ -81,19 +72,4 @@ A simple void function used in testing mapping over a function over all componen
return
end subroutine testVoidFunc
- double precision function testFuncDouble0(component)
- !!{
- A simple test function which returns the enclosed mass for a component. Used in testing mapping over a function over all
- components.
- !!}
- use :: Galactic_Structure_Options, only : componentTypeAll, massTypeAll, radiusLarge, weightByMass, &
- & weightIndexNull
- use :: Galacticus_Nodes , only : nodeComponent
- implicit none
- class(nodeComponent), intent(inout) :: component
-
- testFuncDouble0=component%enclosedMass(radiusLarge,componentTypeAll,massTypeAll,weightByMass,weightIndexNull)
- return
- end function testFuncDouble0
-
end module Test_Nodes_Tasks
diff --git a/source/tests.orbits.F90 b/source/tests.orbits.F90
index af5bc56d3f..f15c8846eb 100644
--- a/source/tests.orbits.F90
+++ b/source/tests.orbits.F90
@@ -30,13 +30,12 @@ program Test_Orbits
use :: Cosmology_Functions , only : cosmologyFunctionsMatterLambda
use :: Dark_Matter_Halo_Scales , only : darkMatterHaloScaleVirialDensityContrastDefinition
use :: Dark_Matter_Profiles_DMO , only : darkMatterProfileDMOIsothermal
- use :: Dark_Matter_Profiles , only : darkMatterProfileDarkMatterOnly
- use :: Galactic_Structure , only : galacticStructureStandard
use :: Virial_Density_Contrast , only : virialDensityContrastSphericalCollapseClsnlssMttrCsmlgclCnstnt
use :: Events_Hooks , only : eventsHooksInitialize
use :: Functions_Global_Utilities, only : Functions_Global_Set
use :: Display , only : displayVerbositySet , verbosityLevelStandard
- use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , treeNode
+ use :: Galacticus_Nodes , only : nodeClassHierarchyFinalize , nodeClassHierarchyInitialize , nodeComponentBasic , treeNode , &
+ & nodeComponentDarkMatterProfile
use :: Input_Parameters , only : inputParameters
use :: Node_Components , only : Node_Components_Initialize , Node_Components_Thread_Initialize, Node_Components_Thread_Uninitialize, Node_Components_Uninitialize
use :: Kepler_Orbits , only : keplerOrbit , keplerOrbitMasses , keplerOrbitRadius
@@ -46,11 +45,10 @@ program Test_Orbits
implicit none
type (treeNode ), pointer :: node
class (nodeComponentBasic ), pointer :: basic
- double precision , parameter :: massHost =1.0d12, massSatellite =0.0d0, &
+ class (nodeComponentDarkMatterProfile ), pointer :: darkMatterProfile
+ double precision , parameter :: massHost =1.0d12, massSatellite =0.00d0, &
& velocityFractionalRadial=0.9d00, velocityFractionalTangential=0.75d0
type (darkMatterProfileDMOIsothermal ), pointer :: darkMatterProfileDMO_
- type (darkMatterProfileDarkMatterOnly ), pointer :: darkMatterProfile_
- type (galacticStructureStandard ), pointer :: galacticStructure_
type (cosmologyParametersSimple ), pointer :: cosmologyParameters_
type (cosmologyFunctionsMatterLambda ), pointer :: cosmologyFunctions_
type (darkMatterHaloScaleVirialDensityContrastDefinition ), pointer :: darkMatterHaloScale_
@@ -76,8 +74,6 @@ program Test_Orbits
allocate(virialDensityContrast_)
allocate(darkMatterHaloScale_ )
allocate(darkMatterProfileDMO_ )
- allocate(darkMatterProfile_ )
- allocate(galacticStructure_ )
!![
@@ -121,29 +117,12 @@ program Test_Orbits
& )
-
-
- darkMatterProfileDarkMatterOnly ( &
- & cosmologyParameters_ =cosmologyParameters_ , &
- & darkMatterProfileDMO_ =darkMatterProfileDMO_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ &
- & )
-
-
-
-
- galacticStructureStandard ( &
- & cosmologyFunctions_ =cosmologyFunctions_ , &
- & darkMatterHaloScale_ =darkMatterHaloScale_ , &
- & darkMatterProfile_ =darkMatterProfile_ &
- & )
-
-
!!]
! Create a node.
- node => treeNode ( )
+ node => treeNode ( )
! Create components.
- basic => node %basic (autoCreate=.true.)
+ basic => node %basic (autoCreate=.true.)
+ darkMatterProfile => node %darkMatterProfile(autoCreate=.true.)
! Set node properties.
call basic%timeSet(cosmologyFunctions_%cosmicTime(1.0d0))
call basic%massSet(massHost )
@@ -162,16 +141,16 @@ program Test_Orbits
call orbit%reset (keep=[keplerOrbitMasses,keplerOrbitRadius] )
call orbit%velocityRadialSet (darkMatterHaloScale_%velocityVirial(node)*velocityFractionalRadial )
call orbit%velocityTangentialSet(darkMatterHaloScale_%velocityVirial(node)*velocityFractionalTangential)
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumPericenter,radiusPericenter,velocityPericenter,galacticStructure_)
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumApocenter ,radiusApocenter ,velocityApocenter ,galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumPericenter,radiusPericenter,velocityPericenter,darkMatterHaloScale_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumApocenter ,radiusApocenter ,velocityApocenter ,darkMatterHaloScale_)
call Assert('non-circular orbit, pericenter radius',radiusPericenter,0.428095d0*darkMatterHaloScale_%radiusVirial(node),relTol=1.0d-5)
call Assert('non-circular orbit, apocenter radius',radiusApocenter ,1.825500d0*darkMatterHaloScale_%radiusVirial(node),relTol=1.0d-5)
! Circular orbit.
call orbit%reset (keep=[keplerOrbitMasses,keplerOrbitRadius] )
call orbit%velocityRadialSet ( 0.0d0 )
call orbit%velocityTangentialSet(darkMatterHaloScale_%velocityVirial(node) )
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumPericenter,radiusPericenter,velocityPericenter,galacticStructure_)
- call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumApocenter ,radiusApocenter ,velocityApocenter ,galacticStructure_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumPericenter,radiusPericenter,velocityPericenter,darkMatterHaloScale_)
+ call Satellite_Orbit_Extremum_Phase_Space_Coordinates(node,orbit,extremumApocenter ,radiusApocenter ,velocityApocenter ,darkMatterHaloScale_)
call Assert(' circular orbit, pericenter radius',radiusPericenter,1.000000d0*darkMatterHaloScale_%radiusVirial(node),relTol=1.0d-5)
call Assert(' circular orbit, apocenter radius',radiusApocenter ,1.000000d0*darkMatterHaloScale_%radiusVirial(node),relTol=1.0d-5)
! End unit tests.
@@ -183,12 +162,10 @@ program Test_Orbits
call nodeClassHierarchyFinalize ()
! Clean up objects.
!![
-
-
-
-
-
-
-
+
+
+
+
+
!!]
end program Test_Orbits
diff --git a/source/tests.parameters.F90 b/source/tests.parameters.F90
index ff8df63169..0ac6c5af61 100644
--- a/source/tests.parameters.F90
+++ b/source/tests.parameters.F90
@@ -120,6 +120,7 @@ program Test_Parameters
call Assert('derived value [recursive; post-reset]',valueNumerical,-1.344442852d2,absTol=1.0d-6)
call Unit_Tests_End_Group()
! End unit tests.
+ call Unit_Tests_End_Group()
call Unit_Tests_Finish ()
! Close down.
call testParameters%destroy()
diff --git a/source/tidal_stripping.mass_loss_rate.simple.F90 b/source/tidal_stripping.mass_loss_rate.simple.F90
index 05db6b5019..a9b8e5b873 100644
--- a/source/tidal_stripping.mass_loss_rate.simple.F90
+++ b/source/tidal_stripping.mass_loss_rate.simple.F90
@@ -22,7 +22,6 @@
!!}
use :: Satellites_Tidal_Fields, only : satelliteTidalFieldClass
- use :: Galactic_Structure , only : galacticStructureClass
!![
@@ -52,7 +51,6 @@
!!}
private
class (satelliteTidalFieldClass), pointer :: satelliteTidalField_ => null()
- class (galacticStructureClass ), pointer :: galacticStructure_ => null()
double precision :: rateFractionalMaximum , beta
contains
final :: simpleDestructor
@@ -78,7 +76,6 @@ function simpleConstructorParameters(parameters) result(self)
type (tidalStrippingSimple ) :: self
type (inputParameters ), intent(inout) :: parameters
class (satelliteTidalFieldClass), pointer :: satelliteTidalField_
- class (galacticStructureClass ), pointer :: galacticStructure_
double precision :: rateFractionalMaximum, beta
!![
@@ -95,18 +92,16 @@ function simpleConstructorParameters(parameters) result(self)
-
!!]
- self=tidalStrippingSimple(rateFractionalMaximum,beta,satelliteTidalField_,galacticStructure_)
+ self=tidalStrippingSimple(rateFractionalMaximum,beta,satelliteTidalField_)
!![
-
!!]
return
end function simpleConstructorParameters
- function simpleConstructorInternal(rateFractionalMaximum,beta,satelliteTidalField_,galacticStructure_) result(self)
+ function simpleConstructorInternal(rateFractionalMaximum,beta,satelliteTidalField_) result(self)
!!{
Internal constructor for the {\normalfont \ttfamily simple} model of tidal stripping class.
!!}
@@ -114,9 +109,8 @@ function simpleConstructorInternal(rateFractionalMaximum,beta,satelliteTidalFiel
type (tidalStrippingSimple ) :: self
double precision , intent(in ) :: rateFractionalMaximum, beta
class (satelliteTidalFieldClass), intent(in ), target :: satelliteTidalField_
- class (galacticStructureClass ), intent(in ), target :: galacticStructure_
!![
-
+
!!]
return
@@ -131,7 +125,6 @@ subroutine simpleDestructor(self)
!![
-
!!]
return
end subroutine simpleDestructor
@@ -140,19 +133,21 @@ double precision function simpleRateMassLoss(self,component)
!!{
Computes the mass loss rate due to tidal stripping assuming a simple model.
!!}
- use :: Galacticus_Nodes , only : nodeComponentDisk, nodeComponentSpheroid, treeNode
- use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
+ use :: Galacticus_Nodes , only : nodeComponentDisk , nodeComponentSpheroid, treeNode
+ use :: Numerical_Constants_Astronomical, only : gigaYear , megaParsec
use :: Numerical_Constants_Prefixes , only : kilo
+ use :: Mass_Distributions , only : massDistributionClass
implicit none
- class (tidalStrippingSimple), intent(inout) :: self
- class (nodeComponent ), intent(inout) :: component
- type (treeNode ), pointer :: node
- double precision :: forceGravitational , forceTidal , &
- & rateMassLossFractional, radiusHalfMass, &
- & tidalTensorRadial , timeDynamical , &
- & velocityRotation , velocity , &
- & massGas , massStellar , &
- & radius
+ class (tidalStrippingSimple ), intent(inout) :: self
+ class (nodeComponent ), intent(inout) :: component
+ type (treeNode ), pointer :: node
+ class (massDistributionClass), pointer :: massDistribution_
+ double precision :: forceGravitational , forceTidal , &
+ & rateMassLossFractional, radiusHalfMass, &
+ & tidalTensorRadial , timeDynamical , &
+ & velocityRotation , velocity , &
+ & massGas , massStellar , &
+ & radius
! Assume no mass loss rate due to tidal by default.
simpleRateMassLoss=0.0d0
@@ -189,10 +184,11 @@ double precision function simpleRateMassLoss(self,component)
! Return if the tidal field is compressive.
if (forceTidal <= 0.0d0) return
! Compute the rotation curve.
- velocityRotation=self%galacticStructure_%velocityRotation( &
- & node , &
- & radiusHalfMass &
- & )
+ massDistribution_ => node %massDistribution( )
+ velocityRotation = massDistribution_%rotationCurve (radiusHalfMass)
+ !![
+
+ !!]
! Compute the gravitational restoring force at the half-mass radius.
forceGravitational=+velocityRotation**2 &
& /radiusHalfMass
diff --git a/source/utility.input_parameters.F90 b/source/utility.input_parameters.F90
index 17a87ae110..5cce2fe180 100644
--- a/source/utility.input_parameters.F90
+++ b/source/utility.input_parameters.F90
@@ -125,13 +125,12 @@ module Input_Parameters
type (node ), pointer, public :: document => null()
type (node ), pointer :: rootNode => null()
type (hdf5Object ) :: outputParameters , outputParametersContainer
- type (inputParameter ), pointer, public :: parameters => null()
+ type (inputParameter ), pointer, public :: parameters => null()
type (inputParameters), pointer, public :: parent => null()
logical :: outputParametersCopied = .false., outputParametersTemporary=.false., &
- & isNull = .false., warnedVersion =.false.
+ & isNull = .false.
type (integerHash ), allocatable :: warnedDefaults
type (ompLock ), pointer :: lock => null()
- type (varying_string ) :: fileName
contains
!![
@@ -404,9 +403,9 @@ function inputParametersConstructorCopy(parameters) result(self)
type (inputParameters) :: self
type (inputParameters), intent(in ) :: parameters
- self = inputParameters(parameters%rootNode ,noOutput=.true.,noBuild=.true.,fileName=char(parameters%fileName))
- self%parameters => parameters%parameters
- self%parent => parameters%parent
+ self = inputParameters(parameters%rootNode ,noOutput=.true.,noBuild=.true.)
+ self%parameters => parameters%parameters
+ self%parent => parameters%parent
if (allocated(parameters%warnedDefaults)) then
if (allocated(self%warnedDefaults)) deallocate(self%warnedDefaults)
allocate(self%warnedDefaults)
@@ -424,9 +423,11 @@ function inputParametersConstructorNode(parametersNode,allowedParameterNames,out
!!{
Constructor for the {\normalfont \ttfamily inputParameters} class from an FoX node.
!!}
- use :: Display , only : displayGreen , displayMessage , displayMagenta , displayReset
+ use :: Display , only : displayGreen , displayMessage , displayMagenta , displayReset , &
+ & verbosityLevelSilent
use :: File_Utilities , only : File_Name_Temporary
- use :: FoX_dom , only : getOwnerDocument , node , setLiveNodeLists, getTextContent, hasAttribute, getAttributeNode
+ use :: FoX_dom , only : getOwnerDocument , node , setLiveNodeLists, getTextContent, &
+ & hasAttribute , getAttributeNode
use :: Error , only : Error_Report
#ifdef GIT2AVAIL
use, intrinsic :: ISO_C_Binding , only : c_null_char
@@ -461,7 +462,6 @@ function inputParametersConstructorNode(parametersNode,allowedParameterNames,out
#endif
type (varying_string ), dimension(:), allocatable , save :: allowedParameterNamesGlobal
!$omp threadprivate(allowedParameterNamesGlobal)
- !$GLC attributes unused :: fileName
!![
@@ -475,7 +475,6 @@ function inputParametersConstructorNode(parametersNode,allowedParameterNames,out
self%parent => null ( )
self%warnedDefaults = integerHash ( )
self%lock = ompLock ( )
- if (present(fileName)) self%fileName=fileName
!$omp critical (FoX_DOM_Access)
self%document => getOwnerDocument(parametersNode)
call setLiveNodeLists(self%document,.false.)
@@ -525,51 +524,51 @@ function inputParametersConstructorNode(parametersNode,allowedParameterNames,out
if (.not.allocated(allowedParameterNamesGlobal)) &
& call knownParameterNames(allowedParameterNamesGlobal)
! Check for migration information.
- if (XML_Path_Exists(self%rootNode,"lastModified")) then
+ if (present(fileName)) then
+ if (XML_Path_Exists(self%rootNode,"lastModified")) then
#ifdef GIT2AVAIL
- ! Look for a "lastModified" element in the parameter file.
- !$omp critical (FoX_DOM_Access)
- lastModifiedNode => XML_Get_First_Element_By_Tag_Name(self%rootNode ,'lastModified')
- hasRevision = hasAttribute ( lastModifiedNode,'revision' )
- if (hasRevision) then
- revisionNode => getAttributeNode(lastModifiedNode,'revision')
- commitHashParameters = getTextContent (revisionNode )//c_null_char
- end if
- !$omp end critical (FoX_DOM_Access)
- if (hasRevision.and..not.self%warnedVersion) then
- ! A revision was available in the parameter file.
- self%warnedVersion=.true.
- !! Build an array of known migration commit hashes.
- !![
-
- !!]
- !! Extract the commit hash at which Galacticus was built.
- call Version(commitHashSelf_)
- commitHashSelf=trim(commitHashSelf_)//c_null_char
- !! Iterate over known migration commit hashes and check if they are ancestors.
- allocate(isAncestorOfParameters(size(commitHash)))
- do i=1,size(commitHash)
- isAncestorOfParameters(i)=gitDescendantOf(char(inputPath(pathTypeExec))//c_null_char,commitHashParameters,commitHash(i))
- end do
- if (any(isAncestorOfParameters /= 0_c_int .and. isAncestorOfParameters /= 1_c_int)) then
- call displayMessage(var_str(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision check failed (#1; error code; ")//maxval(isAncestorOfParameters)//")")
- else if (any(isAncestorOfParameters == 0)) then
- ! Parameter file is missing migrations - issue a warning.
- message=displayMagenta()//"WARNING:"//displayReset()//" parameter file may be missing important parameter updates - consider updating by running:"//char(10)//char(10)//" ./scripts/aux/parametersMigrate.pl "
- if (present(fileName)) message=message//trim(fileName)//" newParameterFile.xml "
- call displayMessage(message)
+ ! Look for a "lastModified" element in the parameter file.
+ !$omp critical (FoX_DOM_Access)
+ lastModifiedNode => XML_Get_First_Element_By_Tag_Name(self%rootNode ,'lastModified')
+ hasRevision = hasAttribute ( lastModifiedNode,'revision' )
+ if (hasRevision) then
+ revisionNode => getAttributeNode(lastModifiedNode,'revision')
+ commitHashParameters = getTextContent (revisionNode )//c_null_char
end if
- isAncestorOfSelf=gitDescendantOf(char(inputPath(pathTypeExec))//c_null_char,commitHashSelf,commitHashParameters)
- if (isAncestorOfSelf /= 0_c_int .and. isAncestorOfSelf /= 1_c_int) then
- call displayMessage(var_str(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision check failed (#2; error code: ")//isAncestorOfSelf//")")
- else if (isAncestorOfSelf == 0_c_int) then
- ! Parameters are more recent than the executable - issue a warning.
- call displayMessage(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision is newer than this executable - consider updating your copy of Galacticus")
+ !$omp end critical (FoX_DOM_Access)
+ if (hasRevision) then
+ ! A revision was available in the parameter file.
+ !! Build an array of known migration commit hashes.
+ !![
+
+ !!]
+ !! Extract the commit hash at which Galacticus was built.
+ call Version(commitHashSelf_)
+ commitHashSelf=trim(commitHashSelf_)//c_null_char
+ !! Iterate over known migration commit hashes and check if they are ancestors.
+ allocate(isAncestorOfParameters(size(commitHash)))
+ do i=1,size(commitHash)
+ isAncestorOfParameters(i)=gitDescendantOf(char(inputPath(pathTypeExec))//c_null_char,commitHashParameters,commitHash(i))
+ end do
+ if (any(isAncestorOfParameters /= 0_c_int .and. isAncestorOfParameters /= 1_c_int)) then
+ call displayMessage(var_str(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision check failed (#1; error code; ")//maxval(isAncestorOfParameters)//")")
+ else if (any(isAncestorOfParameters == 0)) then
+ ! Parameter file is missing migrations - issue a warning.
+ message=displayMagenta()//"WARNING:"//displayReset()//" parameter file may be missing important parameter updates - consider updating by running:"//char(10)//char(10)//" ./scripts/aux/parametersMigrate.pl "//trim(fileName)//" newParameterFile.xml"
+ call displayMessage(message//char(10),verbosityLevelSilent)
+ end if
+ isAncestorOfSelf=gitDescendantOf(char(inputPath(pathTypeExec))//c_null_char,commitHashSelf,commitHashParameters)
+ if (isAncestorOfSelf /= 0_c_int .and. isAncestorOfSelf /= 1_c_int) then
+ call displayMessage(var_str(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision check failed (#2; error code: ")//isAncestorOfSelf//")")
+ else if (isAncestorOfSelf == 0_c_int) then
+ ! Parameters are more recent than the executable - issue a warning.
+ call displayMessage(displayMagenta()//"WARNING:"//displayReset()//" parameter file revision is newer than this executable - consider updating your copy of Galacticus",verbosityLevelSilent)
+ end if
end if
- end if
#else
- call Warn(displayMagenta()//"WARNING:"//displayReset()//" can not check if parameter file is up to date (`libgit` is not available)")
+ call Warn(displayMagenta()//"WARNING:"//displayReset()//" can not check if parameter file is up to date (`libgit` is not available)")
#endif
+ end if
end if
! Check parameters.
call self%checkParameters(allowedParameterNamesGlobal=allowedParameterNamesGlobal,allowedParameterNames=allowedParameterNames)
@@ -1743,7 +1742,7 @@ function inputParametersSubParameters(self,parameterName,requireValue,requirePre
inputParametersSubParameters = inputParameters (parameterNode%content,noOutput =.true. ,noBuild =.true. )
inputParametersSubParameters%parameters => parameterNode
end if
- inputParametersSubParameters%parent => self
+ inputParametersSubParameters%parent => self
!$ call hdf5Access%set()
if (self%outputParameters%isOpen()) then
groupName=parameterName
diff --git a/testSuite/parameters/barInstability.xml b/testSuite/parameters/barInstability.xml
index 4ed9de37df..53baacff85 100644
--- a/testSuite/parameters/barInstability.xml
+++ b/testSuite/parameters/barInstability.xml
@@ -9,7 +9,7 @@
-
+
@@ -88,6 +88,8 @@
+
+
diff --git a/testSuite/parameters/cgmMassCooled.xml b/testSuite/parameters/cgmMassCooled.xml
index ac05e727ca..00118d9518 100644
--- a/testSuite/parameters/cgmMassCooled.xml
+++ b/testSuite/parameters/cgmMassCooled.xml
@@ -8,7 +8,7 @@
-
+
@@ -78,6 +78,8 @@
+
+
diff --git a/testSuite/parameters/checkpointingCheckpoints.xml b/testSuite/parameters/checkpointingCheckpoints.xml
index 10640ed3d6..4cab38c473 100644
--- a/testSuite/parameters/checkpointingCheckpoints.xml
+++ b/testSuite/parameters/checkpointingCheckpoints.xml
@@ -312,9 +312,7 @@
-
-
-
+
diff --git a/testSuite/parameters/checkpointingNoCheckpoints.xml b/testSuite/parameters/checkpointingNoCheckpoints.xml
index d287ba5773..5bffd8a0a7 100644
--- a/testSuite/parameters/checkpointingNoCheckpoints.xml
+++ b/testSuite/parameters/checkpointingNoCheckpoints.xml
@@ -311,9 +311,7 @@
-
-
-
+
diff --git a/testSuite/parameters/checkpointingResume.xml b/testSuite/parameters/checkpointingResume.xml
index 0c650a639c..451a0e793d 100644
--- a/testSuite/parameters/checkpointingResume.xml
+++ b/testSuite/parameters/checkpointingResume.xml
@@ -311,9 +311,7 @@
-
-
-
+
diff --git a/testSuite/parameters/constrainedMergerTrees.xml b/testSuite/parameters/constrainedMergerTrees.xml
index 742f9c91e8..604ce970e9 100644
--- a/testSuite/parameters/constrainedMergerTrees.xml
+++ b/testSuite/parameters/constrainedMergerTrees.xml
@@ -98,7 +98,7 @@
-
+
diff --git a/testSuite/parameters/galacticStructure.xml b/testSuite/parameters/galacticStructure.xml
new file mode 100644
index 0000000000..7966c670cf
--- /dev/null
+++ b/testSuite/parameters/galacticStructure.xml
@@ -0,0 +1,15 @@
+
+
+
+ 2
+ 0.9.4
+
+
+
+
+
+
+
+
+
+
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.005_alpha_1.0_beta_3.0_gamma_1.5.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.005_alpha_1.0_beta_3.0_gamma_1.5.xml
index 9898ef183c..304a16b3a7 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.005_alpha_1.0_beta_3.0_gamma_1.5.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.005_alpha_1.0_beta_3.0_gamma_1.5.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -214,9 +211,6 @@
-
-
-
@@ -230,9 +224,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.01_alpha_1.0_beta_3.0_gamma_1.5.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.01_alpha_1.0_beta_3.0_gamma_1.5.xml
index 698b0f3f13..8166756e14 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.01_alpha_1.0_beta_3.0_gamma_1.5.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.01_alpha_1.0_beta_3.0_gamma_1.5.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -214,9 +211,6 @@
-
-
-
@@ -230,9 +224,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_0.5.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_0.5.xml
index 2dc732051f..25f389318c 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_0.5.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_0.5.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -209,9 +206,6 @@
-
-
-
@@ -225,9 +219,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_1.0.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_1.0.xml
index ffd3af26c4..275156422f 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_1.0.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.05_alpha_1.0_beta_3.0_gamma_1.0.xml
@@ -63,9 +63,6 @@
-
-
-
@@ -206,9 +203,6 @@
-
-
-
@@ -218,9 +212,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.0.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.0.xml
index 724106af14..cc9137bbaf 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.0.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.0.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -207,9 +204,6 @@
-
-
-
@@ -223,9 +217,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.5.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.5.xml
index 797fb3bfc4..f215c7a31b 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.5.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_0.5.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -214,9 +211,6 @@
-
-
-
@@ -230,9 +224,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_1.0.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_1.0.xml
index abe49a0511..0b3d98116f 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_1.0.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.2_alpha_1.0_beta_3.0_gamma_1.0.xml
@@ -63,9 +63,6 @@
-
-
-
@@ -204,9 +201,6 @@
-
-
-
@@ -216,9 +210,6 @@
-
-
-
diff --git a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.4_alpha_1.0_beta_3.0_gamma_0.0.xml b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.4_alpha_1.0_beta_3.0_gamma_0.0.xml
index 5a2edcfdff..703faad41b 100644
--- a/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.4_alpha_1.0_beta_3.0_gamma_0.0.xml
+++ b/testSuite/parameters/idealizedSubhaloSimulations/tidalTrackBestFit_xc_0.7_ratio_0.4_alpha_1.0_beta_3.0_gamma_0.0.xml
@@ -67,9 +67,6 @@
-
-
-
@@ -211,9 +208,6 @@
-
-
-
@@ -227,9 +221,6 @@
-
-
-
diff --git a/testSuite/parameters/impulsiveHeating.xml b/testSuite/parameters/impulsiveHeating.xml
index f3dfda6b9a..1f2e85ff63 100644
--- a/testSuite/parameters/impulsiveHeating.xml
+++ b/testSuite/parameters/impulsiveHeating.xml
@@ -154,6 +154,7 @@
+
@@ -167,10 +168,6 @@
-
-
-
-
diff --git a/testSuite/parameters/massDefinitionsTimeCurrent.xml b/testSuite/parameters/massDefinitionsTimeCurrent.xml
index 0c01f399c1..0f72fceed8 100644
--- a/testSuite/parameters/massDefinitionsTimeCurrent.xml
+++ b/testSuite/parameters/massDefinitionsTimeCurrent.xml
@@ -15,14 +15,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -95,6 +95,8 @@
+
+
diff --git a/testSuite/parameters/massDefinitionsTimeInfall.xml b/testSuite/parameters/massDefinitionsTimeInfall.xml
index c1146853d7..6bc4fefc2f 100644
--- a/testSuite/parameters/massDefinitionsTimeInfall.xml
+++ b/testSuite/parameters/massDefinitionsTimeInfall.xml
@@ -15,14 +15,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -95,6 +95,8 @@
+
+
diff --git a/testSuite/parameters/massHostMaximum.xml b/testSuite/parameters/massHostMaximum.xml
index 0061b7497f..bf21d5ef82 100644
--- a/testSuite/parameters/massHostMaximum.xml
+++ b/testSuite/parameters/massHostMaximum.xml
@@ -8,7 +8,7 @@
-
+
@@ -50,7 +50,7 @@
-
+
@@ -89,6 +89,8 @@
+
+
diff --git a/testSuite/parameters/noninstantaneous_recycling.xml b/testSuite/parameters/noninstantaneous_recycling.xml
index 4a46cf99d1..5102d6d52e 100644
--- a/testSuite/parameters/noninstantaneous_recycling.xml
+++ b/testSuite/parameters/noninstantaneous_recycling.xml
@@ -276,12 +276,11 @@
-
+
-
diff --git a/testSuite/parameters/orbits.xml b/testSuite/parameters/orbits.xml
index 660438676c..d0cc68627b 100644
--- a/testSuite/parameters/orbits.xml
+++ b/testSuite/parameters/orbits.xml
@@ -4,7 +4,7 @@
2
-
+
@@ -32,6 +32,4 @@
-
-
diff --git a/testSuite/parameters/reproducibility/closedBox.xml b/testSuite/parameters/reproducibility/closedBox.xml
index a9ae69ab93..b1f655b47d 100644
--- a/testSuite/parameters/reproducibility/closedBox.xml
+++ b/testSuite/parameters/reproducibility/closedBox.xml
@@ -9,7 +9,7 @@
-
+
@@ -88,6 +88,8 @@
+
+
diff --git a/testSuite/parameters/reproducibility/cooling.xml b/testSuite/parameters/reproducibility/cooling.xml
index 2dd39c0b02..9bf2082e16 100644
--- a/testSuite/parameters/reproducibility/cooling.xml
+++ b/testSuite/parameters/reproducibility/cooling.xml
@@ -9,7 +9,7 @@
-
+
@@ -111,6 +111,8 @@
+
+
diff --git a/testSuite/parameters/reproducibility/leakyBox.xml b/testSuite/parameters/reproducibility/leakyBox.xml
index 2e3e8d9378..63d072ea9c 100644
--- a/testSuite/parameters/reproducibility/leakyBox.xml
+++ b/testSuite/parameters/reproducibility/leakyBox.xml
@@ -9,7 +9,7 @@
-
+
@@ -100,6 +100,8 @@
+
+
diff --git a/testSuite/parameters/stellarMassWeightedAgesMerging.xml b/testSuite/parameters/stellarMassWeightedAgesMerging.xml
index 6c70182c4e..4773b5339e 100644
--- a/testSuite/parameters/stellarMassWeightedAgesMerging.xml
+++ b/testSuite/parameters/stellarMassWeightedAgesMerging.xml
@@ -8,7 +8,7 @@
-
+
@@ -109,6 +109,8 @@
+
+
diff --git a/testSuite/parameters/stellarMassWeightedAgesSimple.xml b/testSuite/parameters/stellarMassWeightedAgesSimple.xml
index df0bdf27b6..faef296ec7 100644
--- a/testSuite/parameters/stellarMassWeightedAgesSimple.xml
+++ b/testSuite/parameters/stellarMassWeightedAgesSimple.xml
@@ -8,7 +8,7 @@
-
+
@@ -97,6 +97,8 @@
+
+
diff --git a/testSuite/parameters/test-allowed-parameters.xml b/testSuite/parameters/test-allowed-parameters.xml
index 93723f63ba..b825dd6e1e 100644
--- a/testSuite/parameters/test-allowed-parameters.xml
+++ b/testSuite/parameters/test-allowed-parameters.xml
@@ -15,7 +15,7 @@
-
+
@@ -144,8 +144,6 @@
-
-
@@ -170,7 +168,6 @@
-
@@ -216,7 +213,6 @@
-
diff --git a/testSuite/parameters/test-splitForests-split.xml b/testSuite/parameters/test-splitForests-split.xml
index 4032868589..26d3fe08ed 100644
--- a/testSuite/parameters/test-splitForests-split.xml
+++ b/testSuite/parameters/test-splitForests-split.xml
@@ -25,7 +25,7 @@
-
+
@@ -216,9 +216,8 @@
-
-
-
+
+
diff --git a/testSuite/parameters/test-splitForests-unsplit.xml b/testSuite/parameters/test-splitForests-unsplit.xml
index 7da510bb35..933dcdd3c2 100644
--- a/testSuite/parameters/test-splitForests-unsplit.xml
+++ b/testSuite/parameters/test-splitForests-unsplit.xml
@@ -25,7 +25,7 @@
-
+
@@ -215,9 +215,8 @@
-
-
-
+
+
diff --git a/testSuite/parameters/tidalTracksMonotonic_gamma0.0.xml b/testSuite/parameters/tidalTracksMonotonic_gamma0.0.xml
index 83144a43a4..c287077501 100644
--- a/testSuite/parameters/tidalTracksMonotonic_gamma0.0.xml
+++ b/testSuite/parameters/tidalTracksMonotonic_gamma0.0.xml
@@ -61,9 +61,6 @@
-
-
-
diff --git a/testSuite/parameters/tidalTracksMonotonic_gamma0.5.xml b/testSuite/parameters/tidalTracksMonotonic_gamma0.5.xml
index 3ad5843ffe..da29834eb5 100644
--- a/testSuite/parameters/tidalTracksMonotonic_gamma0.5.xml
+++ b/testSuite/parameters/tidalTracksMonotonic_gamma0.5.xml
@@ -61,9 +61,6 @@
-
-
-
diff --git a/testSuite/parameters/tidalTracksMonotonic_gamma1.0.xml b/testSuite/parameters/tidalTracksMonotonic_gamma1.0.xml
index 1006effd98..4d329c7e58 100644
--- a/testSuite/parameters/tidalTracksMonotonic_gamma1.0.xml
+++ b/testSuite/parameters/tidalTracksMonotonic_gamma1.0.xml
@@ -61,9 +61,6 @@
-
-
-
diff --git a/testSuite/parameters/tidalTracksNonMonotonic_gamma1.0.xml b/testSuite/parameters/tidalTracksNonMonotonic_gamma1.0.xml
index dfc28a582e..5d23da6458 100644
--- a/testSuite/parameters/tidalTracksNonMonotonic_gamma1.0.xml
+++ b/testSuite/parameters/tidalTracksNonMonotonic_gamma1.0.xml
@@ -61,10 +61,7 @@
-
-
-
-
+
diff --git a/testSuite/parameters/validation/duplicate-value-invalid.xml b/testSuite/parameters/validation/duplicate-value-invalid.xml
index f76f272648..a8818a4aac 100644
--- a/testSuite/parameters/validation/duplicate-value-invalid.xml
+++ b/testSuite/parameters/validation/duplicate-value-invalid.xml
@@ -39,10 +39,10 @@
-
+
random
random
-
+
diff --git a/testSuite/regressions/finalTimeBeforeOutputTime.xml b/testSuite/regressions/finalTimeBeforeOutputTime.xml
index 107a0cf948..210f5149f5 100644
--- a/testSuite/regressions/finalTimeBeforeOutputTime.xml
+++ b/testSuite/regressions/finalTimeBeforeOutputTime.xml
@@ -4,7 +4,7 @@
2
-
+
@@ -24,6 +24,8 @@
+
+
diff --git a/testSuite/regressions/immediateSubSubMergerThenBranchJump.xml b/testSuite/regressions/immediateSubSubMergerThenBranchJump.xml
index 2013d13076..ab4f73faf5 100644
--- a/testSuite/regressions/immediateSubSubMergerThenBranchJump.xml
+++ b/testSuite/regressions/immediateSubSubMergerThenBranchJump.xml
@@ -7,7 +7,7 @@
2
-
+
@@ -22,6 +22,8 @@
+
+
diff --git a/testSuite/regressions/initialSatelliteNoPrimaryProgenitor.xml b/testSuite/regressions/initialSatelliteNoPrimaryProgenitor.xml
index 9a9e19a183..437d2cdb50 100644
--- a/testSuite/regressions/initialSatelliteNoPrimaryProgenitor.xml
+++ b/testSuite/regressions/initialSatelliteNoPrimaryProgenitor.xml
@@ -8,7 +8,7 @@
2
-
+
@@ -16,6 +16,8 @@
+
+
diff --git a/testSuite/regressions/mergerAtFinalTimeInTree.xml b/testSuite/regressions/mergerAtFinalTimeInTree.xml
index 19e2cbe46a..571834c5fb 100644
--- a/testSuite/regressions/mergerAtFinalTimeInTree.xml
+++ b/testSuite/regressions/mergerAtFinalTimeInTree.xml
@@ -6,7 +6,7 @@
2
-
+
@@ -21,6 +21,8 @@
+
+
diff --git a/testSuite/regressions/particulate.xml b/testSuite/regressions/particulate.xml
index 686d1af587..ac1a0c4d6a 100644
--- a/testSuite/regressions/particulate.xml
+++ b/testSuite/regressions/particulate.xml
@@ -149,7 +149,9 @@
-
+
+
+
diff --git a/testSuite/regressions/satellitePresetBoundMassNonZero.xml b/testSuite/regressions/satellitePresetBoundMassNonZero.xml
index 2a06293a46..bdcc5022ab 100644
--- a/testSuite/regressions/satellitePresetBoundMassNonZero.xml
+++ b/testSuite/regressions/satellitePresetBoundMassNonZero.xml
@@ -5,7 +5,7 @@
2
-
+
@@ -180,6 +180,8 @@
+
+
diff --git a/testSuite/regressions/subhaloMergesAtFinalTimeOfTree.xml b/testSuite/regressions/subhaloMergesAtFinalTimeOfTree.xml
index b7204250f1..be27ddd76e 100644
--- a/testSuite/regressions/subhaloMergesAtFinalTimeOfTree.xml
+++ b/testSuite/regressions/subhaloMergesAtFinalTimeOfTree.xml
@@ -3,7 +3,7 @@
2
-
+
@@ -24,6 +24,8 @@
+
+
diff --git a/testSuite/regressions/subhaloTwoConsecutiveMergers.xml b/testSuite/regressions/subhaloTwoConsecutiveMergers.xml
index b221542408..d7dec94d45 100644
--- a/testSuite/regressions/subhaloTwoConsecutiveMergers.xml
+++ b/testSuite/regressions/subhaloTwoConsecutiveMergers.xml
@@ -3,7 +3,7 @@
2
-
+
@@ -24,6 +24,8 @@
+
+
diff --git a/testSuite/test-halo-mass-functions.py b/testSuite/test-halo-mass-functions.py
index 4e7284fc60..6fdd981eef 100755
--- a/testSuite/test-halo-mass-functions.py
+++ b/testSuite/test-halo-mass-functions.py
@@ -99,7 +99,7 @@
# Run Galacticus to generate the mass function.
status = subprocess.run("cd ..; ./Galacticus.exe testSuite/outputs/HMFcalc/"+massFunctionType['label']+".xml",shell=True)
if status.returncode != 0:
- print("FAILED: Galacticus failed")
+ print("FAILED: Galacticus failed for '"+massFunctionType['label']+"'")
sys.exit()
# Read the mass function generated by Galacticus.
diff --git a/testSuite/test-methods.xml b/testSuite/test-methods.xml
index f68508e412..e4a754f057 100644
--- a/testSuite/test-methods.xml
+++ b/testSuite/test-methods.xml
@@ -15,6 +15,13 @@
1
10
+
+ no
+ 24
+ 40
+ 1
+ 10
+
2
@@ -106,12 +113,8 @@
-
-
+
-
-
-
@@ -575,7 +578,7 @@
2
-
+
@@ -1298,7 +1301,7 @@
-
+
@@ -1570,13 +1573,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -1585,13 +1588,15 @@
+
+
-
+
@@ -1639,6 +1644,8 @@
+
+
@@ -1646,7 +1653,7 @@
-
+
@@ -1682,13 +1689,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -1696,6 +1703,8 @@
+
+
@@ -1703,7 +1712,7 @@
-
+
@@ -1742,6 +1751,8 @@
+
+
@@ -1749,7 +1760,7 @@
-
+
@@ -1791,6 +1802,8 @@
+
+
@@ -1798,7 +1811,7 @@
-
+
@@ -1839,6 +1852,8 @@
+
+
@@ -1846,7 +1861,7 @@
-
+
@@ -1887,6 +1902,8 @@
+
+
@@ -1894,7 +1911,7 @@
-
+
@@ -1935,6 +1952,8 @@
+
+
@@ -1942,7 +1961,7 @@
-
+
@@ -1989,13 +2008,15 @@
+
+
-
+
@@ -2036,6 +2057,8 @@
+
+
@@ -2043,7 +2066,7 @@
-
+
@@ -2100,6 +2123,8 @@
+
+
@@ -2107,7 +2132,7 @@
-
+
@@ -2160,6 +2185,8 @@
+
+
@@ -2167,7 +2194,7 @@
-
+
@@ -2223,6 +2250,8 @@
+
+
@@ -2232,7 +2261,7 @@
-
+
@@ -2328,7 +2357,7 @@
-
+
diff --git a/testSuite/test-noninstantaneous-recycling.pl b/testSuite/test-noninstantaneous-recycling.pl
index bdfc5df9eb..fbe3a8703f 100755
--- a/testSuite/test-noninstantaneous-recycling.pl
+++ b/testSuite/test-noninstantaneous-recycling.pl
@@ -20,6 +20,38 @@
} else {
print "SUCCESS: model run\n";
}
+
+my $yields;
+open(my $logFile,"outputs/noninstantaneous_recycling.log");
+while ( my $line = <$logFile> ) {
+ if ( $line =~ m/(\S+\/yield(Metals|Fe)_[a-z0-9]+.hdf5)/ ) {
+ my $fileName = $1;
+ my $type = $2;
+ $yields->{'file'}->{$type} = $fileName;
+ my $file = new PDL::IO::HDF5($fileName);
+ $yields ->{$type} = $file->dataset("yield".$type)->get();
+ }
+}
+close($logFile);
+unless ( exists($yields->{'Metals'}) && exists($yields->{'Fe'}) ) {
+ foreach my $type ( "Metals", "Fe" ) {
+ if ( exists($yields->{'file'}->{$type}) ) {
+ print "found ".$type." yield in '".$yields->{'file'}->{$type}."'\n";
+ } else {
+ print "failed to find file for ".$type." yield\n";
+ print "log file is:\n";
+ system("cat outputs/noninstantaneous_recycling.log");
+ }
+ }
+ die("failed to read yield tables");
+}
+
+my $haveMetals = which($yields->{'Metals'} > 0.0);
+my $ratioMaximum = maximum(
+ +$yields->{'Fe' }->flat()->($haveMetals)
+ /$yields->{'Metals'}->flat()->($haveMetals)
+ );
+
# Read the model data and check for consistency.
my $model = new PDL::IO::HDF5("outputs/noninstantaneous_recycling.hdf5");
my $outputs = $model ->group('Outputs' );
@@ -38,6 +70,6 @@
my $nonZero = which($massMetals > 1.0);
my $ratio = +$massFe ->($nonZero)
/$massMetals->($nonZero);
-my $status = all($ratio < 0.16) ? "SUCCESS" : "FAILED";
+my $status = all($ratio < $ratioMaximum) ? "SUCCESS" : "FAILED";
print $status.": Fe/Z ratio\n";
exit 0;
diff --git a/testSuite/test-tidalTracks.pl b/testSuite/test-tidalTracks.pl
index d97140b7a5..22ac51853a 100755
--- a/testSuite/test-tidalTracks.pl
+++ b/testSuite/test-tidalTracks.pl
@@ -19,7 +19,7 @@
{
label => "nonMonotonic",
gamma => 1.00 ,
- fitMetric => 0.03
+ fitMetric => 0.0301
},
{
label => "monotonic",
@@ -34,7 +34,7 @@
{
label => "monotonic",
gamma => 0.000 ,
- fitMetric => 0.028
+ fitMetric => 0.0280
}
);
foreach my $testCase ( @testCases ) {
@@ -124,7 +124,6 @@
my $status = $fitMetric < $testCase->{'fitMetric'} ? "SUCCESS" : "FAILED";
print $status.": subhalo tidal tracks '".$testCase->{'label'}." gamma=".sprintf("%3.1f",$testCase->{'gamma'})."'\n";
-
}
exit;
diff --git a/testSuite/validate-baryonicSuppression.pl b/testSuite/validate-baryonicSuppression.pl
index 437f3543ef..77fa9fb09e 100755
--- a/testSuite/validate-baryonicSuppression.pl
+++ b/testSuite/validate-baryonicSuppression.pl
@@ -120,13 +120,13 @@
# Define χ² targets for each dataset.
my $chiSquaredTarget;
$chiSquaredTarget->{'withBaryons' }->[1] = 6.0;
-$chiSquaredTarget->{'withBaryons_noReionization'}->[1] = 4.0;
-$chiSquaredTarget->{'withBaryons' }->[2] = 3.0;
-$chiSquaredTarget->{'withBaryons_noReionization'}->[2] = 2.0;
+$chiSquaredTarget->{'withBaryons_noReionization'}->[1] = 6.0;
+$chiSquaredTarget->{'withBaryons' }->[2] = 4.0;
+$chiSquaredTarget->{'withBaryons_noReionization'}->[2] = 3.0;
$chiSquaredTarget->{'withBaryons' }->[3] = 5.0;
-$chiSquaredTarget->{'withBaryons_noReionization'}->[3] = 1.0;
+$chiSquaredTarget->{'withBaryons_noReionization'}->[3] = 4.0;
$chiSquaredTarget->{'withBaryons' }->[4] = 3.0;
-$chiSquaredTarget->{'withBaryons_noReionization'}->[4] = 2.0;
+$chiSquaredTarget->{'withBaryons_noReionization'}->[4] = 4.0;
# Make output directory.
system("mkdir -p outputs/");
@@ -223,6 +223,7 @@
# Compute ratios of mass functions with the dark matter only model mass function.
my $output;
my $chiSquared;
+my $failed = 0;
for(my $outputIndex=1;$outputIndex<=4;++$outputIndex) {
foreach my $suffix ( "withBaryons", "withBaryons_noReionization" ) {
$haloMassFunction->{$suffix}->[$outputIndex]->{'ratio' } = $haloMassFunction->{$suffix}->[$outputIndex]->{'massFunction'}/$haloMassFunction->{'withoutBaryons'}->[$outputIndex]->{'massFunction'};
@@ -253,6 +254,8 @@
@{$output->{'model'}->{$suffix }->[$outputIndex]->{'ratioError'}} = $haloMassFunction->{$suffix }->[$outputIndex]->{'ratioError'}->list();
# Report.
my $status = $chiSquared->{$suffix}->[$outputIndex] < $chiSquaredTarget->{$suffix}->[$outputIndex] ? "SUCCESS" : "FAILED";
+ $failed = 1
+ if ( $status eq "FAILED" );
my $inequality = $chiSquared->{$suffix}->[$outputIndex] < $chiSquaredTarget->{$suffix}->[$outputIndex] ? "<" : "≥" ;
print $status.": model '".$suffix.(" " x (length("withBaryons_noReionization")-length($suffix)))."' at z=".$redshifts[$outputIndex]." validation (χ² = ".sprintf("%5.3f",$chiSquared->{$suffix}->[$outputIndex])." ".$inequality." ".sprintf("%5.3f",$chiSquaredTarget->{$suffix}->[$outputIndex]).")\n";
}
@@ -295,5 +298,9 @@
print $reportFile "window.BARYONICSUPPRESSION_DATA = ";
print $reportFile $json;
close($reportFile);
+if ( $failed ) {
+ print "model failed - results were:\n\n";
+ system("cat outputs/results_baryonicSuppression.json");
+}
exit;