Skip to content

Pack on virtual fields - #1322

Merged
acreyes merged 26 commits into
developfrom
acreyes/virtual-type-vars
Aug 27, 2026
Merged

Pack on virtual fields#1322
acreyes merged 26 commits into
developfrom
acreyes/virtual-type-vars

Conversation

@acreyes

@acreyes acreyes commented Sep 18, 2025

Copy link
Copy Markdown
Collaborator

PR Summary

This PR allows for type-based packs to pack "virtual" fields that don't exist in memory, but can be evaluated from their own set of dependent variables in the pack by calling a provided evaluate function.

This seems related to #1228, but doesn't provide that capability for outputs as mentioned for ADIOS2.

Also adds SubPacks to provide stencil (0, 1, 2, & 3D) views into a pack centered on a meshblock and ijk index. This can simplify the need to pass around extra indices in the evaluate functions for virtual fields.

PR Checklist

  • Code passes cpplint
  • New features are documented.
  • Adds a test for any bugs fixed. Adds tests for new features.
  • Code is formatted
  • Changes are summarized in CHANGELOG.md
  • Change is breaking (API, behavior, ...)
    • Change is additionally added to CHANGELOG.md in the breaking section
    • PR is marked as breaking
    • Short summary API changes at the top of the PR (plus optionally with an automated update/fix script)
  • CI has been triggered on Darwin for performance regression tests.
  • Docs build
  • (@lanl.gov employees) Update copyright on changed files

@acreyes acreyes added the enhancement New feature or request label Sep 18, 2025

@Yurlungur Yurlungur left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is very cool and I like it a lot. It definitely needs a bit of documentation though. Though I suppose we also don't have documentation for type-based variables, which is sorely needed...

Comment thread src/pack/pack_utils.hpp Outdated
Comment on lines +170 to +174
template <typename T, int... tuv>
struct TUV_t<T, tuv...> : impl::TUV_t<T, TopologicalElement::CC, tuv...> {};

template <typename T, TopologicalElement te, int... tuv>
struct TUV_t<T, te, tuv...> : impl::TUV_t<T, te, tuv...> {};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

with the variadics is this going to get correctly resolved?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think so, since the TE & ints are different types. Happy to add some static asserts like this to the unit tests to demonstrate. Unless there is a different edge case you're thinking of?

Comment thread src/pack/pack_utils.hpp
Comment on lines +176 to +177
// Concept to state that a typed-field is dependent on other
// fields, and is not itself an actual indexable field.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should the concepts stuff be in concepts_lite or should it be here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It seemed specialized enough to the pack types that I put it here, but if the convention is to have all concepts in concepts_lite I'm happy to move it

Comment thread src/utils/type_list.hpp
Comment on lines +98 to +117
template <typename, typename, typename...>
struct UnionTypeLists {};

template <typename... Ts>
struct UnionTypeLists<TypeList<Ts...>, TypeList<>> {
using type = TypeList<Ts...>;
};

template <typename... Ts, typename V, typename... Vs>
struct UnionTypeLists<TypeList<Ts...>, TypeList<V, Vs...>> {
using TL = TypeList<Ts...>;
using type = std::conditional_t<
TL::template IsIn<V>(), TL,
typename UnionTypeLists<TypeList<Ts..., V>, TypeList<Vs...>>::type>;
};

template <typename T, typename U, typename V, typename... Args>
struct UnionTypeLists<T, U, V, Args...> {
using type = typename UnionTypeLists<typename UnionTypeLists<T, U>::type, V>::type;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm surprised there isn't functionality for this in the standard

Comment thread src/pack/pack_utils.hpp Outdated
Comment on lines +261 to +263
// Get a TypeList of all the non-dependent variables that make up the requested types.
template <typename... Ts>
using all_dependent_variables_t = decltype(impl::AllDependentVariables::get(Ts()...));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The name and the comment disagree. Is this all dependent variables or all independent variables?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The types should be renamed. It is all the independent variables pulled out from the dependent variables

@Yurlungur

Copy link
Copy Markdown
Collaborator

Also changelog and mark non-wip :)

@acreyes

acreyes commented Sep 20, 2025

Copy link
Copy Markdown
Collaborator Author

This is very cool and I like it a lot. It definitely needs a bit of documentation though. Though I suppose we also don't have documentation for type-based variables, which is sorely needed...

Happy to add both in this PR.

@lroberts36

lroberts36 commented Sep 22, 2025

Copy link
Copy Markdown
Collaborator

This is very cool! I am wondering though about how complex the template stuff needs to be. Why can't we just do something like

class my_derived_var_t : public variable_names::dependent_var_base_t<...> { 
  ...
  template <class pack_t>
  KOKKOS_INLINE_FUNCTION
  Real evaluate(const pack_t &pack, int b, TopologicalElemet te, int k, int j, int i) {
    return pack(b, var1_t(), te, k, j, i) + pack(b, var2_t(), te, k, j, i);
  }
};

and

template <class TIn, REQUIRES(IncludesType<TIn, Ts...>::value && std::is_base_of_v<dependent_var_base_t, TIn>)>
  KOKKOS_INLINE_FUNCTION Real operator()(const int b, const TE el, const TIn &t,
                                          const int k, const int j, const int i) const {
    return t.evaluate(*this, b, te, k, j, i);
  }

I think there are obviously some circular dependencies that could crop up here (e.g. var2_t is a dependent variable that depends on my_derived_var_t), but assuming the downstream user is responsible this allows things like stencil operations etc. (Caveat: I could be missing some obvious reason this doesn't work since I didn't code it up.)

@acreyes

acreyes commented Sep 22, 2025

Copy link
Copy Markdown
Collaborator Author

This is very cool! I am wondering though about how complex the template stuff needs to be. Why can't we just do something like

class my_derived_var_t : public variable_names::dependent_var_base_t<...> { 
  ...
  template <class pack_t>
  KOKKOS_INLINE_FUNCTION
  Real evaluate(const pack_t &pack, int b, TopologicalElemet te, int k, int j, int i) {
    return pack(b, var1_t(), te, k, j, i) + pack(b, var2_t(), te, k, j, i);
  }
};

and

template <class TIn, REQUIRES(IncludesType<TIn, Ts...>::value && std::is_base_of_v<dependent_var_base_t, TIn>)>
  KOKKOS_INLINE_FUNCTION Real operator()(const int b, const TE el, const TIn &t,
                                          const int k, const int j, const int i) const {
    return t.evaluate(*this, b, te, k, j, i);
  }

I think there are obviously some circular dependencies that could crop up here (e.g. var2_t is a dependent variable that depends on my_derived_var_t), but assuming the downstream user is responsible this allows things like stencil operations etc. (Caveat: I could be missing some obvious reason this doesn't work since I didn't code it up.)

That is much simpler and I think would work just fine. My thought was that it would be nice to make it easy to unit test the evaluate methods downstream separate from the packing infrastructure. I think it would be simple enough to mock something that works with the template in your example. This could probably thread later with the sub-pack (#1265 ) infrastructure.

@pgrete

pgrete commented Oct 6, 2025

Copy link
Copy Markdown
Collaborator

I just got the chance to look at this PR.
It's definitely a nice feature and the user facing interface looks clean and easy to use/write.

Quite of few of our targeted use cases would involve either stencil based ops and/or other information (say an adiabatic index).
Thus the structure @lroberts36 suggested seems more flexible in that regard.

How big of a lift do you think this would be (asking from the point of view whether we should get this interface in as is [pending review comments] or spend "just a little" more time on getting a more flexible version in first place).

@acreyes

acreyes commented Oct 6, 2025

Copy link
Copy Markdown
Collaborator Author

I just got the chance to look at this PR. It's definitely a nice feature and the user facing interface looks clean and easy to use/write.

Quite of few of our targeted use cases would involve either stencil based ops and/or other information (say an adiabatic index). Thus the structure @lroberts36 suggested seems more flexible in that regard.

How big of a lift do you think this would be (asking from the point of view whether we should get this interface in as is [pending review comments] or spend "just a little" more time on getting a more flexible version in first place).

I think luke's suggestion would be pretty easy to implement and would be a better starting place for this kind of feature.

@acreyes
acreyes force-pushed the acreyes/virtual-type-vars branch from 2786e1e to b071c5a Compare December 10, 2025 16:28
@acreyes acreyes changed the title [WIP] Pack on virtual fields Pack on virtual fields Dec 10, 2025
@Yurlungur

Copy link
Copy Markdown
Collaborator

@acreyes do we still want to merge this? I'm still pretty excited about it

@acreyes

acreyes commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

@acreyes do we still want to merge this? I'm still pretty excited about it

Yeah it is something I want to use as well. I think @pgrete wanted to review. Looking at #1415 I think that the SubPacks I added here could be made to behave more like the pack views introduced there, and might be something I want to do before merging this assuming that goes through first.

@Yurlungur

Copy link
Copy Markdown
Collaborator

Sounds good. @pgrete can you review?

Comment on lines +88 to +95
Slicing into ``SparsePack``\ s with ``SubPack``\ s
--------------------------------------------------

A `SubPack` provdies a view into a slice of a `SparsePack` along a given dimension(s).
`SubPack`\ s are constructed with a block + `kji` index to give a slice into the
fields at the meshblock + cell index. When a `SubPack` is constructed with the
`Axis` template parameters then the `SubPack` also providies a view into slices
of the `SparsePack` along the provided axes offset from the provided `ijk` indices.

@lroberts36 lroberts36 Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think a lot of this has overlap with the pack_views in my loop abstraction PR. We should think about if there is a way to combine things, but maybe after this is merged.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Probably it is mostly orthogonal after I have looked some more. I could change pack_views to support virtual variables, but that would be separate from SubPacks.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I was thinking of redoing this on top of the pack_views. My first look at what you added seemed to me like it would be pretty straightforward.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pack_views are pretty tightly coupled to the loop abstraction stuff, so maybe not a replacement unless everyone switched to the loop abstraction.

@lroberts36 lroberts36 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM! I think one of my comments was free floating instead of part of the review.

Comment thread src/pack/make_pack_descriptor.hpp Outdated

template <class... Ts, class MT>
template <class... Ts, class MT,
REQUIRES(!variable_names::dependent_variable_v<Ts> && ...)>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
REQUIRES(!variable_names::dependent_variable_v<Ts> && ...)>
requires(!variable_names::dependent_variable_v<Ts> && ...)>

Comment thread src/pack/make_pack_descriptor.hpp Outdated

template <class... Ts, class MT>
template <class... Ts, class MT,
REQUIRES(!variable_names::dependent_variable_v<Ts> && ...)>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
REQUIRES(!variable_names::dependent_variable_v<Ts> && ...)>
requires(!variable_names::dependent_variable_v<Ts> && ...)>

Comment thread src/pack/make_pack_descriptor.hpp Outdated
}

template <class... Ts, class MT,
REQUIRES(variable_names::dependent_variable_v<Ts> || ...)>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
REQUIRES(variable_names::dependent_variable_v<Ts> || ...)>
requires(variable_names::dependent_variable_v<Ts> || ...)>

Comment on lines +88 to +95
Slicing into ``SparsePack``\ s with ``SubPack``\ s
--------------------------------------------------

A `SubPack` provdies a view into a slice of a `SparsePack` along a given dimension(s).
`SubPack`\ s are constructed with a block + `kji` index to give a slice into the
fields at the meshblock + cell index. When a `SubPack` is constructed with the
`Axis` template parameters then the `SubPack` also providies a view into slices
of the `SparsePack` along the provided axes offset from the provided `ijk` indices.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Probably it is mostly orthogonal after I have looked some more. I could change pack_views to support virtual variables, but that would be separate from SubPacks.

@acreyes
acreyes force-pushed the acreyes/virtual-type-vars branch from 086c4bb to 0129de7 Compare August 21, 2026 22:22
@acreyes
acreyes enabled auto-merge August 26, 2026 18:08
@acreyes
acreyes merged commit 25f5436 into develop Aug 27, 2026
44 checks passed
@acreyes
acreyes deleted the acreyes/virtual-type-vars branch August 27, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants