Skip to content

Feature/1717 nodal api#2854

Open
ShahuK283 wants to merge 10 commits intotrixi-framework:mainfrom
ShahuK283:feature/1717-nodal-api
Open

Feature/1717 nodal api#2854
ShahuK283 wants to merge 10 commits intotrixi-framework:mainfrom
ShahuK283:feature/1717-nodal-api

Conversation

@ShahuK283
Copy link
Copy Markdown
Contributor

Hi @jlchan, @ranocha, and @sloede,

Thanks for the great discussion on #1717! I've opened this Draft PR with a prototype that returns StructArrays of SVectors. It uses ntuple and views over Trixi's internal wrap_array to collapse the n_vars dimension with zero allocations, yielding purely spatial arrays.

@jlchan - Does this StructArray format align well with your usual workflows?

For now, I have just added the foundational extraction API (get_u and get_coordinates). If this approach looks right to everyone, Michael's wishlist for spatial interpolation can be done next in a separate PR!

Let me know what you think

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

Review checklist

This checklist is meant to assist creators of PRs (to let them know what reviewers will typically look for) and reviewers (to guide them in a structured review process). Items do not need to be checked explicitly for a PR to be eligible for merging.

Purpose and scope

  • The PR has a single goal that is clear from the PR title and/or description.
  • All code changes represent a single set of modifications that logically belong together.
  • No more than 500 lines of code are changed or there is no obvious way to split the PR into multiple PRs.

Code quality

  • The code can be understood easily.
  • Newly introduced names for variables etc. are self-descriptive and consistent with existing naming conventions.
  • There are no redundancies that can be removed by simple modularization/refactoring.
  • There are no leftover debug statements or commented code sections.
  • The code adheres to our conventions and style guide, and to the Julia guidelines.

Documentation

  • New functions and types are documented with a docstring or top-level comment.
  • Relevant publications are referenced in docstrings (see example for formatting).
  • Inline comments are used to document longer or unusual code sections.
  • Comments describe intent ("why?") and not just functionality ("what?").
  • If the PR introduces a significant change or new feature, it is documented in NEWS.md with its PR number.

Testing

  • The PR passes all tests.
  • New or modified lines of code are covered by tests.
  • New or modified tests run in less then 10 seconds.

Performance

  • There are no type instabilities or memory allocations in performance-critical parts.
  • If the PR intent is to improve performance, before/after time measurements are posted in the PR.

Verification

  • The correctness of the code was verified using appropriate tests.
  • If new equations/methods are added, a convergence test has been run and the results
    are posted in the PR.

Created with ❤️ by the Trixi.jl community.

Copy link
Copy Markdown
Contributor

@jlchan jlchan left a comment

Choose a reason for hiding this comment

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

Hi @ShahuK283! Thanks for the PR; I just replied to the discussion in #1717, and lean towards plain Vector{<:StructArray} return types. I'm happy to do StructArrays if @ranocha or @sloede prefer though.

@ShahuK283 ShahuK283 force-pushed the feature/1717-nodal-api branch from d7ee1bd to badf9c4 Compare March 10, 2026 02:05
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.11%. Comparing base (f380eb1) to head (88f3dc0).
⚠️ Report is 13 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2854      +/-   ##
==========================================
+ Coverage   97.04%   97.11%   +0.06%     
==========================================
  Files         605      608       +3     
  Lines       47303    47308       +5     
==========================================
+ Hits        45905    45940      +35     
+ Misses       1398     1368      -30     
Flag Coverage Δ
unittests 97.11% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ShahuK283
Copy link
Copy Markdown
Contributor Author

Hi! All CI checks have passed except for format-suggestion and Codecov. I believe this is because the new get_u and get_coordinates functions aren't being called anywhere in the test suite yet.

I have a quick test ready to verify them:

Test nodal API

sol = solve(ode, dt=1.0, maxiters=1)
@test size(Trixi.get_u(sol)) == (1, 4, 4, 256)
@test size(Trixi.get_coordinates(sol)) == (2, 4, 4, 256)

Before I add this, I wanted to ask what the preferred approach is:
Should I append these lines to an existing file (like test/test_tree_2d_advection.jl) to reuse an existing sol object, or would you prefer I create a new dedicated test file for the API?

@jlchan
Copy link
Copy Markdown
Contributor

jlchan commented Mar 13, 2026 via email

@ShahuK283
Copy link
Copy Markdown
Contributor Author

Hi @jlchan, I have updated the dimensions to correctly match with the variables in SVector. I also added the Nodal API tests in test_tree_2d_advection.jl using the existing elixir as you suggested, and checked for formatting errors.
Can you review this?

Copy link
Copy Markdown
Contributor

@jlchan jlchan left a comment

Choose a reason for hiding this comment

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

Looks good, just had a few questions. Also, I'm partial to just using plain Vector{<:SVector} instead of StructArrays if that's simpler.

end

function get_coordinates(semi::AbstractSemidiscretization)
x_raw = semi.cache.elements.node_coordinates
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this works for all Semidiscretization types; it might be good to dispatch using mesh_equations_solver_cache and then specialize on the solver or mesh type. For example, it doesn't work with DGMulti

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added multiple dispatch in get_coordinates via mesh_equations_solver_cache(semi) to route coordinate extraction.
It returns cache.elements.node_coordinates for standard meshes and mesh.md.xyz for DGMultiMesh.

