Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4bdba86
For the `particle_animator.py` script, use the length normalisation s…
mrhardman Aug 3, 2026
839d0a8
Initial changes to load a mesh from a GMSH .msh file in serial, and t…
mrhardman Aug 3, 2026
fbc2695
Define `use_external_msh` in the VANTAGE class to allow the same flag…
mrhardman Aug 13, 2026
fc820e9
Move definition of dg0 pointer to vantage header file, introduce sket…
mrhardman Aug 14, 2026
d53e0ee
Make separate function for writing the DMPlex to HDF5.
mrhardman Aug 14, 2026
3ef4de7
Use a c++ style static cast in `DMPlexCreateGmshFromFile()`.
mrhardman Aug 14, 2026
720781d
Restructure `vantage_dmplex.cxx` so that `create_dmplex_from_Bout_mes…
mrhardman Aug 14, 2026
03a1566
Make `kinetic_mesh_map` a class variable, read in map to global index…
mrhardman Aug 14, 2026
ab9b186
Add Rxy, Zxy, and corner variables to the BOUT.dmp.vantage.*.nc outpu…
mrhardman Aug 18, 2026
2b5f8ea
Set xlim based on command-line argument.
mrhardman Aug 21, 2026
f769f56
Use forward and backward tranfers in conversion from NESO-Particles d…
mrhardman Aug 21, 2026
0e0b1f7
Extend the kinetic -> bout mesh data transfer to the VantageSourceMan…
mrhardman Aug 21, 2026
d1c4ccf
Refactor the inputs of the [vantage] component so that pressure and t…
mrhardman Aug 24, 2026
a9dbfac
Experiments using .vtkhdf for prototype velocity-moment diagnostics o…
mrhardman Aug 28, 2026
b9d3d2c
Create function to contain kinetic moment diagnostic code.
mrhardman Aug 28, 2026
39d2c9d
Update derived diagnostics moment kernels before projection.
mrhardman Aug 28, 2026
1d38449
Calculate the Gamma = nu mean flow moment in the kinetic diagnostics.
mrhardman Aug 28, 2026
f8f0f5f
Refactor diagnostics calculation to enable the calculation of derived…
mrhardman Aug 28, 2026
61e1d25
Merge branch 'VANTAGE-Reactions-Coupling/dev' into VANTAGE-Reactions-…
mrhardman Sep 4, 2026
10924f9
Extend checks on cell volumes to cover the case where the kinetic mes…
mrhardman Sep 7, 2026
559302b
Ensure that `y_boundary_guards` is saved to that the `BOUT.dmp.vantag…
mrhardman Sep 8, 2026
1badeeb
Begin refactoring diagnostics so that they are managed through a clas…
mrhardman Sep 8, 2026
277ac52
Split kinetic diagnostic function into update and write functions.
mrhardman Sep 9, 2026
88a58f2
Initial move of BOUT++ style diagnostic functions to VantageDiagnosti…
mrhardman Sep 9, 2026
fca6dec
Extract BOUT++ Field2D from the computed scalar variable diagnostics …
mrhardman Sep 9, 2026
b146830
Split VantageSourceManager class to separate file to speed up compila…
mrhardman Sep 10, 2026
26986ed
Add a vector to store source data on the kinetic mesh.
mrhardman Sep 10, 2026
9a65ea0
Define get functions for plasma grid and kinetic mesh data.
mrhardman Sep 10, 2026
5891a06
Changes aimed to allow testing of mass conservation when the kinetic …
mrhardman Sep 10, 2026
2bc7f23
Make VantageDataTransfer class to handle the transfer of data between…
mrhardman Sep 11, 2026
324da12
Use VantageDataTransfer in VantageSourceManager.
mrhardman Sep 11, 2026
06d7c1f
Changes to initialise the plasma properties on Field2D on the BOUT++ …
mrhardman Sep 11, 2026
bde3529
Fix bug in `VantageDataTransfer::transfer_scalar_to_kinetic_mesh`.
mrhardman Sep 14, 2026
c60dcfc
Make the particle group variable passed as an arguement to enable the…
mrhardman Sep 15, 2026
3825b2a
Introduce particle sub groups which ensure neutral-plasma reactions a…
mrhardman Sep 15, 2026
cac6c3d
Modify the diagnostics so that the sources are written to both kineti…
mrhardman Sep 15, 2026
993353b
Attempt to calculate total mass for mass conservation check using the…
mrhardman Sep 15, 2026
43e8cb1
Fix mass conservation test by using the correct summation of neso_mes…
mrhardman Sep 17, 2026
aef21d0
Confirm that mass is conserved (in a slab) when density is computed f…
mrhardman Sep 17, 2026
c7517da
Write out neso_cell_volumes -> neso_cell_areas as BOUT++ Field2D, use…
mrhardman Sep 17, 2026
8921151
Test the sum of neutral mass and ionised mass by summing over sources.
mrhardman Sep 17, 2026
6962119
Support non-rectangular BOUT++ plasma grid cells, by computing the re…
mrhardman Sep 21, 2026
3c0161d
Refactor particle-pusher/runtest so that simulation directory is spec…
mrhardman Sep 22, 2026
abc713f
Include a .msh file and necessary data to generate a valid bout.grd.n…
mrhardman Sep 22, 2026
9fb7247
Use fewer x, y cells in the test of particle pushing on quad cells al…
mrhardman Sep 22, 2026
0246316
Extend `test_dmplex_cell_centres = true` to test cell centres also on…
mrhardman Sep 25, 2026
d08d05c
Reuse functions to replace copied code.
mrhardman Sep 25, 2026
248ce6b
Move helper and check functions to a library component.
mrhardman Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,24 @@ set(HERMES_SOURCES
include/zero_current.hxx
include/transform.hxx
include/fixed_fraction_radiation.hxx
include/simple_pump.hxx
)
include/simple_pump.hxx)

