Skip to content

Treegrid#93

Merged
allegroLeiden merged 21 commits intomasterfrom
treegrid
Nov 1, 2016
Merged

Treegrid#93
allegroLeiden merged 21 commits intomasterfrom
treegrid

Conversation

@smaret
Copy link
Copy Markdown
Contributor

@smaret smaret commented Feb 26, 2016

This is a rebased version of @imckstewart 's PR #80.

Previously there was a singularity at r==0.
Code that was in grid.c and weights.c has been put in a new module, random_points.c.
This is in preparation for the introduction of a new grid-point-selection algorithm.
The module weights.c is no longer compiled, since it is now obsolete.

Additional changes connected with the above:
	- Added a new (optional) user parameter 'samplingAlgorithm'.
	- Added external variables modelRadiusSquared, densityNormalizer,
          numCollisionPartners.
	- Added macro DENSITY_POWER.
	- New struct locusType.
The new algorithm calls the function randomsViaTree() recursively in a 2^D-tree
divide-and-conquer approach. The aim of this is to allow more accurate
representation of models for which the density varies strongly across the model,
without the severe time penalties that straightforward use of the rejection method
would entail. (The previous algorithm uses rejection method, but it 'flattens' the
density function in order to avoid long run times, which has the effect that
high-density regions are undersampled.) The algorithm assesses the density gradients
over a sub-volume of the model space, and if those gradients are judged too large
for quick results from the rejection method, the sub-volume is divided into 8
smaller sub-volumes and the same function is called for each one of these. The
division is performed by cutting the volume approximately in halves (at the 0.5
point plus some random dither) along each of the (3) axes of the coordinate space.

Additional changes connected with the above:
	- New macros:
		N_TREE_RANDOMS
		MAX_RECURSION
		MAX_N_TRIALS_TREE
		TREE_DITHER
The new grid-selection algorithm estimates the density gradients within a
sub-volume. This estimate may be too low if the function has narrow peaks. In this
case, the functioning of the algorithm is improved if the user pre-supplies a list
of the maximal values of the density function.

Additional changes connected with the above:
	- New (optional) user parameters numDensityMaxima, densityMaxLoc,
densityMaxValue. Note that I have left the appropriate lines commented out for now
in the example model.c file.
	- New macro:
		MAX_N_HIGH
In general it seems desirable to have the grid point number density follow the total
density of the collision partners, as is now more accurately provided by the new
algorithm. If the density function is heavily peaked, whereas the chosen number of
grid points is kept relatively small, a situation could arise in which the
distribution of points in the low-density regions of the model was very sparse. To
prevent this occurring, the user can now set a parameter minPointNumDensity. The
units of this should if possible save the user calculation, and to be
scale-invariant: the chosen unit therefore is points per spherical model volume.

Note that this feature is only implemented when par->samplingAlgorithm==1.

Additional changes connected with the above:
	- A new extern variable minDensity is defined.
Previously there was a singularity at r==0.
Code that was in grid.c and weights.c has been put in a new module, random_points.c.
This is in preparation for the introduction of a new grid-point-selection algorithm.
The module weights.c is no longer compiled, since it is now obsolete.

Additional changes connected with the above:
	- Added a new (optional) user parameter 'samplingAlgorithm'.
	- Added external variables modelRadiusSquared, densityNormalizer,
          numCollisionPartners.
	- Added macro DENSITY_POWER.
	- New struct locusType.
The new algorithm calls the function randomsViaTree() recursively in a 2^D-tree
divide-and-conquer approach. The aim of this is to allow more accurate
representation of models for which the density varies strongly across the model,
without the severe time penalties that straightforward use of the rejection method
would entail. (The previous algorithm uses rejection method, but it 'flattens' the
density function in order to avoid long run times, which has the effect that
high-density regions are undersampled.) The algorithm assesses the density gradients
over a sub-volume of the model space, and if those gradients are judged too large
for quick results from the rejection method, the sub-volume is divided into 8
smaller sub-volumes and the same function is called for each one of these. The
division is performed by cutting the volume approximately in halves (at the 0.5
point plus some random dither) along each of the (3) axes of the coordinate space.

Additional changes connected with the above:
	- New macros:
		N_TREE_RANDOMS
		MAX_RECURSION
		MAX_N_TRIALS_TREE
		TREE_DITHER
The new grid-selection algorithm estimates the density gradients within a
sub-volume. This estimate may be too low if the function has narrow peaks. In this
case, the functioning of the algorithm is improved if the user pre-supplies a list
of the maximal values of the density function.

Additional changes connected with the above:
	- New (optional) user parameters numDensityMaxima, densityMaxLoc,
densityMaxValue. Note that I have left the appropriate lines commented out for now
in the example model.c file.
	- New macro:
		MAX_N_HIGH
