Particle property utilities#

Properties, Species, and their usage#

In order to offer tools for the standardisation of particle properties, VANTAGE-Reactions defines several utilities. For the reaction abstraction to work, it requires knowledge of the various required properties of the particles. Some of these are relatively simple, and can be passed as Sym objects to the various constructors. However, some reactions might require access to multiple different properties, e.g. particle velocities, weights, multiple different fields, and so on. This means that a standardised way of tracking the names and indexing of various properties is very helpful. Unfortunately, due to SYCL requirements (device-copyability, etc.), we are unable to use standard maps directly on device. To work around this, the library utilises a combination of a centralised (extensible) enum struct, and a mapping from the enums to the various particle property names.

Default property enums and maps are supplied with the library, but users can extend/modify one or both. Built-in reaction data and kernels use some or all of the standard_properties_enum entries. These are:

  1. reacted_flag

  2. panic

  3. position

  4. velocity

  5. cell_id

  6. id

  7. tot_reaction_rate

  8. weight

  9. internal_state

  10. boundary_intersection_point

  11. boundary_intersection_normal

  12. boundary_intersection_metadata

  13. temperature

  14. density

  15. flow_speed

  16. source_energy

  17. source_momentum

  18. source_density

  19. fluid_density

  20. fluid_temperature

  21. fluid_flow_speed

If a user-defined reaction extension requires more properties than the above, the following method of extension is preferred.

Extending the default properties enum#
struct CustomPropertiesEnum : StandardPropertiesEnum {
public:
  enum {
    test_custom_prop1 = default_properties.fluid_flow_speed + 1,
    test_custom_prop2
  };
};

The above enum entries get mapped to strings used to construct Sym objects. The default map is:

  • reacted_flag - “PARTICLE_REACTED_FLAG”

  • panic - “REACTIONS_PANIC_FLAG”

  • position - “POSITION”

  • velocity - “VELOCITY”

  • cell_id - “CELL_ID”

  • id - “ID”

  • tot_reaction_rate - “TOT_REACTION_RATE”

  • weight - “WEIGHT”

  • internal_state - “INTERNAL_STATE”

  • boundary_intersection_point - defined by NESO-Particles BoundaryInteractionSpecification::intersection_point

  • boundary_intersection_normal - defined by NESO-Particles BoundaryInteractionSpecification::intersection_normal

  • boundary_intersection_metadata - defined by NESO-Particles BoundaryInteractionSpecification::intersection_metadata

  • temperature - “TEMPERATURE”

  • density - “DENSITY”

  • flow_speed - “FLOW_SPEED”

  • source_energy - “SOURCE_ENERGY”

  • source_momentum - “SOURCE_MOMENTUM”

  • source_density - “SOURCE_DENSITY”

  • fluid_density - “FLUID_DENSITY”

  • fluid_temperature - “FLUID_TEMPERATURE”

  • fluid_flow_speed - “FLUID_FLOW_SPEED”

To create new maps with custom enums or remap the above we can use the following:

Extending and remaping the default property map#
inline void custom_property_map_example() {

  auto custom_props = CustomPropertiesEnum();

  // The property map is wrapped in a class that offers basic consistency checks
  // We can initialise a custom property map with the default constructor, which
  // uses the default property mapping

  auto custom_property_map = PropertiesMap();

  // We can then extend the map to work with our custom enum
  custom_property_map[custom_props.test_custom_prop1] = "TEST_PROP1";
  custom_property_map[custom_props.test_custom_prop2] = "TEST_PROP2";

  // We can also remap the existing properties
  custom_property_map[default_properties.weight] = "w";

  return;
}

The main utility with property maps comes from the added flexibility when working with reaction abstractions. For example, a developer can write a reaction data object that uses the fluid_temperature enum, and the user can use that object with, for example, both “ELECTRON_TEMPERATURE” and “ION_TEMPERATURE” particle properties by passing a custom map (see examples with reaction data and kernels).

Species and the Properties container#

