Skip to content

Conversation

@acreyes
Copy link
Collaborator

@acreyes acreyes commented Sep 18, 2025

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
Copy link
Collaborator

@Yurlungur Yurlungur left a comment

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 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
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
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 on lines +176 to +156
// Concept to state that a typed-field is dependent on other
// fields, and is not itself an actual indexable field.
Copy link
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
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 on lines +98 to +122
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
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 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
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
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
Collaborator

Also changelog and mark non-wip :)

@acreyes
Copy link
Collaborator Author

acreyes commented Sep 20, 2025

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
Copy link
Collaborator

lroberts36 commented Sep 22, 2025

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
Copy link
Collaborator Author

acreyes commented Sep 22, 2025

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
Copy link
Collaborator

pgrete commented Oct 6, 2025

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
Copy link
Collaborator Author

acreyes commented Oct 6, 2025

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

5 participants