From 81064a46431383a3fcae006ac13ba7fac55fb8fb Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 25 Mar 2026 12:35:06 -0500 Subject: [PATCH 1/3] Add NumericVector::is_effectively_serial() Shorthand to make it easier to determine whether a vector can be handled using optimized-for-serial code paths regardless of its conceptual status. --- include/numerics/numeric_vector.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index a81131b403..7a1e8a2d80 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -150,9 +150,24 @@ class NumericVector : public ReferenceCountedObject>, /** * \returns The type (SERIAL, PARALLEL, GHOSTED) of the vector. + * + * This is metadata reflecting what type of vector was requested, + * which should reflect what level of replication and ghosting is + * necessary in parallel. */ ParallelType type() const { return _type; } + /** + * \returns Whether the vector is effectively serial, i.e. whether + * all vector data is accessible on every processor. + * + * This is true for explicitly SERIAL vectors, but also for vectors + * on serial (1-processor) communicators. + */ + + bool is_effectively_serial() const + { return (this->n_processors()==1) || (_type == SERIAL); } + /** * \returns The type (SERIAL, PARALLEL, GHOSTED) of the vector. * From cbf5ab86bcf46c709dfd0c3c37e9ca1244305586 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 25 Mar 2026 12:36:12 -0500 Subject: [PATCH 2/3] Create Seq non-SERIAL PetscVectors when we can This ought to preserve the performance improvements of Seq vectors and serial codepaths without losing PARALLEL-vs-GHOSTED metadata. --- include/numerics/petsc_vector.h | 23 ++++++++++++----------- src/numerics/petsc_vector.C | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/numerics/petsc_vector.h b/include/numerics/petsc_vector.h index 6c90c3d89f..b09b89a8e9 100644 --- a/include/numerics/petsc_vector.h +++ b/include/numerics/petsc_vector.h @@ -731,11 +731,7 @@ void PetscVector::init (const numeric_index_type n, if (this->initialized()) this->clear(); - if (this->comm().size() == 1) - // This can help with some branching decisions and is also consistent with what PETSc does... a - // single rank Vec is going to be a sequential vector - this->_type = SERIAL; - else if (ptype == AUTOMATIC) + if (ptype == AUTOMATIC) { if (n == n_local) this->_type = SERIAL; @@ -745,17 +741,19 @@ void PetscVector::init (const numeric_index_type n, else this->_type = ptype; + // We should have been given consistent settings libmesh_assert ((this->_type==SERIAL && n==n_local) || + (this->n_processors()==1 && n==n_local) || this->_type==PARALLEL); - // create a sequential vector if on only 1 processor - if (this->_type == SERIAL) + // create a sequential vector if on only 1 processor, or if asked + if (this->_type == SERIAL || (this->n_processors() == 1)) { LibmeshPetscCallA(PETSC_COMM_SELF, VecCreate(PETSC_COMM_SELF, &_vec)); LibmeshPetscCallA(PETSC_COMM_SELF, VecSetSizes(_vec, petsc_n, petsc_n)); LibmeshPetscCallA(PETSC_COMM_SELF, VecSetFromOptions (_vec)); } - // otherwise create an MPI-enabled vector + // or create an MPI-enabled PARALLEL vector w/o ghosting if asked else if (this->_type == PARALLEL) { #ifdef LIBMESH_HAVE_MPI @@ -771,8 +769,11 @@ void PetscVector::init (const numeric_index_type n, #endif LibmeshPetscCall(VecSetFromOptions(_vec)); } + // or yell because we don't know what to do else - libmesh_error_msg("Unsupported type " << Utility::enum_to_string(this->_type)); + libmesh_error_msg("Unsupported type " << + Utility::enum_to_string(this->_type) << + " for parallel init with no ghost indices supplied"); this->_is_initialized = true; this->_is_closed = true; @@ -801,14 +802,14 @@ void PetscVector::init (const numeric_index_type n, const numeric_index_type n_local, const std::vector & ghost, const bool fast, - const ParallelType libmesh_dbg_var(ptype)) + const ParallelType ptype) { parallel_object_only(); if (this->comm().size() == 1) { libmesh_assert(ghost.empty()); - this->init(n, n_local, fast, SERIAL); + this->init(n, n_local, fast, ptype); return; } diff --git a/src/numerics/petsc_vector.C b/src/numerics/petsc_vector.C index 5bf53b7e1b..550e21489b 100644 --- a/src/numerics/petsc_vector.C +++ b/src/numerics/petsc_vector.C @@ -533,9 +533,9 @@ PetscVector::operator = (const PetscVector & v) libmesh_assert (v.closed()); AssignmentType assign_type = Error; - if (this->type() == SERIAL && v.type() != SERIAL) + if (this->is_effectively_serial() && !v.is_effectively_serial()) assign_type = ParallelToSerial; - else if (this->type() != SERIAL && v.type() == SERIAL) + else if (!this->is_effectively_serial() && v.is_effectively_serial()) assign_type = SerialToParallel; else if (this->local_size() == v.local_size()) assign_type = SameToSame; @@ -1056,7 +1056,7 @@ void PetscVector::create_subvector(NumericVector & subvector, parallel_object_only(); libmesh_error_msg_if( - subvector.type() == GHOSTED, + subvector.type() == GHOSTED && !subvector.is_effectively_serial(), "We do not support scattering parallel information to ghosts for subvectors"); this->_restore_array(); @@ -1071,11 +1071,11 @@ void PetscVector::create_subvector(NumericVector & subvector, // If not, we use the appropriate PETSc routines to initialize it. if (!petsc_subvector->initialized()) { - if (this->type() == SERIAL) + if (this->is_effectively_serial()) { libmesh_assert(this->comm().verify(rows.size())); LibmeshPetscCall(VecCreateSeq(this->comm().get(), rows.size(), &(petsc_subvector->_vec))); - petsc_subvector->_type = SERIAL; + petsc_subvector->_type = this->type(); } else { From 771fc4acda36faddc788c16e74d8acda7d65261a Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Thu, 26 Mar 2026 12:42:29 -0500 Subject: [PATCH 3/3] We only create PARALLEL subvectors, never GHOSTED And even in the serial case our metadata should reflect that. This fixes a conflict with MOOSE restarts (which want to save a subvector in one cases but can't save a ghosted vector) for me. --- src/numerics/petsc_vector.C | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/numerics/petsc_vector.C b/src/numerics/petsc_vector.C index 550e21489b..63f459186d 100644 --- a/src/numerics/petsc_vector.C +++ b/src/numerics/petsc_vector.C @@ -1075,7 +1075,12 @@ void PetscVector::create_subvector(NumericVector & subvector, { libmesh_assert(this->comm().verify(rows.size())); LibmeshPetscCall(VecCreateSeq(this->comm().get(), rows.size(), &(petsc_subvector->_vec))); - petsc_subvector->_type = this->type(); + + // You'd think we would create GHOSTED from GHOSTED, but we + // never have, and now there'd be downstream compatibility + // issues if we started even marking an effectively-serial + // subvector as GHOSTED. + petsc_subvector->_type = (this->type() == SERIAL) ? SERIAL : PARALLEL; } else {