if(HERMES_USE_VANTAGE)
# include Hermes-VANTAGE include and source files
list(APPEND HERMES_SOURCES
src/vantage_dmplex.cxx
include/vantage_dmplex.hxx
src/vantage.cxx
list(
APPEND
HERMES_SOURCES
src/vantage_dmplex.cxx
include/vantage_dmplex.hxx
src/vantage_helperfunctions.cxx
include/vantage_helperfunctions.hxx
src/vantage_datatransfer.cxx
include/vantage_datatransfer.hxx
src/vantage_sources.cxx
include/vantage_sources.hxx
src/vantage_diagnostics.cxx
include/vantage_diagnostics.hxx
src/vantage.cxx
include/vantage.hxx)
endif()

Expand Down Expand Up @@ -406,11 +415,12 @@ if(HERMES_TESTS)
option(HERMES_UNIT_TESTS "Build the unit tests" ON)
option(SPACK_GTEST "Use spack-installed googletest for unit tests" ON)

# "gtest" and "GTest::gtest" are not the same, and the latter is preferred as being
# more complete. So, instead of just linking to "gtest", we will try to find the package
# using CMake if SPACK_GTEST is enabled. If not, then if gtest isn't already a target, then
# it will get it from the submodule. The GTest target is saved as its own variable
# so that it can be passed to both hermes_unit_tests.
# "gtest" and "GTest::gtest" are not the same, and the latter is preferred as
# being more complete. So, instead of just linking to "gtest", we will try to
# find the package using CMake if SPACK_GTEST is enabled. If not, then if
# gtest isn't already a target, then it will get it from the submodule. The
# GTest target is saved as its own variable so that it can be passed to both
# hermes_unit_tests.
set(HERMES_GTEST_TARGET "")
if(HERMES_UNIT_TESTS)
if(SPACK_GTEST)
Expand Down
102 changes: 36 additions & 66 deletions include/vantage.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,20 @@
#include "../include/component.hxx"
#include "bout/bout.hxx"
#include "bout/petsclib.hxx"
#include <bout/bout_types.hxx>
#include <bout/field2d.hxx>
#include <memory>
#include <neso_particles.hpp>
#include <neso_rng_toolkit.hpp>
#include <reactions/reactions.hpp>
#include <vector>
#include "../include/vantage_diagnostics.hxx"
#include "../include/vantage_sources.hxx"
#include "vantage_datatransfer.hxx"

using namespace NESO::Particles;
using namespace VANTAGE::Reactions;

/// @brief Data struct to hold information about a reaction source.
/// @param reaction_name Name of the reaction, e.g. "ionistaion"
/// @param source_name Name of the source, e.g. Siz (ion density source due to
/// ionisation).
/// @param accumulator CellwiseAccumulator to use to accumulate the source term for this
/// reaction.
/// @param particle_group ParticleGroup to which this source applies.
/// @param zeroer TransformationStrategy to use to zero the source term dat after
/// accumulation.
struct VantageSource {
std::string hermes_source_name;
std::string vantage_source_name;
std::shared_ptr<CellwiseAccumulator<REAL>> accumulator;
std::shared_ptr<ParticleGroup> particle_group;
std::shared_ptr<TransformationStrategy> zeroer;
Field2D source_data;
};

/// @brief Class to manage reaction channel sources from VANTAGE.
/// Source terms from VANTAGE are extracted from the accumulator.
/// These are then converted to actual sources, e.g. units of m^-3 s^-1 for a
/// density source.
class VantageSourceManager {
public:
VantageSourceManager(std::shared_ptr<PetscInterface::DMPlexInterface>& neso_mesh,
Mesh* bout_mesh, Options& units);

Mesh* bout_mesh;

// Register new source
void add_source(const std::string& hermes_source_name,
const std::string& vantage_source_name,
std::shared_ptr<CellwiseAccumulator<REAL>> accumulator,
std::shared_ptr<ParticleGroup> particle_group,
std::shared_ptr<TransformationStrategy> zeroer);

// Update the Hermes-3 source field using the accumulated data from corresponding
// VANTAGE source
void update_source(const std::string& hermes_source_name, double dt);

// Call update_source on all sources
void update_all_sources(double dt);

// Return data for a given Hermes-3 source name
Field2D get_data(const std::string& hermes_source_name);

private:
std::map<std::string, VantageSource> sources;
std::shared_ptr<PetscInterface::DMPlexInterface> neso_mesh;
Options& units;
};

// Need to declare empty struct because the Monitor needs it and it must be
// before component construction as it has the monitor as a member.
// See https://bout-dev.readthedocs.io/en/latest/user_docs/time_integration.html#monitoring-the-simulation-output
Expand Down Expand Up @@ -98,43 +53,58 @@ private:
BoutReal particle_time;
BoutReal N_w;
REAL dt;
BoutReal charge;
BoutReal AA; // mass
BoutReal initial_neutral_density;
int nsteps;
int num_cells_owned; // Number of VANTAGE cells owned per rank

Options bout_output_data; // Options object to hold output data for VANTAGE diagnostics
std::unique_ptr<bout::OptionsIO>
vantage_dump_writer; // OptionsIO object to write VANTAGE diagnostics

int mpi_rank; // Current rank ID
Mesh* bout_mesh; // Pointer to the BOUT++ mesh object
Field2D ion_density, neutral_density, total_density;
Field2D initial_neutral_density; // Initial VANTAGE kinetic neutral density
// Diagnostic variables on the kinetic mesh, for testing
std::vector<REAL> neutral_density, total_density;
std::vector<REAL> ion_density_kmsh;
// Field2D for storing plasma data coming from the plasma grid, that will be evaluated
// on to the kinetic mesh, and then on to the particles themselves.
Field2D electron_density, electron_temperature;
Field2D ion_density, ion_temperature;
std::vector<Field2D> ion_velocity;
// a threshold density, for reactions between neutrals and the plasma
REAL electron_density_threshold;
BoutReal total_mass_initial, total_mass;
std::string dmplex_filepath, vantage_dump_filepath,
particle_data_filepath; // Path for output files
// volumes of neso_mesh cells (from neso_mesh->dmh->get_cell_volume())
// in a vector of size of Nx*Ny, where Nx and Ny are the number of local
// x and y cells in the BOUT++ mesh (excluding guards)
std::vector<REAL> neso_mesh_cell_volumes_on_plasma_grid;


PetscLib petsc_lib; // Ensures PETSc is initialized for the lifetime of this component

DM dm;
std::shared_ptr<PetscInterface::DMPlexInterface> neso_mesh;
std::shared_ptr<SYCLTarget> sycl_target;
std::shared_ptr<PetscInterface::BoundaryInteraction2D>
b2d; // Boundary interaction object
std::shared_ptr<PetscInterface::DMPlexProjectEvaluateDG>
dg0; // DMPlex projection object
std::shared_ptr<PetscInterface::BoundaryInteraction2D> b2d;
std::shared_ptr<PetscInterface::DMPlexProjectEvaluateDG> project_eval_dg0;
std::shared_ptr<PetscInterface::DMPlexMeshCouplerDG0> mesh_coupler_dg0;
std::vector<REAL> dof_kinetic_mesh_scalar;
std::vector<REAL> dof_bout_mesh_scalar;
std::vector<PetscInt> kinetic_mesh_map; // variable for recording the map from serial to parallelised DMPlex cells in terms of a vector of integers
std::shared_ptr<H5Part> h5part; // HDF5 particle output object
std::vector<REAL>
h_project1; // Buffer for scalar projection/evaluation of NESO-Particles properties
std::shared_ptr<ParticleGroup> A_particle_group; // Particle group for main neutrals
std::shared_ptr<ParticleGroup> marker_group; // Particle group for rec markers

// Needed for Vantage::apply_boundary_conditions
std::shared_ptr<BoundaryReflection> reflection; // Boundary reflection object
void apply_boundary_conditions(ParticleSubGroupSharedPtr aa);

// These classes don't have a default constructor so need to be initialised as a unique_ptr
std::unique_ptr<VantageSourceManager>
std::shared_ptr<VantageDataTransfer> data_transfer; // Manager for VANTAGE data transfer
std::unique_ptr<VantageDiagnosticsManager>
diagnostics_manager; // Manager for VANTAGE diagnostics
std::shared_ptr<VantageSourceManager>
source_manager; // Manager for VANTAGE reaction sources
// These classes don't have a default constructor so need to be initialised as a unique_ptr
VantageMonitor monitor{this}; // Output monitor to schedule VANTAGE iterations

std::unique_ptr<ReactionController> reaction_controller;
Expand Down
73 changes: 73 additions & 0 deletions include/vantage_datatransfer.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once
#include "bout/bout.hxx"
#include <bout/bout_types.hxx>
#include <neso_particles.hpp>
#include <vector>

using namespace NESO::Particles;

/// @brief Class to handle transfer of data between kinetic mesh and plasma grid in VANTAGE.
class VantageDataTransfer {
public:
VantageDataTransfer(std::shared_ptr<PetscInterface::DMPlexInterface>& neso_mesh,
std::shared_ptr<PetscInterface::DMPlexProjectEvaluateDG>& project_eval_dg0,
std::shared_ptr<PetscInterface::DMPlexMeshCouplerDG0>& mesh_coupler,
Mesh* bout_mesh, size_t ndim_vector);

// transfer functions for physics scalars
void transfer_scalar_to_plasma_grid(
std::vector<REAL>& scalar_kinetic_mesh,
Field2D& scalar_plasma_grid);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm finding the name of this and similar objects confusing. What do you think about renaming them to make it clear they are quantities, e.g. plasma_grid_scalar.

void transfer_scalar_to_kinetic_mesh(
Field2D& scalar_plasma_grid,
std::vector<REAL>& scalar_kinetic_mesh);
void transfer_scalar_to_particle_property(
std::vector<REAL>& scalar_kinetic_mesh,
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property);
void transfer_scalar_to_particle_property(
Field2D& scalar_plasma_grid,
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property);
void transfer_particle_property_to_scalar(
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property,
std::vector<REAL>& scalar_kinetic_mesh);
void transfer_particle_property_to_vector(
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property,
std::vector<REAL>& vector_kinetic_mesh);

// transfer functions for physics vectors
void transfer_vector_to_particle_property(
std::vector<Field2D>& vector_plasma_grid,
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property);
void transfer_vector_to_particle_property(
std::vector<REAL>& vector_kinetic_mesh,
std::shared_ptr<ParticleGroup>& A_particle_group,
std::string particle_property);
void transfer_vector_to_kinetic_mesh(
std::vector<Field2D>& vector_plasma_grid,
std::vector<REAL>& vector_kinetic_mesh);


private:
// internal variables needed for data transfer
std::shared_ptr<PetscInterface::DMPlexInterface> neso_mesh;
std::shared_ptr<PetscInterface::DMPlexProjectEvaluateDG> project_eval_dg0;
std::shared_ptr<PetscInterface::DMPlexMeshCouplerDG0> mesh_coupler;

// the bout_mesh variable needed for Hermes-3/BOUT++ diagnostics
Mesh* bout_mesh;

// variable used to transfer dofs from kinetic
// to BOUT++ meshes
size_t num_cells_owned_bout_grid;
std::vector<REAL> dof_bout_grid_scalar;
std::vector<REAL> dof_kinetic_mesh_scalar;
// number of physics vector components
size_t ndim_vector;
std::vector<REAL> dof_bout_grid_vector;
std::vector<REAL> dof_kinetic_mesh_vector;
};
87 changes: 87 additions & 0 deletions include/vantage_diagnostics.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once
#include "bout/bout.hxx"
#include <bout/bout_types.hxx>
#include <memory>
#include <neso_particles.hpp>
#include <string>
#include <vector>
#include "../include/vantage_datatransfer.hxx"
#include "../include/vantage_sources.hxx"

using namespace NESO::Particles;

REAL calculate_total_mass(std::vector<REAL>& density,
std::shared_ptr<PetscInterface::DMPlexInterface>& neso_mesh);
REAL calculate_total_mass(Field2D& density,
std::vector<REAL>& neso_cell_volume_on_bout_mesh);

/// @brief Class to manage diagnostics from VANTAGE.
class VantageDiagnosticsManager {
public:
VantageDiagnosticsManager(std::string vtkhdf_filename,
std::shared_ptr<PetscInterface::DMPlexInterface>& neso_mesh,
// neso_mesh cell volumes on the BOUT++ mesh
std::vector<REAL>& neso_cell_volumes,
std::shared_ptr<ParticleGroup>& A_particle_group,
std::shared_ptr<VantageDataTransfer>& data_transfer,
std::shared_ptr<VantageSourceManager>& source_manager,
BoutReal N_w, BoutReal mass,
Mesh* bout_mesh, Options& units,
std::string vantage_dump_filepath);

// compute the kinetic velocity moments and
// store in private variables
void update_kinetic_velocity_moments();
// write kinetic diagnostics to a vtkhdf file
void write_kinetic_velocity_moment_diagnostics(int istep, std::vector<REAL>& ion_density);
// transfer kinetic moments to BOUT++ grid
void transfer_moments_to_plasma_grid();
// write BOUT++ style diagnostics on the BOUT++ grid
void write_bout_diagnostics(
Field2D& ion_density,
BoutReal particle_time);
std::vector<REAL> get_density_kinetic_mesh();
// Field2D transfer_scalar_to_plasma_grid(std::vector<REAL>& scalar_field);

private:
// internal variables needed for diagnostics
std::string vtkhdf_filename;
std::shared_ptr<PetscInterface::DMPlexInterface> neso_mesh;
std::shared_ptr<ParticleGroup> A_particle_group;
std::shared_ptr<VantageDataTransfer> data_transfer;
// pointer to vantage source_manager for diagnostics
std::shared_ptr<VantageSourceManager> source_manager;
BoutReal N_w;
BoutReal mass;

// the bout_mesh variable needed for Hermes-3/BOUT++ diagnostics
Mesh* bout_mesh;
Options& units;
std::string vantage_dump_filepath;
Options bout_output_data; // Options object to hold output data for VANTAGE diagnostics
std::unique_ptr<bout::OptionsIO>
vantage_dump_writer; // OptionsIO object to write VANTAGE diagnostics

// variables used to store the moments of
// the neutral distribution function, on
// the kinetic mesh
const size_t ndimv = 2; // number of velocity dimensions
std::vector<REAL> density;
std::vector<REAL> energy;
std::vector<REAL> gamma;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we rename this to "flux"? "gamma" usually means sheath heat transfer coefficient. It would also be helpful to include units here.

std::vector<REAL> uvector;
std::vector<REAL> pressure;
std::vector<REAL> temperature;

// variable used to transfer dofs from kinetic
// to BOUT++ meshes
std::vector<REAL> dof_bout_mesh_scalar;
// variables used to store the moments of the
// neutral distribution function projected on
// to the BOUT++ grid
Field2D density_plasma_grid;
Field2D energy_plasma_grid;
Field2D pressure_plasma_grid;
Field2D temperature_plasma_grid;
// n.b. only treat scalar variables for now
};
19 changes: 15 additions & 4 deletions include/vantage_dmplex.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,21 @@ std::vector<PetscInt> cells_definition_from_RZ_ivertex(
Field2D& ivertex_lower_right_corners, Field2D& ivertex_upper_right_corners,
Field2D& ivertex_upper_left_corners);

DM create_dmplex_from_Bout_mesh(Mesh* bout_mesh, Options& mesh_options,
std::shared_ptr<SYCLTarget> sycl_target,
std::string dmplex_h5_filename);
void create_dmplex_from_Bout_mesh(DM* dm, Mesh* bout_mesh, Options& mesh_options,
std::shared_ptr<SYCLTarget> sycl_target);

#endif
void create_dmplex_from_GMSH_msh(DM* dm, std::string msh_file);

void write_dmplex_to_file(DM dm, std::string dmplex_name, std::string dmplex_h5_filename);

BoutReal get_triangle_area(size_t itriangle,
const std::vector<double>& vertices,
const std::vector<int>& tri_cell_vertices);

std::vector<double> get_triangle_vertices();

std::vector<int> get_triangle_cell_definition();

#endif

#endif // VANTAGE_DMPLEX_H
Loading
Loading