In general it seems desirable to have the grid point number density follow the total
density of the collision partners, as is now more accurately provided by the new
algorithm. If the density function is heavily peaked, whereas the chosen number of
grid points is kept relatively small, a situation could arise in which the
distribution of points in the low-density regions of the model was very sparse. To
prevent this occurring, the user can now set a parameter minPointNumDensity. The
units of this should if possible save the user calculation, and to be
scale-invariant: the chosen unit therefore is points per spherical model volume.

Note that this feature is only implemented when par->samplingAlgorithm==1.

Additional changes connected with the above:
	- A new extern variable minDensity is defined.
@smaret smaret mentioned this pull request Feb 26, 2016
The 'init_lte' parameter was gone.
@smaret smaret added this to the Release 1.6 milestone Jul 6, 2016
@smaret smaret self-assigned this Jul 6, 2016
@smaret smaret modified the milestones: Release 1.7, Release 1.6 Jul 6, 2016
- Module random_points.c has been deleted and replaced by tree_random.c with header file tree_random.h. The algorithm has been changed in three main ways:
  * Construction of the sub-cell tree is now done separately to filling the sub-cells with points.
  * The number of points assigned to each sub-cell is calculated via the multinomial distribution, instead of (incorrectly) via a kind of progressive binomial distribution.
  * The set of points which are used as input to all the sub-cell rejection sessions is now chosen via a quasi-random routine which spaces them much more evenly apart. This renders post-grid smoothing unnecessary and thus allows much sharper changes in grid point density distribution.

- There is a new macro TREE_POWER by which the density function within gridDensity should be raised in order to approximately match the grid density achieved by the previous flattened-density function with par->sample==2 (logarithmic radius sampling). Why is this a good thing? Because the old routine only gave a good match for the example model, which has a single central condensation. A model with concentrations away from the central core would *not* be handled well by this corner-cutting, but *would* be handled well by the new routine.
@allegroLeiden
Copy link
Copy Markdown
Contributor

I've merged the current master branch into this PR and, if there are no objections in the next day or so, I will merge it into master (i.e. accept the PR). A summary of the changes in this PR is as follows.

The PR addresses two separate challenges:

  • A: choosing random points to follow some freely chooseable distribution function, and doing so efficiently (= quickly).
  • B: making sure that no two points are arbitrarily close together.

A: The canonical algorithm for choosing randoms to follow some generic distribution is called the rejection method (RM). It is robust but can be very slow if the desired distribution function has large variations. LIME<=1.6 basically uses RM but applies a number of tweaks and biases designed to speed it up. These work well for the example model, but in fact only because they are tuned to suit the example model - for a different model they will be about as quick but will not necessarily produce anything close to an accurate sampling of the model.

The present PR applies pure RM but essentially on a local level, which allows it to be fast. It does this by dividing the model space into 8 sub-cubes, then dividing each of these again, and so forth, until the variation of the distribution function within a sub-cube falls to an acceptable level.

B: LIME<=1.6 achieves this by smoothing the points after the initial choice. There are two problems with this: it is an iterative process, and fairly slow; and it blurs out sharp boundaries in the point density. The present PR chooses points, not randomly, but sub-randomly, using a Halton-sequence algorithm, producing points which 'look' random but which avoid approaching too close to each other. Some careful attention was necessary to avoid artefacts at the sub-cube boundaries but test distributions of points from the total algorithm (8-tree recursive RM plus Halton-sequence choice of points in each sub-cube) ended up looking very convincing.

I added a macro TREE_POWER to lime.h whose value I have somewhat arbitrarily set at 2.1. The reason for this is as follows. LIME<=1.6 (at least with par->sample==0 or ==2) biases the point density by generating candidate radial distances r which have an exp(-|k|r) distribution. This, together with a 'flattened' RM test function which is the normalized bulk gas density raised to the power of 0.2, produces the broadly acceptable example-model point distributions of LIME<=1.6. When I started testing the present algorithm, I assumed that I could reproduce this by sampling the grid so as to follow the normalized bulk gas density, with neither exponential pre-bias nor flattening. I found this was not the case: the produced distribution is too sparse in the central portion of the example model and too dense in the outfield. The resulting central cells are clearly too big: rays traced through the centre exhibit far too much noise. Clearly the optical depth of the model is not a linear function of the density. I found I was able to reproduce the broad 'look-and-feel' of the <=1.6 grid distribution by raising the gas density function to the power in TREE_POWER. I rather regret now wiring this in as a macro, because it rather obscures the point that the user has now complete freedom to specify the grid density function in whatever way they please. Raising the gas density to a scalar power is a crude way to accomplish the desired end: there may be many more systematic ways to achieve this.

@allegroLeiden
Copy link
Copy Markdown
Contributor

Note that the Halton-sequence thing removes the need to smooth after grid point choice.

@allegroLeiden allegroLeiden merged commit 5230c0b into master Nov 1, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants