specfem::tag_dispatch¶
-
namespace tag_dispatch¶
- group specfem::tag_dispatch
Compile-time element-type dispatch infrastructure.
tag_dispatchprovides a composable, zero-overhead mechanism for enumerating every valid combination of element tags (dimension × medium × property × attenuation × boundary), iterating over those combinations at compile time, and storing per-combination data in type-safe containers.Motivation¶
SPECFEM++ supports many element flavours —
elastic_psv,elastic_sh,acoustic, etc. in 2-D;elastic,acousticin 3-D — each with its own material property, attenuation, and boundary-condition variants. The raw Cartesian product of all tag enumerators contains many physically meaningless entries (e.g. dim3 + elastic_sh).tag_dispatchencodes the validity rules once (inis_valid.hpp) and propagates them automatically to every higher-level facility.Step 1 — Declare an element set with <tt>element_combinations</tt>¶
Compose named tag-set types using
operator*to describe which tag values are in play. The resulting type,ET, holds aconstexprarrayET::combosof only the valid tuples:namespace td = specfem::tag_dispatch; namespace el = specfem::element; using ET = decltype( td::dimension_set <el::dimension_tag::dim2>{} * td::medium_set <el::medium_tag::elastic_psv, el::medium_tag::elastic_sh, el::medium_tag::acoustic>{} * td::property_set <el::property_tag::isotropic>{} * td::attenuation_set<el::attenuation_tag::none, el::attenuation_tag::constant_isotropic>{} * td::boundary_set <el::boundary_tag::none, el::boundary_tag::stacey>{}); // ET::size = number of valid combos (invalid cross-products are dropped) // ET::combos = constexpr array of TagValueTuples, e.g.: // combos[0] = (dim2, elastic_psv, isotropic, none, none) // combos[1] = (dim2, elastic_psv, isotropic, none, stacey) // combos[2] = (dim2, elastic_psv, isotropic, constant_isotropic, none) // combos[3] = (dim2, elastic_psv, isotropic, constant_isotropic, stacey) // combos[4] = (dim2, elastic_sh, isotropic, none, none) // ... (acoustic combos follow; dim2+elastic_sh+stacey omitted as invalid)
Each entry in
ET::comboscorresponds to one concrete element flavour. The combo at indexImaps directly to aspecfem::tags::Tags<...>type viacombo_to_tags_t<ET, I, ET::combo_type::arity>.Step 2 — Iterate with <tt>for_each</tt>¶
for_eachcalls a generic lambda once per valid combo, passing the combo’sTags<...>type as a template argument. This is the primary mechanism for instantiating type-specialised kernels or populating metadata at startup:td::for_each(ET{}, []<typename TagsType>() { // TagsType == specfem::tags::Tags<dim2, elastic_psv, isotropic, none, none> // for the first combo, then Tags<...> for each subsequent valid combo. setup_kernel<TagsType>(); });
Step 3 — Store per-combo data with <tt>Storage</tt> or¶
TypedStorage**
Storage<T, ET>** — every combo holds the same typeT. Suitable for Kokkos views, counters, or any homogeneous per-combo resource:td::Storage<Kokkos::View<double*>, ET> fields( []<typename TagsType>() { return Kokkos::View<double*>("displacement", n_elems); }); // Compile-time access — zero-overhead base-class upcast: using T = specfem::tags::Tags< el::dimension_tag::dim2, el::medium_tag::elastic_psv, el::property_tag::isotropic, el::attenuation_tag::none, el::boundary_tag::none>; Kokkos::View<double*>& v = fields.get<T>(); // Runtime access — linear scan, host only, homogeneous stores only: auto& v2 = fields.get(el::medium_tag::elastic_psv, el::boundary_tag::none);
**
TypedStorage<Tmpl, ET>** — each combo holdsTmpl<TagsType>, giving a different concrete type per slot. Suitable for type-specialised kernel objects or policy structs:template <typename TagsType> struct Assembler { void run(); }; td::TypedStorage<Assembler, ET> assemblers( []<typename TagsType>() { return Assembler<TagsType>{}; }); Assembler<T>& a = assemblers.get<T>(); // T as defined above a.run();
Both container types support
deep_copy(dest, src)for Kokkos host/device transfers (requires the sameETbut allows different policies, e.g. device view vs host-mirror view).Putting it all together¶
A typical assembly pattern declares the element set once, builds storage from it, and then drives all per-combo work through
for_each:// 1. Element set (usually a shared type alias) using ET = decltype(td::dimension_set<el::dimension_tag::dim2>{} * td::medium_set<el::medium_tag::elastic_psv, el::medium_tag::acoustic>{} * td::property_set<el::property_tag::isotropic>{} * td::attenuation_set<el::attenuation_tag::none>{} * td::boundary_set<el::boundary_tag::none, el::boundary_tag::stacey>{}); // 2. Storage td::Storage<Kokkos::View<int*>, ET> element_counts( []<typename TagsType>() { return Kokkos::View<int*>("counts", 1); }); td::TypedStorage<Assembler, ET> assemblers( []<typename TagsType>() { return Assembler<TagsType>{}; }); // 3. Dispatch td::for_each(ET{}, [&]<typename TagsType>() { auto& counts = element_counts.get<TagsType>(); auto& asm = assemblers.get<TagsType>(); asm.assemble(counts); });
Component summary¶
Header
Provides
tag_dispatch/is_valid.hppTagValueTuple,is_valid_*_combopredicatestag_dispatch/element_combinations.hppNamed tag-set types,
element_combinations| |tag_dispatch/for_each.hpp|for_each(ET, lambda)compile-time iteration | |tag_dispatch/find_in.hpp|find_in_t/find_in_vcompile-time index lookup | |tag_dispatch/storage.hpp|Storage<T,ET>,TypedStorage<Tmpl,ET>,deep_copy|
Core Functionality:
Helper Utilities: