Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Introduce indexed ray transfer APIs and tests - #503

Open
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer
Open

Introduce indexed ray transfer APIs and tests#503
munechika-koyo wants to merge 9 commits into
cherab:developmentfrom
munechika-koyo:feature/add-new-raytransfer

Conversation

@munechika-koyo

@munechika-koyomunechika-koyo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a general index-function-based ray transfer API by adding indexed emitter and integrator classes, replacing mesh-specific naming with functionality-based naming.

Key Changes

Unit Test

TestPurposeSetupVerificationWhy it matters
test_evaluate_functionValidate that IndexedRayTransferEmitter works correctly with NumericalIntegrator and maps contributions to the correct bins via index_function.A 3x3x3 Box domain is used with bins=27. A diagonal ray crosses the volume. The index function maps in-domain points to 0..26 and returns -1 outside.Only bins 0, 13, and 26 are non-zero, each with path-length contribution sqrt(3). The output spectrum matches the expected vector with atol=0.001.Confirms correct geometric integration and bin assignment for the index-function-based workflow.
test_default_integratorConfirm default integrator behavior when no integrator is explicitly passed.IndexedRayTransferEmitter is created without an integrator argument under the same ray/volume setup as above.The emitter uses IndexedRayTransferIntegrator by default, and the resulting spectrum matches the same expected vector (atol=0.001).Guarantees safe default behavior and avoids mandatory integrator wiring for users.
test_discrete3dmesh_as_index_functionValidate that Discrete3DMesh can be used as an equivalent index function source.A Discrete3DMesh is built from a 4x4x4 vertex grid over 3x3x3 cells; each cube is split into 6 tetrahedra. Cell values follow the same indexing rule as the reference index function.Representative points across all 27 cells match the reference mapping; outside-domain points return -1; ray-transfer spectra from mesh-based and function-based indexing are equal within atol=0.001.Demonstrates implementation-agnostic design and compatibility with tetrahedral mesh indexing in practical ray-transfer use.

Executed:

python -m unittest cherab.tools.tests.test_raytransfer.TestIndexedRayTransferEmitter -v

Result:

  • test_default_integrator: ok
  • test_discrete3dmesh_as_index_function: ok
  • test_evaluate_function: ok
  • Ran 3 tests, all passed.

Example Usage

fromraysect.opticalimportWorldfromcherab.tools.raytransferimportIndexedRayTransferEmitterdefindex_func(x, y, z):
ifx<0:
return0return1world=World()
material=IndexedRayTransferEmitter(index_func, bins=2)

Compatibility and Risk

  • Scope is limited to ray transfer emitter/integrator API naming and related tests.
  • Runtime behavior is validated by focused unit tests for indexed evaluation and Discrete3DMesh integration.

Reviewer Notes

Checklist

  • API implementation updated
  • Cython declaration file updated
  • Unit tests added/updated
  • Targeted tests executed in pixi test environment

Benchmark

The appendix in this paper (https://doi.org/10.1063/5.0225703) compared the raytransfer of Discrete3D meshes with that of regular grids, showing that the geometry matrix calculation for rectangular grids' raytransfer was much faster than the Discrete3D one.

@jacklovelljacklovell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool. If I understand this correctly, it's a generalisation of the existing ray transfer objects where a callable function replaces the voxel map, and this function returns the voxel index at a given point in space. As well as the 3D application in https://doi.org/10.1063/5.0225703 (which you should cite somewhere in the documentation by the way), I can see the possibility of application to axisymmetric voxels of arbitrary poloidal cross section without having to approximate them with a rectangular grid.

I think a demo would be highly beneficial, as it's hard to see a concrete use case from the docstrings alone. Perhaps you could adapt Vlad's Space Invaders demos to showcase the new tools.

Comment threadcherab/tools/raytransfer/emitters.pyx
Comment threadcherab/tools/raytransfer/emitters.pyx
@munechika-koyo

Copy link
Copy Markdown
MemberAuthor

Thank you for your review!
I've been thinking about what makes an effective demo script.
Currently, my ideas are using index functions like:

  • analytical geometry with sin, cos, circle, etc.
  • using mesh geometry (triangulation for 2D, tetrahedralization for 3D, e.g., Stanford Bunny data used in raysect).

I will try to work out these ideas with AI assistance for now.
Any idea of yours is really helpful.

@jacklovell

Copy link
Copy Markdown
Member

Analytically-defined voxels will make a nice demo, agreed. For example, flux-aligned axisymmetric voxels for the generomak equilibrium defined in ($\Delta \psi$, $\Delta \beta$) space for normalised flux $\Psi$ and poloidal angle $\beta$. The ToroidalVoxelGrid could handle this but I expect the ray transfer framework to be more performant.

And yes, voxels defined by triangular or tetrahedral meshes would also be a good illustration, showing the ability to extend beyond regular rectilinear grids.

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @munechika-koyo , this is a very nice generalisation of the RT framework! I have one important question to raise.

Changing the indexing function from Function3D to Function6D would allow to pass also direction information which would make the new IRT framwrok applicable to also anisotropic radiation.

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making index_function a Function3D makes the IndexedRay framework specific to isotropic radiation. If you made it Function6D which now we have in Cherab, the framework would be generalised to anisotropic applications because you could also pass direction vector components. That is something I'm be very interested in. From the point of view of the code it shouldn't be a large change. What do you think @munechika-koyo?

@munechika-koyomunechika-koyoJul 22, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really interesting!
It might also be useful for distributional tomography.
I think we need to add a new API or refactor the existing one to accept Function6D as an index function for the IndexedRayTransferEmitter.emission_function() and IndexedRayTransferIntegrator.integrate() methods.
These changes don't seem straightforward to me, so should we handle them in a separate PR after finalizing this one?

In addition, any good ideas for your demo using Function6D would be really helpful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@MateasekMateasek mentioned this pull request Jul 22, 2026
12 tasks

@MateasekMateasek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @munechika-koyo for your reaction. It made me look at the Raytransfer framework previously added by @vsnever and the IndexedRaytransfer framework you did in this PR from a broader perspecitve.

Let me now express how I see the framework and what it should do. I think that RayTransfer in general is there to discretise continuous space into one dimensional integer indexes. Then it uses the integration to add a sensitivity to the integer bins.

What does the current RayTransfer framework does? Its specified by a single aspect:

  • The mapping between a cartesian space and a 1D array of integers. There are now two distinct mappings for which two sets of classes were made. That is cylindrical and cartesian mappings.

Now what your contribution does is, and correct me if I'm wrong, that it allows user to define the mapping function. This from my point of view generalises the previous approach. Now, we can have a single set of integrator and emitter, which can be used for the old raytransfer functionality if you provide the right discretising mapping functions.

Now I'm going to propose what I think should be done, but please tell me if I'm missing something and I would also like @jacklovell and @skuba31 to think about this, since it would be a major change:

  1. The IndexedRaytransfer approach I described above will replace the older Raytransfer because essentially it can do exactly the same thing and we should not be doubling our functionality.
  2. The most general indexing function (and replacements for the old framework) together with inverse indexing funcitons will be added to tools to give users the possibility to do some basic and most applications out of the box.
  3. Extend the indexing function to accept Function6D to make this framework even more general.

I'm sorry to be proposing such a major overhaul of this contribution but there wasn't any previous discussion in an issue, so there was no place to do it. I actually had the approach I described above in my head for a long time and I was very happy to see that you actually did it @munechika-koyo, but I think it could be done in a more general and systematic way which would be actually very powerful. If you think about it, it will allow users to apply discretisation in most of the "information dimensions" Raysect passes to the integrator with the ray which position and direction. This would allow to do anisotropic contribution matrices for example. Or you could decide that you implement a discretisation function which uses x, y, z and wavelength (and you just don't use 2 parameters in the 6D function evaluation).What do you think?

integrator = integrator or IndexedRayTransferIntegrator(step=integration_step)
super().__init__(integrator=integrator)

self.index_function = autowrap_function3d(index_function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what else do you need to change except the Function3D calls, i.e. in IndexedRayTransferIntegrator.integrate you change <int>index_function(x, y, z) to <int>index_function(x, y, z, dx, dy, dz) and then you need to change the object type and etc.. Maybe I'm missing something.

@skuba31

Copy link
Copy Markdown
Contributor

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is:
raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer.
But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like:
raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer.
The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact.
Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

@Mateasek

Copy link
Copy Markdown
Member

This is exactly what I

First of all, great work @munechika-koyo . I did have a look at the proposed changes with the comments by @Mateasek in mind. At this point I think we should consider again whether we want to include these changes into 1.6 at the expense of postponing the release or whether doing the release as soon as possible is the priority. This discussion could potentially take some manpower that could be dedicated to the release.

If not included into 1.6, it could go to development branch and another release be created whenever Matěj finishes his synchrotron model. These two additions would make a logical connection and I think it would justify creation of a release even a short time after 1.6.

I like the idea of having a possibility to define any type of index function including those 6D. A possibility to compute contribution matrices for anisotropic radiation would be quite unique. I agree with Matěj that the functionality of the new classes is more general than those currently implemented.

I am not sure if I understood the proposal by Matěj correctly, but I think the changes could be managed without changing the public API. If I am reading it correctly, the class structure of the changes proposed by Koyo is: raysect emitter/material < base RayTransfer < specific RayTransfer and IndexedRayTransfer. But as was pointed out by Matěj, the indexedariants are actually more general than currently implemented ray transfer classes.

I would propose to use the new indexed classes as a new base for raytransfer and specific raytransfer classes would simply have a index function assigned on creation. So the structure would look like: raysect emitter/material < IndexedRayTransfer (renamed to simply RayTransfer) < specific RayTransfer. The individual steps could be

  1. Change index_function type from function3d to function6d.
  2. Replace old base classes by renaming IndexedRayTransferEmitter -> RayTransferEmitter and IndexedRayTransferIntegrator -> RayTransferIntegrator.
  3. Create cartesian_index_function and cylindrical_index_function both 6d but dropping the direction part of input and use them to define CartesianRayTransferEmitter and CylindricalRayTransferEmitter both utilizing the new general (Indexed)RayTransferIntegrator.
  4. Keep CartesianRayTransferIntegrator and CylindricalRayTransferIntegrator for backward compatibility and possibly add a warning that they are no longer used by the Emitter classes.

If my understanding of the problem is correct, this should include the new functionality while keeping the API basically intact. Do you think these structural change would be appropriate and acceptable @munechika-koyo@jacklovell@Mateasek ?

Yes, this is exactly what I had in mind. Whether we include it into 1.6 or not would depend on how long it would take. I personally don't think the changes will be too demanding to implement. Should we create an issue and move this conversation there?

@jacklovell

Copy link
Copy Markdown
Member

Discussion about the design should be done in #310

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RayTransferEmitter that uses a function to map a point in space to a light source

4 participants

@munechika-koyo@jacklovell@skuba31@Mateasek