diff --git a/.gitignore b/.gitignore
index ff588ebb..87b27a2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,12 @@ compile_commands.json
# OS-specific: Mac
*.dSYM
.DS_Store
+
+# Typical build directory
+/build/
+
+# Visual Studio Code
+/.vscode/
+
+# CMake user configuration files
+user.cmake
diff --git a/CHANGELOG b/CHANGELOG
index 15c5e145..8a6a66c1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,13 @@
https://glvis.org
+Version 4.5 (development)
+=========================
+
+- Implemented DOF numbering, pressing the `n` or `N` key in 2D mode cycles through:
+ None → Elements → Edges → Vertices → DOFs. Parallel numbering is now by default
+ 'local' to each rank; 'global' vs. 'local' numbering can be toggled with 'Alt+n'.
+
Version 4.4 released on May 1, 2025
===================================
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f6c903c3..7e0df6ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@
# introduced.
cmake_minimum_required(VERSION 3.10)
+
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -24,6 +25,16 @@ endif ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
project(glvis NONE)
+# Load user settings
+set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/user.cmake" CACHE PATH
+ "User configuration file. This file is not part of the GLVis source code and
+ is not distributed with GLVis. It can be used to set user-specific options,
+ such as paths to external libraries, compiler flags, etc.")
+include("${USER_CONFIG}" OPTIONAL RESULT_VARIABLE USER_CONFIG_LOADED)
+if (USER_CONFIG_LOADED)
+ message(STATUS "USER_CONFIG = ${USER_CONFIG} (LOADED)")
+endif()
+
# Import MFEM. The following variables can be used to help CMake find MFEM:
# * MFEM_DIR - absolute path to the MFEM build or install prefix.
# * mfem_DIR - absolute path to where MFEMConfig.cmake is.
diff --git a/README.md b/README.md
index ee4c3dee..bef56eeb 100644
--- a/README.md
+++ b/README.md
@@ -177,9 +177,10 @@ Key commands
- Ctrl + o – Toggle an element ordering curve
- n / N – Cycle through numberings. The options are:
- none
- - show element numbering
- - show edge numbering
- - show vertex numbering
+ - show elements numbering
+ - show edges numbering
+ - show vertices numbering
+ - show DOFs numbering
- ` – Toggle a ruler, with initial origin at the center of the bounding box. The origin can be later moved with ~. The options are:
- none
- coordinate axes lines
diff --git a/glvis.cpp b/glvis.cpp
index 8c5721d3..7395dc90 100644
--- a/glvis.cpp
+++ b/glvis.cpp
@@ -19,6 +19,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
// SDL may redefine main() as SDL_main() ostensibly to ease portability.
// (WinMain() instead of main() is used as the entry point in a non-console
@@ -31,8 +35,7 @@
#define SDL_MAIN_HANDLED
#endif
-#include "mfem.hpp"
-#include "lib/palettes.hpp"
+#include
#include "lib/visual.hpp"
#include "lib/stream_reader.hpp"
#include "lib/file_reader.hpp"
@@ -171,6 +174,7 @@ bool GLVisInitVis(StreamCollection input_streams)
// the 'jet-like' palette is used in 2D, see vssolution.cpp).
vs->palette.SetIndex(4);
}
+ vs->SetDataOffsets(stream_state.offsets.get());
}
else if (stream_state.mesh->SpaceDimension() == 3)
{
@@ -224,6 +228,7 @@ bool GLVisInitVis(StreamCollection input_streams)
vs = new VisualizationSceneVector(*stream_state.mesh, stream_state.solu,
stream_state.solv, stream_state.mesh_quad.get());
}
+ vs->SetDataOffsets(stream_state.offsets.get());
}
else if (stream_state.mesh->SpaceDimension() == 3)
{
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index a3e65200..13b3db7c 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -23,6 +23,7 @@ list(APPEND SOURCES
material.cpp
openglvis.cpp
palettes.cpp
+ palettes_default.cpp
palettes_base.cpp
sdl.cpp
sdl_helper.cpp
diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp
index d7e4b282..7c70be5c 100644
--- a/lib/aux_vis.cpp
+++ b/lib/aux_vis.cpp
@@ -11,16 +11,17 @@
#include
#include
-#include
#include
#include
#include
-#include "mfem.hpp"
-#include "sdl.hpp"
-#include "palettes.hpp"
-#include "visual.hpp"
+#include "gl/types.hpp"
#include "gl2ps.h"
+#include "palettes.hpp"
+#include "sdl.hpp"
+#include "threads.hpp"
+
+#include
#if defined(GLVIS_USE_LIBTIFF)
#include "tiffio.h"
@@ -33,11 +34,9 @@
#include
#endif
-using namespace mfem;
-
thread_local int visualize = 0;
thread_local VisualizationScene * locscene;
-thread_local GLVisCommand *glvis_command = NULL;
+thread_local GLVisCommand *glvis_command = nullptr;
#ifdef GLVIS_MULTISAMPLE
static int glvis_multisample = GLVIS_MULTISAMPLE;
@@ -435,7 +434,7 @@ void MyExpose()
}
-thread_local Array IdleFuncs;
+thread_local mfem::Array IdleFuncs;
thread_local int LastIdleFunc;
thread_local bool use_idle = false;
diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp
index 9929639b..989143a0 100644
--- a/lib/aux_vis.hpp
+++ b/lib/aux_vis.hpp
@@ -12,7 +12,6 @@
#ifndef GLVIS_AUX_VIS_HPP
#define GLVIS_AUX_VIS_HPP
-#include "gl/platform_gl.hpp"
#include "gl/types.hpp"
#include "openglvis.hpp"
@@ -184,4 +183,4 @@ function validator = [](T) { return true; })
return input;
}
-#endif
+#endif // GLVIS_AUX_VIS_HPP
diff --git a/lib/coll_reader.cpp b/lib/coll_reader.cpp
index a50fafe3..9782c4de 100644
--- a/lib/coll_reader.cpp
+++ b/lib/coll_reader.cpp
@@ -11,8 +11,6 @@
#include "coll_reader.hpp"
-#include
-
using namespace std;
using namespace mfem;
diff --git a/lib/coll_reader.hpp b/lib/coll_reader.hpp
index a0c6f86c..bba8b9fa 100644
--- a/lib/coll_reader.hpp
+++ b/lib/coll_reader.hpp
@@ -12,8 +12,6 @@
#ifndef GLVIS_COLL_READER_HPP
#define GLVIS_COLL_READER_HPP
-#include
-#include "mfem.hpp"
#include "data_state.hpp"
class DataCollectionReader
diff --git a/lib/data_state.cpp b/lib/data_state.cpp
index 287cb68c..3d65665e 100644
--- a/lib/data_state.cpp
+++ b/lib/data_state.cpp
@@ -9,11 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
-#include "data_state.hpp"
-#include "visual.hpp"
-
#include
+#include "data_state.hpp"
+#include "vsvector.hpp"
+#include "vsvector3d.hpp"
+
using namespace std;
using namespace mfem;
@@ -57,13 +58,59 @@ DataState &DataState::operator=(DataState &&ss)
return *this;
}
-void DataState::SetMesh(mfem::Mesh *mesh_)
+void DataState::SetMesh(Mesh *mesh_)
{
internal.mesh.reset(mesh_);
SetMesh(std::move(internal.mesh));
}
-void DataState::SetMesh(std::unique_ptr &&pmesh)
+void DataState::ComputeDofsOffsets(std::vector &gf_array)
+{
+ const int nprocs = static_cast(gf_array.size());
+ MFEM_VERIFY(!gf_array.empty(), "No grid functions provided for offsets");
+
+ internal.offsets.reset(new DataState::Offsets(nprocs));
+
+ DenseMatrix pointmat;
+ Array dofs, vertices;
+ for (int i = 0, g_e = 0; i < nprocs; i++)
+ {
+ const GridFunction *gf = gf_array[i];
+ const FiniteElementSpace *l_fes = gf->FESpace();
+ Mesh *l_mesh = l_fes->GetMesh();
+ // store the dofs numbers as they are fespace dependent
+ auto &offset = offsets->operator[](i);
+ for (int l_e = 0; l_e < l_mesh->GetNE(); l_e++, g_e++)
+ {
+#ifdef GLVIS_DEBUG
+ // Store elements centers
+ l_mesh->GetPointMatrix(l_e, pointmat);
+ const int nv = pointmat.Width();
+ double xs = 0.0, ys = 0.0;
+ for (int j = 0; j < nv; j++)
+ {
+ xs += pointmat(0,j), ys += pointmat(1,j);
+ }
+ xs /= nv, ys /= nv;
+ offset.exy_map[ {l_e, i} ] = {xs, ys};
+#endif // end GLVIS_DEBUG
+ l_fes->GetElementDofs(l_e, dofs);
+ l_fes->AdjustVDofs(dofs);
+ for (int k = 0; k < dofs.Size(); k++)
+ {
+ offset[ {g_e, k} ] = dofs[k];
+ }
+ }
+ if (i + 1 == nprocs) { continue; }
+ auto &next = offsets->operator[](i+1);
+ // for NE, NV and NEdges, we accumulate the values
+ next.nelems = offset.nelems + l_mesh->GetNE();
+ next.nedges = offset.nedges + l_mesh->GetNEdges();
+ next.nverts = offset.nverts + l_mesh->GetNV();
+ }
+}
+
+void DataState::SetMesh(std::unique_ptr &&pmesh)
{
internal.mesh = std::move(pmesh);
internal.mesh_quad.reset();
@@ -71,14 +118,14 @@ void DataState::SetMesh(std::unique_ptr &&pmesh)
if (quad_f && quad_f->GetSpace()->GetMesh() != mesh.get()) { SetQuadFunction(NULL); }
}
-void DataState::SetGridFunction(mfem::GridFunction *gf, int component)
+void DataState::SetGridFunction(GridFunction *gf, int component)
{
internal.grid_f.reset(gf);
SetGridFunction(std::move(internal.grid_f), component);
}
-void DataState::SetGridFunction(
- std::unique_ptr &&pgf, int component)
+void DataState::SetGridFunction(std::unique_ptr &&pgf,
+ int component)
{
internal.grid_f = std::move(pgf);
internal.quad_f.reset();
@@ -86,7 +133,7 @@ void DataState::SetGridFunction(
SetGridFunctionSolution(component);
}
-void DataState::SetQuadFunction(mfem::QuadratureFunction *qf, int component)
+void DataState::SetQuadFunction(QuadratureFunction *qf, int component)
{
if (quad_f.get() != qf)
{
@@ -97,8 +144,8 @@ void DataState::SetQuadFunction(mfem::QuadratureFunction *qf, int component)
SetQuadFunctionSolution(component);
}
-void DataState::SetQuadFunction(
- std::unique_ptr &&pqf, int component)
+void DataState::SetQuadFunction(std::unique_ptr &&pqf,
+ int component)
{
if (quad_f.get() != pqf.get())
{
@@ -109,8 +156,8 @@ void DataState::SetQuadFunction(
SetQuadFunctionSolution(component);
}
-void DataState::SetQuadFunction(
- const std::vector &qf_array, int component)
+void DataState::SetQuadFunction(const std::vector
+ &qf_array, int component)
{
// assume the same vdim
const int vdim = qf_array[0]->GetVDim();
@@ -132,7 +179,7 @@ void DataState::SetQuadFunction(
SetQuadFunction(qf, component);
}
-void DataState::SetDataCollectionField(mfem::DataCollection *dc, int ti,
+void DataState::SetDataCollectionField(DataCollection *dc, int ti,
const char *field, bool quad, int component)
{
internal.data_coll.reset(dc);
@@ -589,17 +636,18 @@ void DataState::ResetMeshAndSolution(DataState &ss, VisualizationScene* vs)
{
if (ss.grid_f->VectorDim() == 1)
{
- VisualizationSceneSolution *vss =
- dynamic_cast(vs);
+ auto *vss = dynamic_cast(vs);
// use the local vector as pointer is invalid after the move
ss.grid_f->GetNodalValues(sol);
+ // update the offsets before the mesh and solution
+ vss->SetDataOffsets(ss.offsets.get());
vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &sol,
ss.grid_f.get());
}
else
{
- VisualizationSceneVector *vsv =
- dynamic_cast(vs);
+ auto *vsv = dynamic_cast(vs);
+ vsv->SetDataOffsets(ss.offsets.get());
vsv->NewMeshAndSolution(*ss.grid_f, ss.mesh_quad.get());
}
}
@@ -607,8 +655,7 @@ void DataState::ResetMeshAndSolution(DataState &ss, VisualizationScene* vs)
{
if (ss.grid_f->VectorDim() == 1)
{
- VisualizationSceneSolution3d *vss =
- dynamic_cast(vs);
+ auto *vss = dynamic_cast(vs);
// use the local vector as pointer is invalid after the move
ss.grid_f->GetNodalValues(sol);
vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), &sol,
@@ -618,8 +665,7 @@ void DataState::ResetMeshAndSolution(DataState &ss, VisualizationScene* vs)
{
ss.ProjectVectorFEGridFunction();
- VisualizationSceneVector3d *vss =
- dynamic_cast(vs);
+ auto *vss = dynamic_cast(vs);
vss->NewMeshAndSolution(ss.mesh.get(), ss.mesh_quad.get(), ss.grid_f.get());
}
}
diff --git a/lib/data_state.hpp b/lib/data_state.hpp
index 8b61b1e8..c1e7d5f0 100644
--- a/lib/data_state.hpp
+++ b/lib/data_state.hpp
@@ -12,12 +12,17 @@
#ifndef GLVIS_DATA_STATE_HPP
#define GLVIS_DATA_STATE_HPP
+#include