Comment on lines +315 to +316
@test size(Trixi.get_u(sol)) == (4, 4, 256)
@test size(Trixi.get_coordinates(sol)) == (4, 4, 256)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Might be good to also add a test that the retrieved values are correct.

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.

We should also add more tests for other mesh types.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added the Nodal API test suites to cover TreeMesh, StructuredMesh, UnstructuredMesh, and DGMulti. The tests now verify exact value correctness (e.g., @test coords == semi.cache.elements.node_coordinates) rather than just checking dimensions.

function get_u(u_ode, semi::AbstractSemidiscretization)
u_raw = wrap_array(u_ode, semi)
n_vars = nvariables(semi)
@assert ndims(u_raw)>=3 "Unexpected wrap_array shape: $(size(u_raw))"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is this guarding against?

Copy link
Copy Markdown
Member

@ranocha ranocha left a comment

Choose a reason for hiding this comment

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

Thanks for working on this!

Comment on lines +6 to +13
function get_u(sol)
semi = sol.prob.p
u_ode = sol.u[end]
return get_u(u_ode, semi)
end

function get_u(u_ode, semi::AbstractSemidiscretization)
u_raw = wrap_array(u_ode, semi)
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.

These are the two methods that should be documented (get a docstring). We should also mention them briefly in the documentation, e.g., introduce a new subsection "Custom visualization" in https://trixi-framework.github.io/TrixiDocumentation/stable/visualization/ and mention these methods there together with an example.

src/Trixi.jl Outdated
using SparseArrays: AbstractSparseMatrix, AbstractSparseMatrixCSC, sparse, droptol!,
rowvals, nzrange, nonzeros

using StructArrays
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.

We already load the package below: @reexport using StructArrays: StructArrays, StructArray.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I missed that. I had fixed it in this commit

Comment on lines +315 to +316
@test size(Trixi.get_u(sol)) == (4, 4, 256)
@test size(Trixi.get_coordinates(sol)) == (4, 4, 256)
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.

We should also add more tests for other mesh types.

@ShahuK283
Copy link
Copy Markdown
Contributor Author

Hi! @ranocha , @jlchan
Let me know if this updated approach looks good!
The Downgrade and Trixi2Vtk checks are failing with HTTP/2 429 (Too Many Requests) errors; other CI have passed

@ShahuK283 ShahuK283 marked this pull request as ready for review March 26, 2026 13:26
Copy link
Copy Markdown
Contributor

@jlchan jlchan left a comment

Choose a reason for hiding this comment

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

Thank you! It's looking good, I have a few suggestions for organization. When moving the tests to test_visualization.jl it would also be nice if we could consolidate them into a loop over a single test since some of the visualization tests are identical.


If you want to extract the raw nodal coordinates and solution arrays to build your own custom plots or perform external data analysis, you can use the `get_coordinates` and `get_u` functions.

These functions use multiple dispatch to automatically reshape the flat ODE state vectors into intuitive, multidimensional tensors based on the underlying mesh type.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the "multidimensional tensors" comment is true for TreeMesh, not DGMultiMesh

coords = Trixi.get_coordinates(sol)
u_nodal = Trixi.get_u(sol)

# The arrays are now ready to be used with external tools like Makie.jl or Plots.jl
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you include an example of a Plots.jl command?

function get_coordinates(mesh, equations, solver, cache)
return cache.elements.node_coordinates
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a comment about the layout of the coordinates for TreeMesh vs DGMultiMesh?

Comment on lines +311 to +325
@testset "Nodal API" begin
trixi_include(@__MODULE__,
joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_basic.jl"))

semi = sol.prob.p

coords = Trixi.get_coordinates(sol)
@test size(coords) == (2, 4, 4, 256)
@test coords == semi.cache.elements.node_coordinates

u_nodal = Trixi.get_u(sol)
@test size(u_nodal) == (1, 4, 4, 256)
@test u_nodal == Trixi.wrap_array(sol.u[end], semi)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you move this to test_visualization.jl instead?

Comment on lines +158 to +167

@testset "Nodal API" begin
coords = Trixi.get_coordinates(sol)
@test size(coords) == size(semi.cache.elements.node_coordinates)
@test coords == semi.cache.elements.node_coordinates

u_nodal = Trixi.get_u(sol)
@test size(u_nodal) == size(Trixi.wrap_array(sol.u[end], semi))
@test u_nodal == Trixi.wrap_array(sol.u[end], semi)
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you move this to test_visualization.jl instead?

Comment on lines +25 to +34

@testset "Nodal API" begin
coords = Trixi.get_coordinates(sol)
@test size(coords) == size(semi.cache.elements.node_coordinates)
@test coords == semi.cache.elements.node_coordinates

u_nodal = Trixi.get_u(sol)
@test size(u_nodal) == size(Trixi.wrap_array(sol.u[end], semi))
@test u_nodal == Trixi.wrap_array(sol.u[end], semi)
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you move this to test_visualization.jl instead?

Comment on lines +42 to +52

@testset "Nodal API" begin
semi = sol.prob.p

coords = Trixi.get_coordinates(sol)
@test coords == semi.mesh.md.xyz

u_nodal = Trixi.get_u(sol)
@test size(u_nodal) == size(Trixi.wrap_array(sol.u[end], semi))
@test u_nodal == Trixi.wrap_array(sol.u[end], semi)
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you move this to test_visualization.jl instead?

Sign up for free to 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.

4 participants