Skip to content

Smoother raytracing output.#96

Closed
allegroLeiden wants to merge 29 commits intolime-rt:masterfrom
allegroLeiden:raytrace_smooth
Closed

Smoother raytracing output.#96
allegroLeiden wants to merge 29 commits intolime-rt:masterfrom
allegroLeiden:raytrace_smooth

Conversation

@allegroLeiden
Copy link
Copy Markdown
Contributor

The current raytracing algorithm leads to step-function artefacts in the channel maps it produces. These occur because the algorithm, although continuously sampling velocity at relatively finely-spaced intervals along the path of a ray, uses a single set of values for level populations throughout an entire Voronoi cell. In outer regions of an image where the Voronoi cells are likely to be significantly bigger than the image pixels, this leads to the step artefacts which can be seen in the image which is generated by the default example model file. The algorithm subject of the present PR removes these step artefacts by interpolating (to 1st order) the level populations within each Delaunay tetrahedron. This new algorithm has necessitated a lot of extra or altered code, from a new qhull() function (renamed to 'delaunay') which can return the necessary information about the tetrahedra, through routines to work out the sequence of cells passed through by any ray, to the new traceray_smooth() function to actually implement the algorithm.

To aid in checking these many changes I have divided the push into many smaller commits. I checked the intermediate code at several points with valgrind. The final code passes valgrind when the new algorithm is invoked, is backward compatible (because it defaults to the old), and with the old algorithm selected, still produces an identical cube to before.

The following points are worth noting:

  1. The awkward memory structure, particularly for grid points, has made some duplication of code hard to avoid; it also makes it difficult to free memory of grid attributes no longer required. I look forward to the time when we can implement a more flexible scheme.
  2. Initially the velocity was also interpolated, but this proved to be undesirable, because the variation in spectral line amplitude across a typical cell is decidedly non-linear, and a small error in velocity can lead to a large error in intensity. Therefore the present implementation of the routine samples the user-supplied velocity function at several places between entering and leaving a Delaunay tetrahedron.
  3. The interpolation is essentially a Finite Element scheme with 1st-order basis functions; an extension to 2nd order would be relatively easy to implement, requiring only level populations (plus dust/knu) to be stored also for points half-way between each pair of grid neighbours. A 2nd-order interpolation of velocity might also prove to be an adequate approximation for this quantity and thus allow the avoidance of calling a user-supplied function at the raytracing stage.
  4. The new algorithm is slower than the old, but there are many things which could be pre-calculated, if the ensuing cost in memory was thought to be worth it.

This is the first of many commits I plan for an eventual pull request which will provide
LIME with a smoother raytracing capability. This added functionality requires quite a
lot of new code and will also involve some cleaning up of existing code.

The smoother raytracing algorithm works by linear interpolation of level populations
inside the Delaunay cells of the model grid. This first commit lays the groundwork for
reading and using the cell information by defining several new structs to allow
convenient storage and manipulation of this data.

Structs added were: cell, pop2, gridInterp, gAuxType, intersectType, faceType and
triangle2D. None of these are yet active so as yet they cause no change in the task
function.

Also added in this commit is a new user parameter traceRayAlgorithm. This will be used
to invoke the new raytracing algorithm; at present it has no effect.

Finally, I have commented out the function template for FastExp() in lime.h and added
the variable type of the input argument of greetings_parallel() in messages.c just to
avoid the warning messages that gcc issues now on compilation. This is just for present
convenience, I plan to make these minor fixes the subject of a separate PR.
This attribute belongs in struct populations rather than struct grid (directly). Moving
it involved making changes to 18 lines of code over 7 modules. I checked afterward that
the code runs still ok with the default model.

I also slightly rearranged attributes x and vel in the struct template for struct grid,
and added a new attribute (as yet unused) B. These changes have no further effects on
the code.
This is the first of several commits which make changes to the function qhull(). The
only one of these which introduces new functionality will be an option to read and
return the information about the Delaunay cells. However I plan to use this necessity to
reform the whole function, starting with the name. 'qhull' is a bad name because it is
not immediately explanatory and because it can be confused with the library of that
name.
I replaced the input argument par with two, namely numDims and numPoints. This helps to
make the function more generic.