VANTAGE-Reactions offers a simple Species class to encapsulate species data. It specifies the species name, and optionally the species mass, charge, and integer ID associated with it. Together with the :class:Properties` class, species can be used to bundle required properties for use in reaction abstractions, as well as in incremental construction of NESO-Particles ParticleSpec objects, see below.

A :class:Properties` container can have simple properties (directly translated to Sym names) and species properties, which are combined with species name to get the NESO-Particles Sym names. :class:Properties` containers use the property enums and maps as defined above. This allows for flexibility when defining required properties and their mapping to data stored on particles.

Example of constructing Species and Properties containers#
inline void property_container_example() {

  // We first construct a few species objects. Here we only need the species
  // name, so we set the mass, charge, and ID to arbitrary values

  auto electron_species = Species("ELECTRON", // name
                                  1.0,        // mass
                                  -1.0,       // charge
                                  -1          // ID
  );
  // Only the species name is required, since the the main use of the Species
  // class is property name construction

  auto ion_species = Species("ION");

  // We can encapsulate a few integer properties directly. We refer to
  // properties using a property enum, here the default one

  auto int_props = Properties<INT>(std::vector<int>{
      default_properties.id, default_properties.internal_state});

  // We can do the same for real properties, but this time we also store some
  // species information
  auto real_props = Properties<REAL>(
      std::vector<int>{default_properties.velocity, default_properties.weight},
      std::vector<Species>{electron_species, ion_species},
      std::vector<int>{default_properties.temperature,
                       default_properties.density});

  // In order to convert property information into NESO-Particle Sym names,
  // we require a property map. This defaults to the default_map object, but
  // users can supply their own, usually to the constructor of the object that
  // own the properies.

  auto req_int_prop_names = int_props.simple_prop_names(
      get_default_map()); // explicitly passing the default map - could be a
                          // user-defined map
  auto req_simple_real_prop_names =
      real_props.simple_prop_names(); // map defaults to default_map

  // When requesting species property names the species name is concatenated to
  // the property name In the example here, the required species real properties
  // using the default map will contain:
  //  - "ELECTRON_TEMPERATURE"
  //  - "ELECTRON_DENSITY"
  //  - "ION_TEMPERATURE"
  //  - "ION_DENSITY"

  auto req_species_real_prop_names = real_props.species_prop_names();

  return;
}

ParticleSpecBuilder#

The :class:Properties` container class can be used to build up ParticleSpec objects using the ParticleSpecBuilder class. It allows for mixing ParticleSpec objects and :class:Properties` objects to construct a final ParticleSpec.

Constructing a particle spec using the builder#
inline void spec_builder_example() {

  // The recommended way of initialising a ParticleSpecBuilder 
  // is by using the following constructor, which will 
  // make sure that the properties present satisfy requirements in the 
  // library 
  
  auto particle_spec_builder_default = ParticleSpecBuilder(2 // the dimensionality of the default 
                                                             // position and velocity props 
                                                          ); // A map (see below) can additionally i
                                                             // be passed here to remap
                                                             // some of the default property Syms

  // Alternatively, if full control is required 
  // a spec builder can be initialised with an existing spec,
  // which will not add any of the default properties.
  // The user is responsible then for ensuring that
  // all required properties are available when requested.
  auto basic_spec = ParticleSpec{ParticleProp(Sym<REAL>("POSITION"), 2, true),
                                 ParticleProp(Sym<INT>("CELL_ID"), 1, true)};
  auto particle_spec_builder = ParticleSpecBuilder(basic_spec);

  // Default properties alias
  auto props = default_properties;

  // We can create multiple property containers
  auto int_props = Properties<INT>(std::vector<int>{props.internal_state});
  auto scalar_real_props = Properties<REAL>(
      std::vector<int>{props.weight}, std::vector<Species>{Species("ELECTRON")},
      std::vector<int>{props.temperature});

  auto vector_real_props =
      Properties<REAL>(std::vector<int>{props.velocity},
                       std::vector<Species>{Species("ELECTRON")},
                       std::vector<int>{props.flow_speed});

  particle_spec_builder.add_particle_prop(int_props);

  // We can also remap enum properties as we add them using property maps
  auto new_map = get_default_map();
  new_map[props.weight] = "w";
  particle_spec_builder.add_particle_prop(
      scalar_real_props, // The added properties
      1,                 // property dimensionality
      false,  // whether the property is a position property or not (most often
              // false)
      new_map // the used property map - defaults to default_map
  );

  // Here we add the vector (2D) properties
  particle_spec_builder.add_particle_prop(vector_real_props, 2);

  // Finally we can merge the particle spec in the builder with other specs
  // (will ignore duplicates)
  particle_spec_builder.add_particle_spec(
      ParticleSpec{ParticleProp(Sym<REAL>("w"), 1, false),
                   ParticleProp(Sym<REAL>("NEW_PROP"), 2, false)});

  auto built_spec = particle_spec_builder.get_particle_spec();
  return;
}

When constructed using the recommended constructor, the following properties are added by default:

  • position

  • cell_id

  • velocity

  • panic

  • id

  • internal_state

  • reacted_flag

  • weight

  • tot_reaction_rate

The panic flag#

Since capturing exceptions on all SYCL devices is not feasible, the library includes the panic flag, and operations which can fail will populate the panic property on the particles. To check the number of panicked particles in a subgroup particle_subgroup, use panicked(particle_subgroup). In case the panic flag has been remapped, make sure you pass the used map to panicked as the second argument.