An additional variable ppi of type matching numPoints was added to loop over the latter
quantity. Iteration variables of types other than the upper limit variable can cause
strange errors!
The block starting at line 168 in grid.c is rearranged, the if test being inverted such
that the code exits if qh_new_qhull() returns a non-zero status. This involves
unindenting all the code (~32 lines) which was in the block before. No other functional
changes have been made to these lines in the present commit.
The array 'simplex' is renamed to 'pointIdsThisFacet' for added clarity. Additional
variables idI and idJ have been added. All three of these have been made type unsigned
long, and variable 'id' is also now of the same type.
Three more arguments, to mediate the return of the cell information. These are not yet
used, but the changed interface required some additions to every function which calls
delaunay().
This is not yet called anywhere however.
This is a purely cosmetic change, but a useful one, because searching for g returns too
many false positives. I plan to do this elsewhere too.
I also rearranged the variable declarations a little. The only substantive change is the
explicit cast of the return value of qh_pointid() to unsigned long.

This is the last of the delaunay-focussed commits.
To assist the interpolative raytracing algorithm I plan to introduce a function
calcLineAmpInterp() to return the spectral line amplitude relative to line centre, a
quantity which in LIME is usually signified by the variable named 'vfac'. This function
is therefore analogous to velocityspline2() in raytrace.c and the similarly-named
functions in photon.c. This planned new addition is a good excuse to change the names
and interfaces of all three existing 'velocityspline' functions to be more similar. In
the present commit I have changed the names as follows:

  velocityspline     to calcLineAmpSpline
  velocityspline_lin to calcLineAmpLinear
  velocityspline2    to calcLineAmpSample

I also made some of the arguments 'const'. This has no effect on the functioning, it is
just more conservative practice.

The final change is the addition of a calcLineAmpLinear() template to lime.h
This contains the code for finding the cells traversed by a ray on the one hand and
interpolating within Delaunay cells on the other. None of this code is yet used, but it
compiles at this stage.
This function is called by sourceFunc_pol() when the user parameter par->polarization is
set. In the old version of stokesangles(), the user-supplied function magfield() was
called for an arbitrary position in the model. In the planned interpolation raytracing
scheme however it is possible to interpolate the B field between the vertices of any
Delaunay tetrahedron. It is also desirable to remove the calls to user-supplied
functions from any sections of code traversed in the case that either par.doPregrid or
par.restart are set (although this is not yet achieved within the alterative raytrace
code, as will be seen when this is added). For these reasons I re-wrote stokesangles()
to accept a precalculated value of the B field as an input argument. These values are
now also calculated via calls to magfield() at the grid cell loci within grid.c.

At some point the storage of grid data needs to be improved, i.e. to include both
velocity and B-field values. Until that time the par.doPregrid option will not work, not
even after the present commit, even though it moves things in the right direction.
The changes are as follows:
  . Changes to the arguments, to make it easier to call this routine from the planned
interpolated raytrace; the change from 'struct grid *g' to 'struct pop2 gm' also allows
much more compact and clear expressions.
  . Variable name 'angles' changed to more descriptive 'trigFuncs'.
  . All elements of snu[3] are now explicitly defaulted to zero.

The interface changes have necessitated changes in the call to sourceFunc_pol() within
traceray() and the addition of a new argument gAux (whose values are set within
raytrace()) to the traceray() call.

I removed raytrace_1.4() at this point. This obsolete chunk of code was supposed to have
already been removed in a previous PR.
I just changed sourceFunc_line() and sourceFunc_cont() to bring them more into line with
sourceFunc_pol(). This necessitated changes in photon(), getjbar() and traceray(). In
the last of these, the call to sourceFunc_cont() is slightly rearranged, with new
variables contMolI and contLineI being set before the start of the loop.
These duplicate the functionality of sourceFunc_cont() and sourceFunc_line() but are
made necessary, or at least desirable, by the present poor way in which grid data is
stored in memory structures. When this is fixed (which it will hopefully be eventually),
the *_raytrace versions can be merged into the previous ones.
There is a test near the start of the function to see whether the ray will intersect the
model sphere at all; if so, the following large block of ~110 statements (containing the
entire functionality of the function) is entered. It is obviously much less spendthrift
of indents to simply exit the function if the ray does _not_ pass through the model
sphere. The code has been rearranged to implement this change. All the statements which
were formerly within the block have been unindented by 2 spaces; there are no further
changes in the present commit.
The substantive changes are:
  . Where par->polarization and FASTEXP was defined, one of the two necessary
exponentials was still calculated by the native exp() function. This has been changed
now such that Fastexp() is called for both.
  . sourceFunc_cont_raytrace() is no longer (unnecessarily) called once per channel, but
only once; the results being stored and added to jnu and alpha for each new channel.
I also changed the interface of traceray(). This was inadvertent and should have been in
a separate commit.
In the previous raytracing algorithm the values of the sink point atributes are not
accessed. However they are accessed in the interpolation routine. Because of this it is
necessary to be a bit more careful about setting them. Thus I have set their:
  . nmol to zero in LTE();
  . pops to L.T.E with the CMB in molinit();
  . vel to the user-specified values (instead of zero) in getVelosplines().
With this commit, the new algorithm is essentially complete. I'll make 1 more with some
minor cosmetic changes.
Here I have:
  . Improved some variable names in raytrace().
  . Ordered all the function templates alphabetically (or at least, more so) in lime.h.
  . Removed some tab indents in predefgrid.c which were bugging me.
@tlunttil
Copy link
Copy Markdown
Contributor

Do you know how much slower approximately this new raytracing is compared to version 1.5?

@christianbrinch
Copy link
Copy Markdown
Contributor

No because the one time I tried 1.5, I gave up on the raytracing and killed it before it finished. The same model took 20 seconds to run in 1.4.

Sent from my iPhone

On 26. maj 2016, at 20.22, tlunttil <notifications@github.commailto:notifications@github.com> wrote:

Do you know how much slower approximately this new raytracing is compared to version 1.5?


You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHubhttps://github.com//pull/96#issuecomment-221953005

@tlunttil
Copy link
Copy Markdown
Contributor

No because the one time I tried 1.5, I gave up on the raytracing and killed it before it finished. The same model took 20 seconds to run in 1.4.

Do you mean release version 1.5 or one with the changes in this PR included? I haven't tried this PR yet, but raytracing in release 1.5 shouldn't be that terribly slow (though definitely slower than in 1.4 if the image size is large).

@christianbrinch
Copy link
Copy Markdown
Contributor

The release version I suppose.

Sent from my iPhone

On 26. maj 2016, at 22.53, tlunttil <notifications@github.commailto:notifications@github.com> wrote:

No because the one time I tried 1.5, I gave up on the raytracing and killed it before it finished. The same model took 20 seconds to run in 1.4.

Do you mean release version 1.5 or one with the changes in this PR included? I haven't tried this PR yet, but raytracing in release 1.5 shouldn't be that terribly slow (though definitely slower than in 1.4 if the image size is large).


You are receiving this because you commented.
Reply to this email directly or view it on GitHubhttps://github.com//pull/96#issuecomment-221991274

@tlunttil
Copy link
Copy Markdown
Contributor

Hmm, that sounds strange. I wonder if it's a bug rather than a 'feature' in version 1.5 raytracing. I don't don't think a model that only takes 20 seconds in 1.4 should take very long even in 1.5. Can you send the model files so I could have a look?

@allegroLeiden
Copy link
Copy Markdown
Contributor Author

There seems to be a bit of a non-sequitur between @tlunttil 's first comment (which refers to the PR) and @christianbrinch 's reply to that (which refers to 1.5). The probable reason why 1.5 is slower has been remarked on already by @ParfenovS in thread #95.

On my desktop, for the master branch, which is still essentially 1.5, the time to run the standard model is about 13 min. Raytracing takes 25 sec of this. For branch 'raytrace_smooth' (the source of the present PR) with par->traceRayAlgorithm=1 raytracing takes 120 sec. (Everything single-threaded.)

@tlunttil
Copy link
Copy Markdown
Contributor

Yeah, what I originally wanted to know is what you just told. Ok, so raytracing in this PR is a factor of 5-ish slower than release in 1.5. That doesn't sound too bad and it's still only a small fraction of the total run time.

I have some further comments and questions about 1.4 vs 1.5 raytracing, but it's better to continue that discussion in #95.

@allegroLeiden allegroLeiden mentioned this pull request May 30, 2016
@smaret smaret added this to the Release 1.6 milestone Jun 13, 2016
I had left a comment in the part of traceray() which dealt with polarized continuum, but
this comment only indicated that I did not understand the code. I do now, so I have removed
the comment.
@allegroLeiden allegroLeiden mentioned this pull request Jun 29, 2016
@smaret
Copy link
Copy Markdown
Contributor

smaret commented Jun 30, 2016

Superseded by #145.

@smaret smaret closed this Jun 30, 2016
@allegroLeiden allegroLeiden deleted the raytrace_smooth branch September 7, 2016 12:58
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.

5 participants