From 93a1d3b2eb598ccfe21424e118befa43639e7122 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 29 Aug 2024 11:52:01 -0400 Subject: [PATCH 01/26] Convert to MDS mesh during adapt for capVol - Add -m option to convert mesh database to MDS during adaptation. This option speeds up adaptation. - This tests the conversion capability. Signed-off-by: Aiden Woodruff --- test/capVol.cc | 80 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/test/capVol.cc b/test/capVol.cc index 7e44c2e36..0198e8f49 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -11,6 +11,8 @@ // Mesh interfaces #include #include +#include +#include // Geometry interfaces #include @@ -58,12 +60,13 @@ void writeCre(CapstoneModule& cs, const std::string& filename) { } void printUsage(char *argv0) { - printf("USAGE: %s [-agwv] \n", argv0); + printf("USAGE: %s [-agwvm] \n", argv0); printf("Flags:\n" "-a\tEvaluate size-field analytically.\n" "-g\tForce mesh generation.\n" "-v\tEnable verbose output.\n" "-w\tWrite before.vtk, after.vtk, and after.cre.\n" + "-m\tConvert mesh to MDS during adaptation.\n" "SIZE-FIELDS:\n" "%d, for uniform anisotropic size-field\n" "%d, for wing-shock size-field\n" @@ -80,6 +83,7 @@ int main(int argc, char** argv) { pcu::PCU PCUObj; // Initialize logging. + lion_set_verbosity(1); lion_set_stdout(stdout); lion_set_stderr(stderr); @@ -93,7 +97,7 @@ int main(int argc, char** argv) { // Parse arguments. bool volume_flag = false, write_flag = false, analytic_flag = false, - verbose_flag = false; + verbose_flag = false, mds_flag = false; for (int i = 1; i < argc - 2; ++i) { if (*argv[i] == '-') { for (int j = 1; argv[i][j] != '\0'; ++j) { @@ -111,6 +115,9 @@ int main(int argc, char** argv) { case 'w': write_flag = true; break; + case 'm': + mds_flag = true; + break; default: printf("Error: invalid flag.\n"); printUsage(argv[0]); @@ -147,8 +154,9 @@ int main(int argc, char** argv) { filenames.push_back(createFileName); M_GModel gmodel = cs.load_files(filenames); + M_MModel mmodel; if (volume_flag) { - M_MModel mmodel = cs.generate_mesh(); + mmodel = cs.generate_mesh(); if (mmodel.is_invalid()) { lion_eprint(1, "FATAL: Failed to mesh the model.\n"); myExit(EXIT_FAILURE); @@ -159,7 +167,8 @@ int main(int argc, char** argv) { std::vector mmodels; MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); PCU_ALWAYS_ASSERT(mmodels.size() == 1); - MG_API_CALL(m, set_current_model(mmodels[0])); + mmodel = mmodels[0]; + MG_API_CALL(m, set_current_model(mmodel)); } if (write_flag) { @@ -174,21 +183,35 @@ int main(int argc, char** argv) { // Make APF adapter over Capstone mesh. ma::Mesh* apfCapMesh = apf::createMesh(m, g, &PCUObj); + apf::writeVtkFiles("core_capVol_cap.vtk", apfCapMesh); + + ma::Mesh* adaptMesh = apfCapMesh; + if (mds_flag) { + adaptMesh = apf::createMdsMesh(apfCapMesh->getModel(), apfCapMesh); + adaptMesh->verify(); + if (write_flag) { + apf::writeVtkFiles("core_capVol_mds.vtk", adaptMesh); + } + apfCapMesh->setModel(nullptr); // Disown the model. + delete apfCapMesh; + apfCapMesh = nullptr; + MG_API_CALL(m, delete_model(mmodel)); + } // Choose appropriate size-field. ma::AnisotropicFunction* sf = nullptr; switch (mode) { case 1: - sf = new UniformAniso(apfCapMesh); + sf = new UniformAniso(adaptMesh); break; case 2: - sf = new WingShock(apfCapMesh, 50); + sf = new WingShock(adaptMesh, 50); break; case 3: - sf = new Shock(apfCapMesh); + sf = new Shock(adaptMesh); break; case 4: - sf = new CylBoundaryLayer(apfCapMesh); + sf = new CylBoundaryLayer(adaptMesh); break; default: lion_eprint(1, "FATAL: Invalid size-field.\n"); @@ -200,38 +223,38 @@ int main(int argc, char** argv) { apf::Field* scaleField = nullptr; ma::Input *in = nullptr; if (!analytic_flag || write_flag) { - frameField = apf::createFieldOn(apfCapMesh, "adapt_frames", apf::MATRIX); - scaleField = apf::createFieldOn(apfCapMesh, "adapt_scales", apf::VECTOR); + frameField = apf::createFieldOn(adaptMesh, "adapt_frames", apf::MATRIX); + scaleField = apf::createFieldOn(adaptMesh, "adapt_scales", apf::VECTOR); ma::Entity *v; - ma::Iterator* it = apfCapMesh->begin(0); - while( (v = apfCapMesh->iterate(it)) ) { + ma::Iterator* it = adaptMesh->begin(0); + while( (v = adaptMesh->iterate(it)) ) { ma::Vector s; ma::Matrix f; sf->getValue(v, f, s); apf::setVector(scaleField, v, 0, s); apf::setMatrix(frameField, v, 0, f); } - apfCapMesh->end(it); + adaptMesh->end(it); if (write_flag) { - apf::writeVtkFiles("core_capVol_before", apfCapMesh); + apf::writeVtkFiles("core_capVol_before.vtk", adaptMesh); } } if (!analytic_flag) { // Pass the field data. - in = ma::makeAdvanced(ma::configure(apfCapMesh, scaleField, frameField)); + in = ma::makeAdvanced(ma::configure(adaptMesh, scaleField, frameField)); } else { // Pass the function. - in = ma::makeAdvanced(ma::configure(apfCapMesh, sf)); + in = ma::makeAdvanced(ma::configure(adaptMesh, sf)); } in->shouldSnap = true; in->shouldTransferParametric = true; in->shouldFixShape = true; in->shouldForceAdaptation = true; - if (apfCapMesh->getDimension() == 2) + if (adaptMesh->getDimension() == 2) in->goodQuality = 0.04; // this is mean-ratio squared else // 3D meshes in->goodQuality = 0.027; // this is the mean-ratio cubed @@ -246,11 +269,24 @@ int main(int argc, char** argv) { if (volume_flag) { // We can't verify surface meshes. - apfCapMesh->verify(); + adaptMesh->verify(); } if (write_flag) { - apf::writeVtkFiles("core_capVol_after", apfCapMesh); + if (mds_flag) { + apf::writeVtkFiles("core_capVol_after_mds.vtk", adaptMesh); + MG_API_CALL(m, create_associated_model(mmodel, gmodel, "MDS Adapt")); + MG_API_CALL(m, set_adjacency_state(REGION2FACE|REGION2EDGE|REGION2VERTEX| + FACE2EDGE|FACE2VERTEX)); + MG_API_CALL(m, set_reverse_states()); + // MG_API_CALL(m, compute_adjacency()); + ma::Mesh* newCapMesh = apf::createMesh(m, g); + apf::convert(adaptMesh, newCapMesh); + apf::writeVtkFiles("core_capVol_after_cap.vtk", newCapMesh); + apf::destroyMesh(newCapMesh); + } else { + apf::writeVtkFiles("core_capVol_after_cap.vtk", adaptMesh); + } writeCre(cs, "core_capVol_after.cre"); } @@ -264,13 +300,11 @@ int main(int argc, char** argv) { } // Clean up. - if (frameField) apf::destroyField(frameField); - if (scaleField) apf::destroyField(scaleField); - apf::destroyMesh(apfCapMesh); + apf::destroyMesh(adaptMesh); delete sf; // Exit calls. gmi_cap_stop(); } pcu::Finalize(); -} \ No newline at end of file +} From 49262e889d0bc89d5712df80ee933b698f03ea63 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 12 Sep 2024 14:59:51 -0400 Subject: [PATCH 02/26] Clean up capVol - Replace writeCre with cs.save_file. - Replace ma::adaptVerbose with ma::adapt (the production version). - Remove ma::Input arguments which are just reinforcing the defaults. - If the defaults chagne we shouldn't force using the old ones. - Don't delete old mesh model before adapting. - Change CapstoneModule name. - Reorder mds mesh after conversion. Signed-off-by: Aiden Woodruff --- test/capVol.cc | 53 ++++++++------------------------------------------ 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/test/capVol.cc b/test/capVol.cc index 0198e8f49..bd9302113 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -36,29 +36,6 @@ void myExit(int exit_code = EXIT_SUCCESS) { exit(exit_code); } -void writeCre(CapstoneModule& cs, const std::string& filename) { - GeometryDatabaseInterface *gdbi = cs.get_geometry(); - MeshDatabaseInterface *mdbi = cs.get_mesh(); - AppContext *ctx = cs.get_context(); - - // Get the CRE writer. - Writer *creWriter = get_writer(ctx, "Create Native Writer"); - if (!creWriter) { - lion_eprint(1, "FATAL: Could not find the CRE writer.\n"); - myExit(EXIT_FAILURE); - } - - IdMapping idmapping; - std::vector mmodels; - M_GModel gmodel; - M_MModel mmodel; - gdbi->get_current_model(gmodel); - mdbi->get_current_model(mmodel); - mmodels.clear(); - mmodels.push_back(mmodel); - creWriter->write(ctx, gmodel, mmodels, filename.c_str(), idmapping); -} - void printUsage(char *argv0) { printf("USAGE: %s [-agwvm] \n", argv0); printf("Flags:\n" @@ -139,7 +116,7 @@ int main(int argc, char** argv) { const std::string mdbName("Mesh Database : Create"); const std::string adbName("Attribution Database : Create"); - CapstoneModule cs("capTest", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); + CapstoneModule cs("capVol", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); GeometryDatabaseInterface *g = cs.get_geometry(); MeshDatabaseInterface *m = cs.get_mesh(); @@ -172,7 +149,7 @@ int main(int argc, char** argv) { } if (write_flag) { - writeCre(cs, "core_capVol_before.cre"); + cs.save_file("core_capVol_before.cre", gmodel); } // Calculate adjacencies. @@ -189,13 +166,14 @@ int main(int argc, char** argv) { if (mds_flag) { adaptMesh = apf::createMdsMesh(apfCapMesh->getModel(), apfCapMesh); adaptMesh->verify(); + apf::reorderMdsMesh(adaptMesh); + adaptMesh->verify(); if (write_flag) { apf::writeVtkFiles("core_capVol_mds.vtk", adaptMesh); } apfCapMesh->setModel(nullptr); // Disown the model. delete apfCapMesh; apfCapMesh = nullptr; - MG_API_CALL(m, delete_model(mmodel)); } // Choose appropriate size-field. @@ -250,22 +228,7 @@ int main(int argc, char** argv) { in = ma::makeAdvanced(ma::configure(adaptMesh, sf)); } - in->shouldSnap = true; - in->shouldTransferParametric = true; - in->shouldFixShape = true; - in->shouldForceAdaptation = true; - if (adaptMesh->getDimension() == 2) - in->goodQuality = 0.04; // this is mean-ratio squared - else // 3D meshes - in->goodQuality = 0.027; // this is the mean-ratio cubed - in->maximumIterations = 10; - - if (verbose_flag) { - // Adapt with verbose logging but without intermediate VTKs. - ma::adaptVerbose(in, false); - } else { - ma::adapt(in); - } + ma::adapt(in); if (volume_flag) { // We can't verify surface meshes. @@ -275,11 +238,11 @@ int main(int argc, char** argv) { if (write_flag) { if (mds_flag) { apf::writeVtkFiles("core_capVol_after_mds.vtk", adaptMesh); - MG_API_CALL(m, create_associated_model(mmodel, gmodel, "MDS Adapt")); + MG_API_CALL(m, create_associated_model(mmodel, gmodel, "MeshAdapt")); MG_API_CALL(m, set_adjacency_state(REGION2FACE|REGION2EDGE|REGION2VERTEX| FACE2EDGE|FACE2VERTEX)); MG_API_CALL(m, set_reverse_states()); - // MG_API_CALL(m, compute_adjacency()); + // MG_API_CALL(m, compute_adjacency()); // unnecessary because no elements? ma::Mesh* newCapMesh = apf::createMesh(m, g); apf::convert(adaptMesh, newCapMesh); apf::writeVtkFiles("core_capVol_after_cap.vtk", newCapMesh); @@ -287,7 +250,7 @@ int main(int argc, char** argv) { } else { apf::writeVtkFiles("core_capVol_after_cap.vtk", adaptMesh); } - writeCre(cs, "core_capVol_after.cre"); + cs.save_file("core_capVol_after.cre", gmodel); } /* PRINT ADAPTED MESH INFO */ From 2fbdf38793934d4f668143032d902ae97fc1f292 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 12 Sep 2024 17:39:52 -0400 Subject: [PATCH 03/26] Add mds adapts to capVol test matrix - Disable verify() for non-volume meshes. Signed-off-by: Aiden Woodruff --- test/capVol.cc | 4 ++-- test/testing.cmake | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test/capVol.cc b/test/capVol.cc index bd9302113..e7717f7e5 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -165,9 +165,9 @@ int main(int argc, char** argv) { ma::Mesh* adaptMesh = apfCapMesh; if (mds_flag) { adaptMesh = apf::createMdsMesh(apfCapMesh->getModel(), apfCapMesh); - adaptMesh->verify(); apf::reorderMdsMesh(adaptMesh); - adaptMesh->verify(); + // APF default routine will typically fail to verify surface meshes. + if (volume_flag) adaptMesh->verify(); if (write_flag) { apf::writeVtkFiles("core_capVol_mds.vtk", adaptMesh); } diff --git a/test/testing.cmake b/test/testing.cmake index 9f93a1d66..86eab8143 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -987,6 +987,14 @@ if(ENABLE_CAPSTONE) mpi_test(capVolWing 1 ./capVol -vg 2 ${MESHES}/cap/wing_surf_only.cre) mpi_test(capVolCube 1 ./capVol -vg 3 ${MESHES}/cap/cube_surf_only.cre) mpi_test(capVolCyl2 1 ./capVol -vg 4 ${MESHES}/cap/cyl_surf_only.cre) + mpi_test(capCylMds 1 ./capVol -vm 1 ${MESHES}/cap/cyl_surf_only.cre) + mpi_test(capWingMds 1 ./capVol -vm 2 ${MESHES}/cap/wing_surf_only.cre) + mpi_test(capCubeMds 1 ./capVol -vm 3 ${MESHES}/cap/cube_surf_only.cre) + mpi_test(capCyl2Mds 1 ./capVol -vm 4 ${MESHES}/cap/cyl_surf_only.cre) + mpi_test(capVolCylMds 1 ./capVol -vgm 1 ${MESHES}/cap/cyl_surf_only.cre) + mpi_test(capVolWingMds 1 ./capVol -vgm 2 ${MESHES}/cap/wing_surf_only.cre) + mpi_test(capVolCubeMds 1 ./capVol -vgm 3 ${MESHES}/cap/cube_surf_only.cre) + mpi_test(capVolCyl2Mds 1 ./capVol -vgm 4 ${MESHES}/cap/cyl_surf_only.cre) mpi_test(cap_inClosureOf 1 ./cap_inClosureOf ${MESHES}/cap/cube_surf_only.cre) mpi_test(cap_closestPoint 1 ./cap_closestPoint ${MESHES}/cap/cube_surf_only.cre) if(HAVE_CAPSTONE_SIZINGMETRICTOOL) From 7ddcb6593a5dd0be70408d771d7bdb8b41846143 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 12 Jun 2025 18:51:07 -0400 Subject: [PATCH 04/26] use new PCU object Signed-off-by: Aiden Woodruff --- test/capVol.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/capVol.cc b/test/capVol.cc index e7717f7e5..16017f501 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -243,7 +243,7 @@ int main(int argc, char** argv) { FACE2EDGE|FACE2VERTEX)); MG_API_CALL(m, set_reverse_states()); // MG_API_CALL(m, compute_adjacency()); // unnecessary because no elements? - ma::Mesh* newCapMesh = apf::createMesh(m, g); + ma::Mesh* newCapMesh = apf::createMesh(m, g, &PCUObj); apf::convert(adaptMesh, newCapMesh); apf::writeVtkFiles("core_capVol_after_cap.vtk", newCapMesh); apf::destroyMesh(newCapMesh); From e7c796fc523219d4ceb04b7a0f188e6f185ce396 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Tue, 17 Jun 2025 00:45:19 -0400 Subject: [PATCH 05/26] add gmi_cap_probe and gmi_cap_load_some - gmi_cap.h: add gmi_cap_probe and gmi_cap_load_some functions. - wrap headers and functions requiring C++ into #ifdef __cplusplus. - remove using namespace declarations. - add import with gmodel. - gmi_cap.cc: add using namespace CreateMG. - add Geometry namespace to enums (i.e. VERTEX, EDGE, FACE, REGION). - add gmodel to struct cap_model. - use reinterpret_cast instead of C-style casts. - (gmi_cap_start): handle initialization of static capstonemodule. - (gmi_cap_stop): destruct static capstone module. - (create_cre): uncomment function. - (gmi_cap_probe): add short overload to just get mesh names. - add long overload to get model and mesh contents. - (gmi_register_cap): register create_cre for gmi_load. - (owned_import): uncomment function. - (gmi_cap_load): call gmi_cap_load_some with all mesh names. - (gmi_cap_load_some): add new function to only load specific mesh names. - (gmi_import_cap): add overload to use current geometry. - replace PCU_ALWAYS_ASSERT with if statement and gmi_fail. - test/capLoadSome.cc: add utility to test gmi_cap_load_some. - test/capProbe.cc: add utility to test gmi_cap_probe. - test/CMakeLists.txt: add capLoadSome and capProbe. Signed-off-by: Aiden Woodruff --- gmi_cap/gmi_cap.cc | 251 ++++++++++++++++++++++++++++++-------------- gmi_cap/gmi_cap.h | 45 +++++--- test/CMakeLists.txt | 2 + test/capLoadSome.cc | 29 +++++ test/capProbe.cc | 31 ++++++ 5 files changed, 265 insertions(+), 93 deletions(-) create mode 100644 test/capLoadSome.cc create mode 100644 test/capProbe.cc diff --git a/gmi_cap/gmi_cap.cc b/gmi_cap/gmi_cap.cc index 64b23e26a..06873304e 100644 --- a/gmi_cap/gmi_cap.cc +++ b/gmi_cap/gmi_cap.cc @@ -8,12 +8,43 @@ *******************************************************************************/ #include "gmi_cap.h" + #include -#include +#include #include + #include +#include #include +#include +#include +#include +#include + +using namespace CreateMG; + +static std::unique_ptr cs_module; + +void gmi_cap_start(void) { + if (!cs_module) { + cs_module.reset(new CapstoneModule( + "SCOREC/gmi_cap", + "Geometry Database : SMLIB", + "Mesh Database : Create", + "Attribution Database : Create" + )); + PCU_ALWAYS_ASSERT(cs_module->get_context()); + PCU_ALWAYS_ASSERT(cs_module->get_geometry()); + PCU_ALWAYS_ASSERT(cs_module->get_mesh()); + } +} + +void gmi_cap_stop(void) { + if (!cs_module) + gmi_fail("gmi_cap_stop called before gmi_cap_start"); + cs_module.reset(); +} gmi_ent* toGmiEntity(M_GTopo topo) { @@ -30,10 +61,10 @@ M_GTopo fromGmiEntity(gmi_ent* g) return topo; } - struct cap_model { struct gmi_model model; - GeometryDatabaseInterface* geomInterface; + GDBI* geomInterface; + M_GModel gmodel; bool owned; }; @@ -42,17 +73,17 @@ static gmi_iter* begin(gmi_model* m, int dim) cap_model* cm = (cap_model*)m; M_GBRep brep; cm->geomInterface->get_brep_by_index(0, brep); - GeometrySmartIterator* giter = new GeometrySmartIterator(cm->geomInterface); + auto giter = new Geometry::GeometrySmartIterator(cm->geomInterface); if (dim == 0) - cm->geomInterface->get_topo_iterator(brep, VERTEX, *giter); + cm->geomInterface->get_topo_iterator(brep, Geometry::VERTEX, *giter); if (dim == 1) - cm->geomInterface->get_topo_iterator(brep, EDGE, *giter); + cm->geomInterface->get_topo_iterator(brep, Geometry::EDGE, *giter); if (dim == 2) - cm->geomInterface->get_topo_iterator(brep, FACE, *giter); + cm->geomInterface->get_topo_iterator(brep, Geometry::FACE, *giter); if (dim == 3) - cm->geomInterface->get_topo_iterator(brep, REGION, *giter); + cm->geomInterface->get_topo_iterator(brep, Geometry::REGION, *giter); cm->geomInterface->iterator_begin(*giter); - return (gmi_iter*)(giter); + return reinterpret_cast(giter); } /* NOTE: giter is located at the first item in the list, therefore @@ -61,7 +92,7 @@ static gmi_iter* begin(gmi_model* m, int dim) static gmi_ent* next(gmi_model*m, gmi_iter* i) { cap_model* cm = (cap_model*)m; - GeometrySmartIterator* giter = (GeometrySmartIterator*)i; + auto giter = reinterpret_cast(i); M_GTopo topo = cm->geomInterface->iterator_value(*giter); @@ -73,10 +104,8 @@ static gmi_ent* next(gmi_model*m, gmi_iter* i) return toGmiEntity(topo); } -static void end(gmi_model*, gmi_iter* i) -{ - GeometrySmartIterator* giter = (GeometrySmartIterator*)i; - delete giter; +static void end(gmi_model*, gmi_iter* i) { + delete reinterpret_cast(i); } static int get_dim(gmi_model* m, gmi_ent* e) @@ -108,13 +137,13 @@ static gmi_ent* find(gmi_model* m, int dim, int tag) cap_model* cm = (cap_model*)m; M_GTopo topo; if (dim == 0) - topo = cm->geomInterface->get_topo_by_id(VERTEX, tag); + topo = cm->geomInterface->get_topo_by_id(Geometry::VERTEX, tag); else if (dim == 1) - topo = cm->geomInterface->get_topo_by_id(EDGE, tag); + topo = cm->geomInterface->get_topo_by_id(Geometry::EDGE, tag); else if (dim == 2) - topo = cm->geomInterface->get_topo_by_id(FACE, tag); + topo = cm->geomInterface->get_topo_by_id(Geometry::FACE, tag); else if (dim == 3) - topo = cm->geomInterface->get_topo_by_id(REGION, tag); + topo = cm->geomInterface->get_topo_by_id(Geometry::REGION, tag); else gmi_fail("input dim is out of range."); return toGmiEntity(topo); @@ -136,21 +165,21 @@ static gmi_set* adjacent(gmi_model* m, gmi_ent* e, int dim) int edim = gmi_dim(m, e); std::vector gtopos; if (edim == 0 && dim == 1) - cm->geomInterface->get_adjacency(gtopo, EDGE, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::EDGE, gtopos); else if (edim == 1 && dim == 0) - cm->geomInterface->get_adjacency(gtopo, VERTEX, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::VERTEX, gtopos); else if (edim == 1 && dim == 2) - cm->geomInterface->get_adjacency(gtopo, FACE, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::FACE, gtopos); else if (edim == 2 && dim == 0) - cm->geomInterface->get_adjacency(gtopo, VERTEX, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::VERTEX, gtopos); else if (edim == 2 && dim == 1) - cm->geomInterface->get_adjacency(gtopo, EDGE, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::EDGE, gtopos); else if (edim == 2 && dim == 3) - cm->geomInterface->get_adjacency(gtopo, REGION, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::REGION, gtopos); else if (edim == 1 && dim == 3) - cm->geomInterface->get_adjacency(gtopo, REGION, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::REGION, gtopos); else if (edim == 3 && dim == 2) - cm->geomInterface->get_adjacency(gtopo, FACE, gtopos); + cm->geomInterface->get_adjacency(gtopo, Geometry::FACE, gtopos); else gmi_fail("requested adjacency not available\n"); return vector_to_set(gtopos); @@ -285,7 +314,7 @@ static int is_in_closure_of(struct gmi_model* m, struct gmi_ent* e, cap_model* cm = (cap_model*)m; M_GTopo ce = fromGmiEntity(e); M_GTopo cet = fromGmiEntity(et); - GeometryTopo ce_type; + Geometry::GeometryTopo ce_type; MG_API_CALL(cm->geomInterface, get_topo_type(ce, ce_type)); std::vector adj; MG_API_CALL(cm->geomInterface, get_adjacency(cet, ce_type, adj)); @@ -325,28 +354,76 @@ static int is_discrete_ent(struct gmi_model*, struct gmi_ent* e) return 0; } -static void destroy(gmi_model* m) -{ +static void destroy(gmi_model* m) { cap_model* cm = (cap_model*)m; + if (cm->owned) { + if (!cs_module) gmi_fail( + "gmi_cap destroy: called before gmi_cap_start or after gmi_cap_stop" + ); + auto ctx = cm->geomInterface->get_context(); + FunctionPtr fn = get_function(ctx, "DeleteGeometryModel"); + set_input(fn, "Model", cm->gmodel); + auto proc = get_context_processor(ctx); + if (proc->execute(fn) != STATUS_OK) + gmi_fail("gmi_cap destroy: failed to delete Capstone geometry model"); + } free(cm); } -/* static gmi_model* create_cre(const char* filename) */ -/* { */ -/* return gmi_cap_load(filename); */ -/* } */ - -static struct gmi_model_ops ops; - +static gmi_model* create_cre(const char* filename) { + return gmi_cap_load(filename); +} -void gmi_cap_start(void) -{ +void gmi_cap_probe( + const char* creFileName, std::string& model_content, + std::vector& mesh_names, std::vector& mesh_contents +) { + model_content.clear(); + mesh_names.clear(); + mesh_contents.clear(); + Reader *reader = get_reader( + cs_module->get_context(), "Create Native Reader" + ); + DataFileInfo info; + reader->probe(creFileName, info); + for (std::size_t i = 0; i < info.get_num_sections(); i++) { + std::string secType, secName; + info.get_section(i, secType, secName); + if (secType == "mmodel") mesh_names.push_back(secName); + v_string infoNames, infoValues; + info.get_section_info(i, infoNames, infoValues); + PCU_DEBUG_ASSERT(infoNames.size() == infoValues.size()); + for (std::size_t j = 0; j < infoNames.size(); ++j) { + if (secType == "gmodel" && infoNames[j] == "content") { + PCU_DEBUG_ASSERT(model_content.empty()); // should only be one gmodel + model_content = infoValues[j]; + } else if (secType == "mmodel" && infoNames[j] == "content") { + mesh_contents.push_back(infoValues[j]); + } + } + } + PCU_DEBUG_ASSERT(!model_content.empty()); + PCU_DEBUG_ASSERT(mesh_names.size() == mesh_contents.size()); } -void gmi_cap_stop(void) -{ +void gmi_cap_probe( + const char* creFileName, std::vector& mesh_names +) { + mesh_names.clear(); + Reader *reader = get_reader( + cs_module->get_context(), "Create Native Reader" + ); + DataFileInfo info; + reader->probe(creFileName, info); + for (std::size_t i = 0; i < info.get_num_sections(); i++) { + std::string secType, secName; + info.get_section(i, secType, secName); + if (secType == "mmodel") mesh_names.push_back(secName); + } } +static struct gmi_model_ops ops; + void gmi_register_cap(void) { ops.begin = begin; @@ -368,70 +445,84 @@ void gmi_register_cap(void) ops.bbox = bbox; ops.is_discrete_ent = is_discrete_ent; ops.destroy = destroy; - /* gmi_register(create_cre, "cre"); */ + gmi_register(create_cre, "cre"); /* gmi_register(create_native, "xmt_txt"); */ /* gmi_register(create_native, "x_t"); */ /* gmi_register(create_native, "sat"); */ } -/* static gmi_model* owned_import(GeometryDatabaseInterface* gi) */ -/* { */ -/* gmi_model* m = gmi_import_cap(gi); */ -/* ((cap_model*)m)->owned = true; */ -/* return m; */ -/* } */ - -/* struct gmi_model* gmi_cap_load(const char* creFileName) */ -/* { */ -/* if (!gmi_has_ext(creFileName, "cre")) */ -/* gmi_fail("gmi_cap_load: cre file must have .cre extension"); */ -/* const std::string gdbName("Geometry Database : SMLIB");// Switch Create with SMLIB for CAD */ -/* const std::string mdbName("Mesh Database : Create"); */ -/* const std::string adbName("Attribution Database : Create"); */ - -/* CapstoneModule cs("test", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); */ - -/* GeometryDatabaseInterface *g = cs.get_geometry(); */ -/* MeshDatabaseInterface *m = cs.get_mesh(); */ -/* AppContext *c = cs.get_context(); */ - -/* PCU_ALWAYS_ASSERT(g); */ -/* PCU_ALWAYS_ASSERT(m); */ -/* PCU_ALWAYS_ASSERT(c); */ +static gmi_model* owned_import(GDBI* g, M_GModel gmodel) { + cap_model* m = reinterpret_cast(gmi_import_cap(g, gmodel)); + m->owned = true; + return reinterpret_cast(m); +} -/* v_string filenames; */ -/* filenames.push_back(creFileName); */ +struct gmi_model* gmi_cap_load(const char* creFileName) { + if (!gmi_has_ext(creFileName, "cre")) + gmi_fail("gmi_cap_load: cre file must have .cre extension"); + if (!cs_module) gmi_fail("gmi_cap_load called before gmi_cap_start"); -/* M_GModel gmodel = cs.load_files(filenames); */ + std::vector mesh_names; + gmi_cap_probe(creFileName, mesh_names); + return gmi_cap_load_some(creFileName, mesh_names); +} -/* int numBreps; */ -/* g->get_num_breps(numBreps); */ -/* PCU_ALWAYS_ASSERT(numBreps == 1); */ +struct gmi_model* gmi_cap_load_some( + const char* creFileName, const std::vector& mesh_names +) { + if (!gmi_has_ext(creFileName, "cre")) + gmi_fail("gmi_cap_load_some: CRE file must have .cre extension"); + if (!cs_module) gmi_fail("gmi_cap_load_some: called before gmi_cap_start"); + static bool called = false; + if (!called) called = true; + else { + lion_eprint(1, + "WARNING: gmi_cap_load_some called more than once. gmi_cap operations" + " may fail.\n" + ); + } + auto ctx = cs_module->get_context(); + FunctionPtr fn(get_function(ctx, "LoadCreateData")); + set_input(fn, "FileName", creFileName); + set_input(fn, "Meshes", mesh_names); + auto proc = get_context_processor(ctx); + if (proc->execute(fn) != STATUS_OK) + gmi_fail("gmi_cap_load_some: failed to read CRE file"); + M_GModel gmodel; + get_output(fn, "Model", gmodel); + MG_API_CALL(cs_module->get_geometry(), set_current_model(gmodel)); + return owned_import(cs_module->get_geometry(), gmodel); +} -/* return owned_import(g); */ -/* } */ +gmi_model* gmi_import_cap(GDBI* gi) { + M_GModel gmodel; + MG_API_CALL(gi, get_current_model(gmodel)); + return gmi_import_cap(gi, gmodel); +} -gmi_model* gmi_import_cap(GeometryDatabaseInterface* gi) +gmi_model* gmi_import_cap(GDBI* gi, M_GModel gmodel) { cap_model* m; m = (cap_model*)malloc(sizeof(*m)); m->model.ops = &ops; m->geomInterface = gi; + m->gmodel = gmodel; M_GBRep brep; int numBreps; m->geomInterface->get_num_breps(numBreps); - PCU_ALWAYS_ASSERT(numBreps == 1); + if (numBreps != 1) + gmi_fail("gmi_import_cap: loaded CRE with numBreps != 1"); m->geomInterface->get_brep_by_index(0, brep); - m->geomInterface->get_num_topos(brep, VERTEX, m->model.n[0]); - m->geomInterface->get_num_topos(brep, EDGE, m->model.n[1]); - m->geomInterface->get_num_topos(brep, FACE, m->model.n[2]); - m->geomInterface->get_num_topos(brep, REGION, m->model.n[3]); + m->geomInterface->get_num_topos(brep, Geometry::VERTEX, m->model.n[0]); + m->geomInterface->get_num_topos(brep, Geometry::EDGE, m->model.n[1]); + m->geomInterface->get_num_topos(brep, Geometry::FACE, m->model.n[2]); + m->geomInterface->get_num_topos(brep, Geometry::REGION, m->model.n[3]); m->owned = false; return &m->model; } -GeometryDatabaseInterface* gmi_export_cap(gmi_model* m) +GDBI* gmi_export_cap(gmi_model* m) { cap_model* cm = (cap_model*)m; return cm->geomInterface; diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index f2e331d5a..ef0804af3 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -12,26 +12,45 @@ #include "gmi.h" -#include "CapstoneModule.h" +#ifdef __cplusplus +#include +#include #include "CreateMG_Framework_Geometry.h" -#include "CreateMG_Framework_Mesh.h" - -using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; - -gmi_ent* toGmiEntity(M_GTopo topo); -M_GTopo fromGmiEntity(gmi_ent* g); +#else +extern "C" { +#endif void gmi_cap_start(void); void gmi_cap_stop(void); void gmi_register_cap(void); - struct gmi_model* gmi_cap_load(const char* creFileName); -struct gmi_model* gmi_import_cap(GeometryDatabaseInterface* gi); -GeometryDatabaseInterface* gmi_export_cap(struct gmi_model* m); +#ifdef __cplusplus + +void gmi_cap_probe( + const char* creFileName, std::vector& mesh_names +); +void gmi_cap_probe( + const char* creFileName, std::string& model_content, + std::vector& mesh_names, std::vector& mesh_contents +); +struct gmi_model* gmi_cap_load_some( + const char* creFileName, const std::vector& mesh_names +); + +gmi_ent* toGmiEntity(CreateMG::M_GTopo topo); +CreateMG::M_GTopo fromGmiEntity(gmi_ent* g); + +struct gmi_model* gmi_import_cap(CreateMG::GDBI* gi); +struct gmi_model* gmi_import_cap(CreateMG::GDBI* gi, CreateMG::M_GModel); +CreateMG::GDBI* gmi_export_cap(struct gmi_model* m); + +#endif // __cplusplus + + +#ifndef __cplusplus +} // extern "C" #endif +#endif // GMI_CAP_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 85b3db547..52556a534 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -229,6 +229,8 @@ if(ENABLE_CAPSTONE) util_exe_func(capCheckParam capCheckParam.cc) util_exe_func(capAdapt capAdapt.cc) target_link_libraries(capAdapt framework_meshing) + util_exe_func(capProbe capProbe.cc) + util_exe_func(capLoadSome capLoadSome.cc) endif() # send all the newly added utility executable targets diff --git a/test/capLoadSome.cc b/test/capLoadSome.cc new file mode 100644 index 000000000..0a964863b --- /dev/null +++ b/test/capLoadSome.cc @@ -0,0 +1,29 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "USAGE: " << argv[0] << " [meshname...]" + << std::endl; + return 1; + } + pcu::Init(&argc, &argv); + gmi_cap_start(); + gmi_register_cap(); + std::string model_content; + std::vector mesh_names; + for (int i = 2; i < argc; ++i) mesh_names.push_back(argv[i]); + gmi_model* model = gmi_cap_load_some(argv[1], mesh_names); + gmi_destroy(model); + gmi_cap_stop(); + pcu::Finalize(); + return 0; +} diff --git a/test/capProbe.cc b/test/capProbe.cc new file mode 100644 index 000000000..fb8e731a7 --- /dev/null +++ b/test/capProbe.cc @@ -0,0 +1,31 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "USAGE: " << argv[0] << " " << std::endl; + return 1; + } + pcu::Init(&argc, &argv); + gmi_cap_start(); + gmi_register_cap(); + std::string model_content; + std::vector mesh_names, mesh_contents; + gmi_cap_probe(argv[1], model_content, mesh_names, mesh_contents); + std::cout << "model: " << model_content << std::endl; + for (std::size_t i = 0; i < mesh_names.size(); ++i) { + std::cout << "mesh (" << i << ") `" << mesh_names[i] << "`: " + << mesh_contents[i] << std::endl; + } + gmi_cap_stop(); + pcu::Finalize(); + return 0; +} From 5f2e701455e95e23904e232e2f08ec26473dbf0c Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Wed, 18 Jun 2025 01:50:02 -0400 Subject: [PATCH 06/26] add gmi_cap documentation and update cmake - gmi_cap/CMakeLists.txt: update link libraries. - gmi_cap/gmi_cap.h: add function documentation. - add doxygen group Signed-off-by: Aiden Woodruff --- gmi_cap/CMakeLists.txt | 3 +- gmi_cap/gmi_cap.h | 82 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/gmi_cap/CMakeLists.txt b/gmi_cap/CMakeLists.txt index 3fa431f34..3c616071d 100644 --- a/gmi_cap/CMakeLists.txt +++ b/gmi_cap/CMakeLists.txt @@ -13,7 +13,8 @@ set(HEADERS gmi_cap.h) add_library(gmi_cap ${SOURCES}) -target_link_libraries(gmi_cap PUBLIC gmi pcu lion capstone_module framework_testing) +target_link_libraries(gmi_cap PUBLIC gmi framework_geometry) +target_link_libraries(gmi_cap PRIVATE pcu lion capstone_module) # Include directories target_include_directories(gmi_cap PUBLIC diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index ef0804af3..c98d3870a 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -9,7 +9,22 @@ *******************************************************************************/ #ifndef GMI_CAP_H #define GMI_CAP_H - +/** + * \file gmi_cap.h + * \brief Concrete Capstone GMI implementation. + * + * This interface can be used in two ways: + * + * 1. to import an existing Capstone model; and + * 2. to load the model and (optionally) meshes from a CRE file by wrapping + * Capstone calls. + * + * The first option results in a non-owned model (gmi_cap_start does not need + * to be called). The second option results in an owned model (gmi_cap_start + * MUST be called) for which destruction is handled by gmi_destroy. Both + * options require gmi_register_cap to be called. It is recommended to call + * gmi_cap_start (if desired) before gmi_register_cap. + */ #include "gmi.h" #ifdef __cplusplus @@ -20,9 +35,49 @@ extern "C" { #endif +/** + * \defgroup gmi_cap Capstone geometric model interface + * + * gmi_cap implements gmi. The interface in gmi_cap.h includes additional + * functions to initialize the library and simplify loading of Capstone models. + * + * \{ + */ + +/** + * \brief Initialize gmi_cap library. + * + * \note This call is required before calling any other gmi_cap functions. + */ void gmi_cap_start(void); +/** + * \brief Finalize gmi_cap library. + * + * \note No gmi_cap functions except gmi_cap_import and gmi_cap_export should + * bec called after this function. + */ void gmi_cap_stop(void); +/** + * \brief Register gmi_cap in GMI's global list. + * + * \note This call is required before every gmi_cap function except + * gmi_cap_start. + * + * This call registers gmi_cap with GMI. It also registers the ".cre" extension + * with the Create loader so that gmi_load can be used with ".cre" files. + */ void gmi_register_cap(void); +/** + * \brief Load a gmi_model from a CRE file. + * + * This method is called by gmi_load when the input file has a ".cre" + * extension. It loads the file with all associated mesh models. Use + * gmi_cap_load_some to only load certain meshes. + * + * \param creFileName the name of the CRE file + * \pre gmi_cap_stop must be called before this method. + * \pre creFileName ends in ".cre" + */ struct gmi_model* gmi_cap_load(const char* creFileName); #ifdef __cplusplus @@ -41,8 +96,29 @@ struct gmi_model* gmi_cap_load_some( gmi_ent* toGmiEntity(CreateMG::M_GTopo topo); CreateMG::M_GTopo fromGmiEntity(gmi_ent* g); +/** + * \brief Import a non-owned Capstone model. + * + * Loads the current geometry model on the interface. + * + * \param gi Geometry interface to load. + * \pre gi has a current model. + */ struct gmi_model* gmi_import_cap(CreateMG::GDBI* gi); -struct gmi_model* gmi_import_cap(CreateMG::GDBI* gi, CreateMG::M_GModel); +/** + * \brief Import a non-owned, specific Capstone model. + * + * \param gi Geometry interface to load from. + * \param gmodel The existing geometry model to load. + */ +struct gmi_model* gmi_import_cap( + CreateMG::GDBI* gi, CreateMG::M_GModel gmodel +); +/** + * \brief Expose the underlying Capstone geometry interface from a gmi_model. + * + * \param m Loaded or imported gmi_model. + */ CreateMG::GDBI* gmi_export_cap(struct gmi_model* m); #endif // __cplusplus @@ -52,5 +128,7 @@ CreateMG::GDBI* gmi_export_cap(struct gmi_model* m); } // extern "C" #endif +/** \} */ + #endif // GMI_CAP_H From da0bcd082e7b43da3570583ec89d6a5ebf7b2a34 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Wed, 18 Jun 2025 02:17:21 -0400 Subject: [PATCH 07/26] update apf_cap and tests that call it - apf_cap/CMakeLists.txt: - apf_cap/apfCAP.h: move MeshCAP definition to apfCAP.cc. - add function documentation. - (hasCAP): new function. - add copyright notice. - fix #include guard name. - remove namespace using declarations. - forward declare arguments. - remove unnecessary #includes. - (createMesh): rename to createCapMesh. - similar to loadMdsMesh, etc - (createCapMesh): add overload using existing gmi_model. - (getCapNative): add function to replace MeshCAP::getMesh functionality. - test/cap*: replace createMesh with createCapMesh. - update #includes now that apf_cap doesn't have anything. - remove using namespace CreateMG::*. - prefer MDBI/GDBI to long names. - apf_cap/apfCAP.cc: reorganize #includes. - add using namespace CreateMG and alias commonly used CreateMG::Mesh (which would also conflict with apf::Mesh). - (hasCAP): add definition. - (MeshCAP): add class definition from apfCAP.h - add ownership of mesh database interface in case we use the gmi_model createCapMesh. - (MeshCAP::MeshCAP): use Capstone get_dimension. - add MeshMG:: namespace where needed. - light reformatting. - (createMesh): remove unused function (with non-existent capMesh* type). - (castEntity): remove unused function. Signed-off-by: Aiden Woodruff --- apf_cap/CMakeLists.txt | 4 +- apf_cap/apfCAP.cc | 420 ++++++++++++++++++++++++++------------- apf_cap/apfCAP.h | 239 ++++++---------------- test/cap2vtk.cc | 2 +- test/capAdapt.cc | 18 +- test/capGeomTest.cc | 4 +- test/capVol.cc | 17 +- test/cap_closestPoint.cc | 5 +- test/cap_inClosureOf.cc | 5 +- 9 files changed, 377 insertions(+), 337 deletions(-) diff --git a/apf_cap/CMakeLists.txt b/apf_cap/CMakeLists.txt index 785a38a6b..156d3a65e 100644 --- a/apf_cap/CMakeLists.txt +++ b/apf_cap/CMakeLists.txt @@ -17,9 +17,7 @@ set(HEADERS apfCAP.h) #Library add_library(apf_cap ${SOURCES}) -target_link_libraries(apf_cap PUBLIC apf gmi_cap) -target_link_libraries(apf_cap PUBLIC capstone_module - framework_testing) +target_link_libraries(apf_cap PUBLIC apf gmi_cap capstone_module) set(CMAKE_CXX_OLD_STANDARD "${CMAKE_CXX_STANDARD}") cmake_push_check_state(RESET) diff --git a/apf_cap/apfCAP.cc b/apf_cap/apfCAP.cc index 13919376c..1e8022e44 100644 --- a/apf_cap/apfCAP.cc +++ b/apf_cap/apfCAP.cc @@ -1,18 +1,26 @@ #include "apfCAP.h" + +#include +#include + #include +#include #include #include #include -#include +#include #include #include -#include -#include +#include +#include #ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL #include #endif +using namespace CreateMG; +namespace MeshMG = CreateMG::Mesh; + namespace apf { /* These tables are taken from ../mds/mds.c @@ -63,8 +71,9 @@ int const degree[Mesh::TYPES][4] = ,{5, 8,5,1} /* MDS_PYRAMID */ }; - - +bool hasCAP() noexcept { + return true; +} MeshEntity* toEntity(M_MTopo topo) { @@ -81,21 +90,170 @@ M_MTopo fromEntity(MeshEntity* e) return topo; } -MeshCAP::MeshCAP(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb): - meshInterface(mdb), geomInterface(gdb) +class TagCAP; + +class MeshCAP : public Mesh2 { - PCU_ALWAYS_ASSERT(meshInterface); + public: + MeshCAP(MDBI* mdb, GDBI* gdb); + MeshCAP(gmi_model* mdl, MDBIP mdb); + virtual ~MeshCAP(); + /* --------------------------------------------------------------------- */ + /* Category 00: General Mesh APIs */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + int getDimension(); + std::size_t count(int dimension); + Type getType(MeshEntity* e); + void verify(); + // OPTIONAL Member Functions // + void writeNative(const char* fileName); + void destroyNative(); + + /* --------------------------------------------------------------------- */ + /* Category 01: General Getters and Setters for vertex coordinates */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + void getPoint_(MeshEntity* e, int, Vector3& point); + void setPoint_(MeshEntity* e, int, Vector3 const& p); + void getParam(MeshEntity* e, Vector3& p); + void setParam(MeshEntity* e, Vector3 const& point); + + /* --------------------------------------------------------------------- */ + /* Category 02: Iterators */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + MeshIterator* begin(int dimension); + MeshEntity* iterate(MeshIterator* it); + void end(MeshIterator* it); + void increment(MeshIterator* it); + bool isDone(MeshIterator* it); + MeshEntity* deref(MeshIterator* it); + + /* --------------------------------------------------------------------- */ + /* Category 03: Adjacencies */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + void getAdjacent(MeshEntity* e, int dimension, Adjacent& adjacent); + int getDownward(MeshEntity* e, int dimension, MeshEntity** adjacent); + MeshEntity* getUpward(MeshEntity* e, int i); + bool hasUp(MeshEntity* e); + // OPTIONAL Member Functions // + bool hasAdjacency(int from_dim, int to_dim); + void createAdjacency(int from_dim, int to_dim); + void deleteAdjacency(int from_dim, int to_dim); + void getUp(MeshEntity* e, Up& up); + int countUpward(MeshEntity* e); + + /* --------------------------------------------------------------------- */ + /* Category 04: CAD model inquires */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + ModelEntity* toModel(MeshEntity* e); + // OPTIONAL Member Functions // + gmi_model* getModel(); + void setModel(gmi_model* newModel); + void setModelEntity(MeshEntity* e, ModelEntity* me); + + /* --------------------------------------------------------------------- */ + /* Category 05: Entity Creation/Deletion */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + MeshEntity* createVert_(ModelEntity* me); + MeshEntity* createEntity_(int type, ModelEntity* me, MeshEntity** down); + void destroy_(MeshEntity* e); + + /* --------------------------------------------------------------------- */ + /* Category 06: Attachable Data Functionality */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + MeshTag* createDoubleTag(const char* name, int size); + MeshTag* createIntTag(const char* name, int size); + MeshTag* createLongTag(const char* name, int size); + MeshTag* findTag(const char* name); + void destroyTag(MeshTag* t); + void renameTag(MeshTag* t, const char* name); + void getTags(DynamicArray& tags); + /* void getTag(MeshEntity* e, MeshTag* t, void* data); */ + /* void setTag(MeshEntity* e, MeshTag* t, void const* data); */ + void getDoubleTag(MeshEntity* e, MeshTag* tag, double* data); + void setDoubleTag(MeshEntity* e, MeshTag* tag, double const* data); + void getIntTag(MeshEntity* e, MeshTag* tag, int* data); + void setIntTag(MeshEntity* e, MeshTag* tag, int const* data); + void getLongTag(MeshEntity* e, MeshTag* tag, long* data); + void setLongTag(MeshEntity* e, MeshTag* tag, long const* data); + void removeTag(MeshEntity* e, MeshTag* t); + bool hasTag(MeshEntity* e, MeshTag* t); + int getTagType(MeshTag* t); + int getTagSize(MeshTag* t); + const char* getTagName(MeshTag* t); + unsigned getTagChecksum(MeshTag*,int); + + + /* --------------------------------------------------------------------- */ + /* Category 07: Distributed Meshes */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + bool isShared(MeshEntity* e); + bool isGhost(MeshEntity*) { return false; } + bool isGhosted(MeshEntity*) { return false; } + bool isOwned(MeshEntity* e); + int getOwner(MeshEntity* e); + void getRemotes(MeshEntity* e, Copies& remotes); + void getResidence(MeshEntity* e, Parts& residence); + int getId(); + void setResidence(MeshEntity*, Parts&) {} + void acceptChanges() {} + // OPTIONAL Member Functions // + void deleteGhost(MeshEntity*) {} + void addGhost(MeshEntity*, int, MeshEntity*) {} + int getGhosts(MeshEntity*, Copies&) { return 0; } + void migrate(Migration* plan); + void setRemotes(MeshEntity*, Copies&) {} + void addRemote(MeshEntity*, int, MeshEntity*) {} + void clearRemotes(MeshEntity*) {} + + + /* --------------------------------------------------------------------- */ + /* Category 08: Periodic Meshes */ + /* --------------------------------------------------------------------- */ + // REQUIRED Member Functions // + bool hasMatching() { return false; } + void getMatches(MeshEntity* e, Matches& m); + // OPTIONAL Member Functions // + void addMatch(MeshEntity*, int, MeshEntity*) {} + void clearMatches(MeshEntity*) {} + void clear_() {} + void getDgCopies(MeshEntity* e, DgCopies& dgCopies, ModelEntity* me); + + MDBI* getMesh() { return meshInterface; } + protected: + gmi_model* model; + MDBIP meshOwner; + MDBI* meshInterface; + int d; + std::vector tags; +}; + +MeshCAP::MeshCAP(MDBI* mdb, GDBI* gdb): meshInterface(mdb) { + model = gmi_import_cap(gdb); + PCU_ALWAYS_ASSERT(mdb); + MG_API_CALL(mdb, get_dimension(d)); +} - std::size_t numRegions = 0; - meshInterface->get_num_topos(TOPO_REGION, numRegions); - d = numRegions ? 3 : 2; - iterDim = -1; - model = gmi_import_cap(geomInterface); +MeshCAP::MeshCAP(gmi_model* mdl, MDBIP mdb): + model(mdl), meshOwner(mdb), meshInterface(mdb.get()) +{ + PCU_ALWAYS_ASSERT(mdl); + PCU_ALWAYS_ASSERT(mdb.is_valid()); + MG_API_CALL(mdb.get(), get_dimension(d)); } MeshCAP::~MeshCAP() { - if (model) destroyNative(); + if (model) gmi_destroy(model); + model = nullptr; + meshInterface = nullptr; } int MeshCAP::getDimension() @@ -107,37 +265,29 @@ std::size_t MeshCAP::count(int dimension) { std::size_t count = 0; if (dimension == 0) - meshInterface->get_num_topos(TOPO_VERTEX, count); + meshInterface->get_num_topos(MeshMG::TOPO_VERTEX, count); if (dimension == 1) - meshInterface->get_num_topos(TOPO_EDGE, count); + meshInterface->get_num_topos(MeshMG::TOPO_EDGE, count); if (dimension == 2) - meshInterface->get_num_topos(TOPO_FACE, count); + meshInterface->get_num_topos(MeshMG::TOPO_FACE, count); if (dimension == 3) - meshInterface->get_num_topos(TOPO_REGION, count); + meshInterface->get_num_topos(MeshMG::TOPO_REGION, count); return count; } Mesh::Type MeshCAP::getType(MeshEntity* e) { M_MTopo topo = fromEntity(e); - MeshShape topoShape; + MeshMG::MeshShape topoShape; meshInterface->get_topo_shape(topo, topoShape); - if (topoShape == SHAPE_NODE) - return Mesh::VERTEX; - else if (topoShape == SHAPE_SEGMENT) - return Mesh::EDGE; - else if (topoShape == SHAPE_TRIANGLE) - return Mesh::TRIANGLE; - else if (topoShape == SHAPE_QUAD) - return Mesh::QUAD; - else if (topoShape == SHAPE_TETRA) - return Mesh::TET; - else if (topoShape == SHAPE_HEX) - return Mesh::HEX; - else if (topoShape == SHAPE_PRISM) - return Mesh::PRISM; - else if (topoShape == SHAPE_PYRAMID) - return Mesh::PYRAMID; + if (topoShape == MeshMG::SHAPE_NODE) return VERTEX; + else if (topoShape == MeshMG::SHAPE_SEGMENT) return EDGE; + else if (topoShape == MeshMG::SHAPE_TRIANGLE) return TRIANGLE; + else if (topoShape == MeshMG::SHAPE_QUAD) return QUAD; + else if (topoShape == MeshMG::SHAPE_TETRA) return TET; + else if (topoShape == MeshMG::SHAPE_HEX) return HEX; + else if (topoShape == MeshMG::SHAPE_PRISM) return PRISM; + else if (topoShape == MeshMG::SHAPE_PYRAMID) return PYRAMID; else apf::fail("MeshCAP::getType encountered an unknown entity type!\n"); } @@ -184,7 +334,7 @@ void MeshCAP::getParam(MeshEntity* e, Vector3& point) { M_MTopo topo = fromEntity(e); double u, v; - GeometryTopoType gtype; + MeshMG::GeometryTopoType gtype; meshInterface->get_vertex_uv_parameters(topo, u, v, gtype); point = Vector3(u, v, 0.); } @@ -198,15 +348,15 @@ void MeshCAP::setParam(MeshEntity* e, Vector3 const& point) MeshIterator* MeshCAP::begin(int dimension) { - MeshSmartIterator* miter = new MeshSmartIterator(meshInterface); + auto miter = new MeshMG::MeshSmartIterator(meshInterface); if (dimension == 0) - meshInterface->get_topo_iterator(TOPO_VERTEX, *miter); + meshInterface->get_topo_iterator(MeshMG::TOPO_VERTEX, *miter); if (dimension == 1) - meshInterface->get_topo_iterator(TOPO_EDGE, *miter); + meshInterface->get_topo_iterator(MeshMG::TOPO_EDGE, *miter); if (dimension == 2) - meshInterface->get_topo_iterator(TOPO_FACE, *miter); + meshInterface->get_topo_iterator(MeshMG::TOPO_FACE, *miter); if (dimension == 3) - meshInterface->get_topo_iterator(TOPO_REGION, *miter); + meshInterface->get_topo_iterator(MeshMG::TOPO_REGION, *miter); meshInterface->iterator_begin(*miter); return reinterpret_cast(miter); } @@ -216,7 +366,7 @@ MeshIterator* MeshCAP::begin(int dimension) */ MeshEntity* MeshCAP::iterate(MeshIterator* it) { - MeshSmartIterator* miter = reinterpret_cast(it); + auto miter = reinterpret_cast(it); M_MTopo topo = meshInterface->iterator_value(*miter); @@ -230,26 +380,26 @@ MeshEntity* MeshCAP::iterate(MeshIterator* it) void MeshCAP::end(MeshIterator* it) { - MeshSmartIterator* miter = reinterpret_cast(it); + auto miter = reinterpret_cast(it); delete miter; } void MeshCAP::increment(MeshIterator* it) { - MeshSmartIterator* miter = reinterpret_cast(it); + auto miter = reinterpret_cast(it); meshInterface->iterator_next(*miter); it = reinterpret_cast(miter); } bool MeshCAP::isDone(MeshIterator* it) { - MeshSmartIterator* miter = reinterpret_cast(it); + auto miter = reinterpret_cast(it); return meshInterface->iterator_end(*miter); } MeshEntity* MeshCAP::deref(MeshIterator* it) { - MeshSmartIterator* miter = reinterpret_cast(it); + auto miter = reinterpret_cast(it); M_MTopo topo = meshInterface->iterator_value(*miter); return toEntity(topo); } @@ -259,7 +409,7 @@ void MeshCAP::getAdjacent(MeshEntity* e, DynamicArray& adjacent) { M_MTopo topo = fromEntity(e); - MeshTopo type; + MeshMG::MeshTopo type; meshInterface->get_topo_type(topo, type); std::vector adjTopos; if (apf::getDimension(this, e) == dimension) @@ -268,41 +418,41 @@ void MeshCAP::getAdjacent(MeshEntity* e, adjacent[0] = e; return; } - if (type == TOPO_VERTEX) + if (type == MeshMG::TOPO_VERTEX) { if (dimension == 1) - meshInterface->get_adjacency_vector(topo, TOPO_EDGE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_EDGE, adjTopos); if (dimension == 2) - meshInterface->get_adjacency_vector(topo, TOPO_FACE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_FACE, adjTopos); if (dimension == 3) - meshInterface->get_adjacency_vector(topo, TOPO_REGION, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_REGION, adjTopos); } - if (type == TOPO_EDGE) + if (type == MeshMG::TOPO_EDGE) { if (dimension == 0) - meshInterface->get_adjacency_vector(topo, TOPO_VERTEX, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_VERTEX, adjTopos); if (dimension == 2) - meshInterface->get_adjacency_vector(topo, TOPO_FACE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_FACE, adjTopos); if (dimension == 3) - meshInterface->get_adjacency_vector(topo, TOPO_REGION, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_REGION, adjTopos); } - if (type == TOPO_FACE) + if (type == MeshMG::TOPO_FACE) { if (dimension == 0) - meshInterface->get_adjacency_vector(topo, TOPO_VERTEX, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_VERTEX, adjTopos); if (dimension == 1) - meshInterface->get_adjacency_vector(topo, TOPO_EDGE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_EDGE, adjTopos); if (dimension == 3) - meshInterface->get_adjacency_vector(topo, TOPO_REGION, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_REGION, adjTopos); } - if (type == TOPO_REGION) + if (type == MeshMG::TOPO_REGION) { if (dimension == 0) - meshInterface->get_adjacency_vector(topo, TOPO_VERTEX, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_VERTEX, adjTopos); if (dimension == 1) - meshInterface->get_adjacency_vector(topo, TOPO_EDGE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_EDGE, adjTopos); if (dimension == 2) - meshInterface->get_adjacency_vector(topo, TOPO_FACE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_FACE, adjTopos); } adjacent.setSize(adjTopos.size()); for (size_t i = 0; i < adjTopos.size(); i++) { @@ -315,15 +465,15 @@ int MeshCAP::getDownward(MeshEntity* e, MeshEntity** down) { M_MTopo topo = fromEntity(e); - MeshTopo type; + MeshMG::MeshTopo type; meshInterface->get_topo_type(topo, type); int from; - if (type == TOPO_VERTEX) + if (type == MeshMG::TOPO_VERTEX) from = 0; - else if(type == TOPO_EDGE) + else if(type == MeshMG::TOPO_EDGE) from = 1; - else if(type == TOPO_FACE) + else if(type == MeshMG::TOPO_FACE) from = 2; else from = 3; @@ -337,11 +487,11 @@ int MeshCAP::getDownward(MeshEntity* e, PCU_ALWAYS_ASSERT(from > dimension); if (dimension == 0) - meshInterface->get_adjacency_vector(topo, TOPO_VERTEX, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_VERTEX, adjTopos); if (dimension == 1) - meshInterface->get_adjacency_vector(topo, TOPO_EDGE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_EDGE, adjTopos); if (dimension == 2) - meshInterface->get_adjacency_vector(topo, TOPO_FACE, adjTopos); + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_FACE, adjTopos); for (std::size_t i = 0; i < adjTopos.size(); i++) down[i] = toEntity(adjTopos[i]); @@ -353,25 +503,25 @@ int MeshCAP::getDownward(MeshEntity* e, MeshEntity* MeshCAP::getUpward(MeshEntity* e, int i) { M_MTopo topo = fromEntity(e); - MeshTopo type; + MeshMG::MeshTopo type; meshInterface->get_topo_type(topo, type); std::vector adjId; - if (type == TOPO_VERTEX) + if (type == MeshMG::TOPO_VERTEX) { - meshInterface->get_adjacency_id_vector(topo, TOPO_EDGE, adjId); - M_MTopo topo = meshInterface->get_topo_by_id(TOPO_EDGE, adjId[i]); + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_EDGE, adjId); + M_MTopo topo = meshInterface->get_topo_by_id(MeshMG::TOPO_EDGE, adjId[i]); return toEntity(topo); } - if (type == TOPO_EDGE) + if (type == MeshMG::TOPO_EDGE) { - meshInterface->get_adjacency_id_vector(topo, TOPO_FACE, adjId); - M_MTopo topo = meshInterface->get_topo_by_id(TOPO_FACE, adjId[i]); + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_FACE, adjId); + M_MTopo topo = meshInterface->get_topo_by_id(MeshMG::TOPO_FACE, adjId[i]); return toEntity(topo); } - if (type == TOPO_FACE) + if (type == MeshMG::TOPO_FACE) { - meshInterface->get_adjacency_id_vector(topo, TOPO_REGION, adjId); - M_MTopo topo = meshInterface->get_topo_by_id(TOPO_REGION, adjId[i]); + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_REGION, adjId); + M_MTopo topo = meshInterface->get_topo_by_id(MeshMG::TOPO_REGION, adjId[i]); return toEntity(topo); } return 0; @@ -404,15 +554,15 @@ void MeshCAP::deleteAdjacency(int from_dim, int to_dim) void MeshCAP::getUp(MeshEntity* e, Up& up) { M_MTopo topo = fromEntity(e); - MeshTopo type; + MeshMG::MeshTopo type; meshInterface->get_topo_type(topo, type); std::vector adjTopos; - if (type == TOPO_VERTEX) - meshInterface->get_adjacency_vector(topo, TOPO_EDGE, adjTopos); - if (type == TOPO_EDGE) - meshInterface->get_adjacency_vector(topo, TOPO_FACE, adjTopos); - if (type == TOPO_FACE) - meshInterface->get_adjacency_vector(topo, TOPO_REGION, adjTopos); + if (type == MeshMG::TOPO_VERTEX) + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_EDGE, adjTopos); + if (type == MeshMG::TOPO_EDGE) + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_FACE, adjTopos); + if (type == MeshMG::TOPO_FACE) + meshInterface->get_adjacency_vector(topo, MeshMG::TOPO_REGION, adjTopos); up.n = adjTopos.size(); for (int i = 0; i < up.n; i++) { up.e[i] = toEntity(adjTopos[i]); @@ -422,15 +572,15 @@ void MeshCAP::getUp(MeshEntity* e, Up& up) int MeshCAP::countUpward(MeshEntity* e) { M_MTopo topo = fromEntity(e); - MeshTopo type; + MeshMG::MeshTopo type; meshInterface->get_topo_type(topo, type); std::vector adjTopos; - if (type == TOPO_VERTEX) - meshInterface->get_adjacency_id_vector(topo, TOPO_EDGE, adjTopos); - if (type == TOPO_EDGE) - meshInterface->get_adjacency_id_vector(topo, TOPO_FACE, adjTopos); - if (type == TOPO_FACE) - meshInterface->get_adjacency_id_vector(topo, TOPO_REGION, adjTopos); + if (type == MeshMG::TOPO_VERTEX) + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_EDGE, adjTopos); + if (type == MeshMG::TOPO_EDGE) + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_FACE, adjTopos); + if (type == MeshMG::TOPO_FACE) + meshInterface->get_adjacency_id_vector(topo, MeshMG::TOPO_REGION, adjTopos); return (int)adjTopos.size(); } @@ -438,7 +588,7 @@ ModelEntity* MeshCAP::toModel(MeshEntity* e) { M_MTopo topo = fromEntity(e); M_GTopo gtopo; - GeometryTopoType gtype; + MeshMG::GeometryTopoType gtype; meshInterface->get_geom_entity(topo, gtype, gtopo); gmi_ent* g = toGmiEntity(gtopo); return reinterpret_cast(g); @@ -461,21 +611,21 @@ void MeshCAP::setModelEntity(MeshEntity* e, ModelEntity* me) apf::fail("MeshCAP::setModelEntity called!\n"); } -static GeometryTopoType getCapGeomType(int d) +static MeshMG::GeometryTopoType getCapGeomType(int d) { - GeometryTopoType gtype = GVERTEX; + MeshMG::GeometryTopoType gtype = MeshMG::GVERTEX; switch (d) { case 0: - gtype = GVERTEX; + gtype = MeshMG::GVERTEX; break; case 1: - gtype = GEDGE; + gtype = MeshMG::GEDGE; break; case 2: - gtype = GFACE; + gtype = MeshMG::GFACE; break; case 3: - gtype = GREGION; + gtype = MeshMG::GREGION; break; default: break; @@ -494,38 +644,38 @@ MeshEntity* MeshCAP::createVert_(ModelEntity* me) gmi_ent* g = reinterpret_cast(me); M_GTopo gtopo = fromGmiEntity(g); int d = getModelType(me); - GeometryTopoType gtype = getCapGeomType(d); + MeshMG::GeometryTopoType gtype = getCapGeomType(d); meshInterface->create_vertex(xyz, vertex, gtype, gtopo); return toEntity(vertex); } -static MeshShape getCapShape(int type) +static MeshMG::MeshShape getCapShape(int type) { - MeshShape shape = SHAPE_UNKNOWN; + MeshMG::MeshShape shape = MeshMG::SHAPE_UNKNOWN; switch (type) { case Mesh::VERTEX: - shape = SHAPE_NODE; + shape = MeshMG::SHAPE_NODE; break; case Mesh::EDGE: - shape = SHAPE_SEGMENT; + shape = MeshMG::SHAPE_SEGMENT; break; case Mesh::TRIANGLE: - shape = SHAPE_TRIANGLE; + shape = MeshMG::SHAPE_TRIANGLE; break; case Mesh::QUAD: - shape = SHAPE_QUAD; + shape = MeshMG::SHAPE_QUAD; break; case Mesh::TET: - shape = SHAPE_TETRA; + shape = MeshMG::SHAPE_TETRA; break; case Mesh::HEX: - shape = SHAPE_HEX; + shape = MeshMG::SHAPE_HEX; break; case Mesh::PRISM: - shape = SHAPE_PRISM; + shape = MeshMG::SHAPE_PRISM; break; case Mesh::PYRAMID: - shape = SHAPE_PYRAMID; + shape = MeshMG::SHAPE_PYRAMID; break; default: break; @@ -592,14 +742,14 @@ MeshEntity* MeshCAP::createEntity_(int type, ModelEntity* me, MeshEntity** down) } M_MTopo topo; - MeshShape shape = getCapShape(type); + MeshMG::MeshShape shape = getCapShape(type); if ( !me ) { meshInterface->create_topo(shape, mtopos, topo); } // if model is not 0 figure out its type else { int d = getModelType(me); - GeometryTopoType gtype = getCapGeomType(d); + MeshMG::GeometryTopoType gtype = getCapGeomType(d); gmi_ent* g = reinterpret_cast(me); M_GTopo gtopo = fromGmiEntity(g); meshInterface->create_topo(shape, mtopos, topo, gtype, gtopo); @@ -621,15 +771,12 @@ void MeshCAP::destroy_(MeshEntity* e) class TagCAP { public: - TagCAP(MeshDatabaseInterface* m, - const char* n, - int c): + TagCAP(MDBI* m, const char* n, int c): mesh(m), count(c), name(n) {} - virtual ~TagCAP() - {} + virtual ~TagCAP() {} virtual void* allocate() = 0; virtual void deallocate(void* p) = 0; virtual int getType() = 0; @@ -658,7 +805,7 @@ class TagCAP { this->name = n; } - MeshDatabaseInterface* mesh; + MDBI* mesh; int count; std::string name; std::map tagContainer; @@ -667,7 +814,7 @@ class TagCAP class DoubleTagCAP : public TagCAP { public: - DoubleTagCAP(MeshDatabaseInterface* m, const char* name, int c): + DoubleTagCAP(MDBI* m, const char* name, int c): TagCAP(m, name,c) {} virtual void* allocate() @@ -700,7 +847,7 @@ class DoubleTagCAP : public TagCAP class IntTagCAP : public TagCAP { public: - IntTagCAP(MeshDatabaseInterface* m, const char* name, int c): + IntTagCAP(MDBI* m, const char* name, int c): TagCAP(m, name,c) {} virtual void* allocate() @@ -925,23 +1072,22 @@ void MeshCAP::getDgCopies(MeshEntity* e, DgCopies& dgCopies, ModelEntity* me) apf::fail("MeshCAP::getDgCopies called!\n"); } -Mesh2* createMesh(capMesh* mesh) -{ - (void)mesh; - apf::fail("MeshCAP::createMesh called!\n"); - return 0; -} - -MeshEntity* castEntity(capEntity* entity) -{ - (void)entity; - apf::fail("MeshCAP::castEntity called!\n"); - return 0; +Mesh2* createCapMesh(MDBI* mdb, GDBI* gdb, pcu::PCU *PCUObj) { + MeshCAP* m = new MeshCAP(mdb, gdb); + m->init(getLagrange(1), PCUObj); + return m; } -Mesh2* createMesh(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb, pcu::PCU *PCUObj) -{ - MeshCAP* m = new MeshCAP(mdb, gdb); +Mesh2* createCapMesh( + gmi_model* model, const char* meshname, pcu::PCU* PCUObj +) { + GDBI* gdb = gmi_export_cap(model); + AppContext* ctx = gdb->get_context(); + MDBIP mdp = get_context_mesh_database_interface(ctx); + M_MModel mmodel; + MG_API_CALL(mdp.get(), get_model_by_name(meshname, mmodel)); + MG_API_CALL(mdp.get(), set_current_model(mmodel)); + MeshCAP* m = new MeshCAP(model, mdp); m->init(getLagrange(1), PCUObj); return m; } diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index 849c90d22..1b4d4d675 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -1,187 +1,77 @@ -#ifndef APFCAP -#define APFCAP +/****************************************************************************** -#include -#include + Copyright 2025 Scientific Computation Research Center, + Rensselaer Polytechnic Institute. All rights reserved. -#include "CapstoneModule.h" -#include "CreateMG_Framework_Geometry.h" -#include "CreateMG_Framework_Mesh.h" + This work is open source software, licensed under the terms of the + BSD license as described in the LICENSE file in the top-level directory. -using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; +******************************************************************************/ +#ifndef APF_CAP_H +#define APF_CAP_H +#include - -class capEntity; -class capMesh; +// Forward declarations +struct gmi_model; +namespace pcu { + class PCU; +} +namespace CreateMG { + namespace Geometry { class GeometryDatabaseInterface; } + namespace Mesh { class MeshDatabaseInterface; } + typedef Geometry::GeometryDatabaseInterface GDBI; + typedef Mesh::MeshDatabaseInterface MDBI; +} namespace apf { +class Mesh2; +class Field; + /** - * \brief Creates an apf::Mesh from a CapStone mesh. - * - * \details This object should be destroyed by apf::destroyMesh. + * \brief Test for compiled Capstone library support. + * \return true if apf_cap was compiled with Capstone. otherwise all routines + * will call apf::fail. */ -Mesh2* createMesh(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb, pcu::PCU *PCUObj); +bool hasCAP() noexcept; /** - * \brief Casts a CapStone entity to an apf::MeshEntity. - * - * \details This does not create any objects, use freely. - */ -MeshEntity* castEntity(capEntity* entity); - -class TagCAP; - -class MeshCAP : public Mesh2 -{ - public: - MeshCAP(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb); - virtual ~MeshCAP(); - /* --------------------------------------------------------------------- */ - /* Category 00: General Mesh APIs */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - int getDimension(); - std::size_t count(int dimension); - Type getType(MeshEntity* e); - void verify(); - // OPTIONAL Member Functions // - void writeNative(const char* fileName); - void destroyNative(); - - /* --------------------------------------------------------------------- */ - /* Category 01: General Getters and Setters for vertex coordinates */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - void getPoint_(MeshEntity* e, int, Vector3& point); - void setPoint_(MeshEntity* e, int, Vector3 const& p); - void getParam(MeshEntity* e, Vector3& p); - void setParam(MeshEntity* e, Vector3 const& point); - - /* --------------------------------------------------------------------- */ - /* Category 02: Iterators */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - MeshIterator* begin(int dimension); - MeshEntity* iterate(MeshIterator* it); - void end(MeshIterator* it); - void increment(MeshIterator* it); - bool isDone(MeshIterator* it); - MeshEntity* deref(MeshIterator* it); - - /* --------------------------------------------------------------------- */ - /* Category 03: Adjacencies */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - void getAdjacent(MeshEntity* e, int dimension, Adjacent& adjacent); - int getDownward(MeshEntity* e, int dimension, MeshEntity** adjacent); - MeshEntity* getUpward(MeshEntity* e, int i); - bool hasUp(MeshEntity* e); - // OPTIONAL Member Functions // - bool hasAdjacency(int from_dim, int to_dim); - void createAdjacency(int from_dim, int to_dim); - void deleteAdjacency(int from_dim, int to_dim); - void getUp(MeshEntity* e, Up& up); - int countUpward(MeshEntity* e); - - /* --------------------------------------------------------------------- */ - /* Category 04: CAD model inquires */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - ModelEntity* toModel(MeshEntity* e); - // OPTIONAL Member Functions // - gmi_model* getModel(); - void setModel(gmi_model* newModel); - void setModelEntity(MeshEntity* e, ModelEntity* me); - - /* --------------------------------------------------------------------- */ - /* Category 05: Entity Creation/Deletion */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - MeshEntity* createVert_(ModelEntity* me); - MeshEntity* createEntity_(int type, ModelEntity* me, MeshEntity** down); - void destroy_(MeshEntity* e); - - /* --------------------------------------------------------------------- */ - /* Category 06: Attachable Data Functionality */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - MeshTag* createDoubleTag(const char* name, int size); - MeshTag* createIntTag(const char* name, int size); - MeshTag* createLongTag(const char* name, int size); - MeshTag* findTag(const char* name); - void destroyTag(MeshTag* t); - void renameTag(MeshTag* t, const char* name); - void getTags(DynamicArray& tags); - /* void getTag(MeshEntity* e, MeshTag* t, void* data); */ - /* void setTag(MeshEntity* e, MeshTag* t, void const* data); */ - void getDoubleTag(MeshEntity* e, MeshTag* tag, double* data); - void setDoubleTag(MeshEntity* e, MeshTag* tag, double const* data); - void getIntTag(MeshEntity* e, MeshTag* tag, int* data); - void setIntTag(MeshEntity* e, MeshTag* tag, int const* data); - void getLongTag(MeshEntity* e, MeshTag* tag, long* data); - void setLongTag(MeshEntity* e, MeshTag* tag, long const* data); - void removeTag(MeshEntity* e, MeshTag* t); - bool hasTag(MeshEntity* e, MeshTag* t); - int getTagType(MeshTag* t); - int getTagSize(MeshTag* t); - const char* getTagName(MeshTag* t); - unsigned getTagChecksum(MeshTag*,int); - - - /* --------------------------------------------------------------------- */ - /* Category 07: Distributed Meshes */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - bool isShared(MeshEntity* e); - bool isGhost(MeshEntity*) { return false; } - bool isGhosted(MeshEntity*) { return false; } - bool isOwned(MeshEntity* e); - int getOwner(MeshEntity* e); - void getRemotes(MeshEntity* e, Copies& remotes); - void getResidence(MeshEntity* e, Parts& residence); - int getId(); - void setResidence(MeshEntity*, Parts&) {} - void acceptChanges() {} - // OPTIONAL Member Functions // - void deleteGhost(MeshEntity*) {} - void addGhost(MeshEntity*, int, MeshEntity*) {} - int getGhosts(MeshEntity*, Copies&) { return 0; } - void migrate(Migration* plan); - void setRemotes(MeshEntity*, Copies&) {} - void addRemote(MeshEntity*, int, MeshEntity*) {} - void clearRemotes(MeshEntity*) {} - - - /* --------------------------------------------------------------------- */ - /* Category 08: Periodic Meshes */ - /* --------------------------------------------------------------------- */ - // REQUIRED Member Functions // - bool hasMatching() { return false; } - void getMatches(MeshEntity* e, Matches& m); - // OPTIONAL Member Functions // - void addMatch(MeshEntity*, int, MeshEntity*) {} - void clearMatches(MeshEntity*) {} - void clear_() {} - void getDgCopies(MeshEntity* e, DgCopies& dgCopies, ModelEntity* me); - + * \brief Create an apf::Mesh2 object from a Capstone mesh database. + * + * This object should be destroyed by apf::destroyMesh. Since Capstone meshes + * are serial only right now, PCUObj will not be directly used, but may have an + * impact on collective calls involving the mesh (e.g. a PCU::Add which uses + * Mesh::getPCU()). + * + * \param mdb A Capstone MeshDatabaseInterface with a current M_MModel. + * \param gdb A Capstone GeometryDatabaseInterface with a current M_GModel. + * \param PCUObj The PCU communicator to define the mesh over. + */ +Mesh2* createCapMesh( + CreateMG::MDBI* mdb, CreateMG::GDBI* gdb, pcu::PCU *PCUObj +); +/** + * \brief Create an apf::Mesh2 object from a mesh linked to a loaded + * geometry model. + * + * The gmi_model should be loaded previously by gmi_load (on a .cre file), + * gmi_cap_load, or gmi_cap_load_some. The list of acceptable values for + * meshname can be found by using gmi_cap_probe or direct Capstone interfaces. + * + * \param model A gmi_model associated with a Capstone geometry. + * \param meshname The name of a mesh associated with + * \return an apf::Mesh2 interface to the Capstone mesh. + */ +Mesh2* createCapMesh(gmi_model* model, const char* meshname, pcu::PCU* PCUObj); - MeshDatabaseInterface* getMesh() { return meshInterface; } - protected: - /* CapstoneModule capModule; */ - MeshDatabaseInterface* meshInterface; - GeometryDatabaseInterface *geomInterface; - /* AppContext *c; */ - int iterDim; - int d; - gmi_model* model; - std::vector tags; -}; +/** + * \brief Get native Capstone mesh database interface. + * \param capMesh Previously loaded capstone mesh. + * \return Underlying Capstone mesh database interface from capMesh. + */ +CreateMG::MDBI* getCapNative(Mesh2* capMesh); /** * \brief Test for smoothCAPAnisoSizes support. @@ -204,11 +94,12 @@ bool has_smoothCAPAnisoSizes(void) noexcept; * \param frames An apf::Field of apf::Matrix3x3 with orthogonal basis frames. * \param scales An apf::Field of apf::Vector3 with frame scales (eigenvalues). * \return A boolean indicating success. - * \pre m must be an apf::MeshCAP. + * \pre m must be a Capstone mesh. */ -bool smoothCAPAnisoSizes(apf::Mesh2* m, std::string analysis, - apf::Field* scales, apf::Field* frames); +bool smoothCAPAnisoSizes( + apf::Mesh2* m, std::string analysis, apf::Field* scales, apf::Field* frames +); }//namespace apf -#endif +#endif // APF_CAP_H diff --git a/test/cap2vtk.cc b/test/cap2vtk.cc index 1b2c696e7..93adb81aa 100644 --- a/test/cap2vtk.cc +++ b/test/cap2vtk.cc @@ -133,7 +133,7 @@ int main(int argc, char** argv) // convert the mesh to apf/mds mesh - apf::Mesh2* mesh = apf::createMesh(m,g,&PCUObj); + apf::Mesh2* mesh = apf::createCapMesh(m,g,&PCUObj); apf::writeVtkFiles(folderName, mesh); diff --git a/test/capAdapt.cc b/test/capAdapt.cc index b5c9b6aab..4e09ea591 100644 --- a/test/capAdapt.cc +++ b/test/capAdapt.cc @@ -34,12 +34,10 @@ #include #include "CapstoneModule.h" +#include "CreateMG_Framework_Mesh.h" #include "CreateMG_SizingMetricTool.h" using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; namespace { @@ -87,12 +85,12 @@ void decompose_metric(const v_double& metric, apf::Matrix3x3& frame, * \param sizFielName path to bulk sizing file. * */ std::vector loadSizing( - MeshDatabaseInterface *m, std::string mapFileName, std::string sizFileName + MDBI *m, std::string mapFileName, std::string sizFileName ); /** \brief Load Metric6 vector into APF frame and scale fields. */ void convertMetric6toAnisoFields( - MeshDatabaseInterface *m, const std::vector& sizing6, + MDBI *m, const std::vector& sizing6, apf::Field* frameField, apf::Field* scaleField ); @@ -173,7 +171,7 @@ int main(int argc, char** argv) { "Mesh Database : Create", "Attribution Database : Create" ); - MeshDatabaseInterface *m = cs.get_mesh(); + MDBI *m = cs.get_mesh(); PCU_ALWAYS_ASSERT(m); if (PCU.Self() == 0) @@ -298,7 +296,7 @@ void print_exception(const std::exception& e, int level) { } std::vector loadSizing( - MeshDatabaseInterface *m, std::string mapFileName, std::string sizFileName + MDBI *m, std::string mapFileName, std::string sizFileName ) { std::vector vmap; std::vector sizing; @@ -334,14 +332,14 @@ std::vector loadSizing( } void convertMetric6toAnisoFields( - MeshDatabaseInterface *m, const std::vector& sizing6, + MDBI *m, const std::vector& sizing6, apf::Field* frameField, apf::Field* scaleField ) { v_double sz(6); apf::Vector3 scalar; apf::Matrix3x3 frame; for (std::size_t i=0; iget_topo_by_id(Mesh::TOPO_VERTEX,i + 1); + M_MTopo mtopo = m->get_topo_by_id(CreateMG::Mesh::TOPO_VERTEX,i + 1); for (std::size_t j=0; j<6; j++) sz[j] = sizing6[i][j]; // field eigen values @@ -362,7 +360,7 @@ ma::Mesh* makeApfInterfaceWithSizing( const std::string& vmapFile, const std::string& sizingFile, bool smooth ) { // All ranks need apfCapMesh->getModel(). - ma::Mesh* apfCapMesh = apf::createMesh( + ma::Mesh* apfCapMesh = apf::createCapMesh( cs.get_mesh(), cs.get_geometry(), pcu.get() ); diff --git a/test/capGeomTest.cc b/test/capGeomTest.cc index 25f681fa2..eda350d48 100644 --- a/test/capGeomTest.cc +++ b/test/capGeomTest.cc @@ -118,7 +118,7 @@ int main(int argc, char** argv) gmi_register_cap(); - apf::Mesh2* mesh0 = apf::createMesh(m,g,&PCUObj); + apf::Mesh2* mesh0 = apf::createCapMesh(m,g,&PCUObj); apf::writeVtkFiles("mesh_no_param", mesh0); gmi_model* model = gmi_import_cap(g); @@ -147,7 +147,7 @@ int main(int argc, char** argv) printf("creating mesh with param field\n"); - apf::Mesh2* mesh = apf::createMesh(m,g,&PCUObj); + apf::Mesh2* mesh = apf::createCapMesh(m,g,&PCUObj); apf::Field* pf = apf::createFieldOn(mesh, "param_field", apf::VECTOR); apf::Field* idf = apf::createFieldOn(mesh, "id", apf::SCALAR); apf::MeshEntity* e; diff --git a/test/capVol.cc b/test/capVol.cc index 16017f501..9308724ff 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -10,6 +10,7 @@ // Mesh interfaces #include +#include #include #include #include @@ -21,10 +22,10 @@ // Mesh adapt #include +#include +#include + using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; #include "capVolSizeFields.h" @@ -118,9 +119,9 @@ int main(int argc, char** argv) { CapstoneModule cs("capVol", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); - GeometryDatabaseInterface *g = cs.get_geometry(); - MeshDatabaseInterface *m = cs.get_mesh(); - AppContext *c = cs.get_context(); + GDBI *g = cs.get_geometry(); + MDBI *m = cs.get_mesh(); + AppContext *c = cs.get_context(); PCU_ALWAYS_ASSERT(g); PCU_ALWAYS_ASSERT(m); @@ -159,7 +160,7 @@ int main(int argc, char** argv) { MG_API_CALL(m, compute_adjacency()); // Make APF adapter over Capstone mesh. - ma::Mesh* apfCapMesh = apf::createMesh(m, g, &PCUObj); + ma::Mesh* apfCapMesh = apf::createCapMesh(m, g, &PCUObj); apf::writeVtkFiles("core_capVol_cap.vtk", apfCapMesh); ma::Mesh* adaptMesh = apfCapMesh; @@ -243,7 +244,7 @@ int main(int argc, char** argv) { FACE2EDGE|FACE2VERTEX)); MG_API_CALL(m, set_reverse_states()); // MG_API_CALL(m, compute_adjacency()); // unnecessary because no elements? - ma::Mesh* newCapMesh = apf::createMesh(m, g, &PCUObj); + ma::Mesh* newCapMesh = apf::createCapMesh(m, g, &PCUObj); apf::convert(adaptMesh, newCapMesh); apf::writeVtkFiles("core_capVol_after_cap.vtk", newCapMesh); apf::destroyMesh(newCapMesh); diff --git a/test/cap_closestPoint.cc b/test/cap_closestPoint.cc index 4db2b59c2..39d746346 100644 --- a/test/cap_closestPoint.cc +++ b/test/cap_closestPoint.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -20,7 +21,9 @@ int main (int argc, char* argv[]) { "Mesh Database : Create", "Attribution Database : Create"); cs.load_files(v_string(1, creFile)); // 2. CreateMesh. - apf::Mesh2* m = apf::createMesh(cs.get_mesh(), cs.get_geometry(), &PCUObj); + apf::Mesh2* m = apf::createCapMesh( + cs.get_mesh(), cs.get_geometry(), &PCUObj + ); PCU_ALWAYS_ASSERT(m->canGetClosestPoint()); diff --git a/test/cap_inClosureOf.cc b/test/cap_inClosureOf.cc index 63ebb828b..05a042a82 100644 --- a/test/cap_inClosureOf.cc +++ b/test/cap_inClosureOf.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -20,7 +21,9 @@ int main (int argc, char* argv[]) { "Mesh Database : Create", "Attribution Database : Create"); cs.load_files(v_string(1, creFile)); // 2. CreateMesh. - apf::Mesh2* m = apf::createMesh(cs.get_mesh(), cs.get_geometry(), &PCUObj); + apf::Mesh2* m = apf::createCapMesh( + cs.get_mesh(), cs.get_geometry(), &PCUObj + ); // 3. Get region 1 ModelEntity*. apf::ModelEntity* rgn = m->findModelEntity(3, 1); PCU_ALWAYS_ASSERT(rgn); From 60326feca712a41f579108b0d5c829284a72db9f Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 19 Jun 2025 02:03:00 -0400 Subject: [PATCH 08/26] add Capstone functions and rewrite capVol - gmi_cap/gmi_cap.h: add gmi_cap_write. - gmi_cap/gmi_cap.cc (gmi_cap_write): add function to write native capstone CRE file. - apf_cap/apfCAP.h: add createCapMesh with no mesh name to load first Capstone mesh model. - remove trailing whitespace. - add generateCapMesh to generate meshes. - add makeEmptyCapMesh for copying. - add disownCapModel like disownMdsModel. - apf_cap/apfCAP.cc: add AppProcessor for Functions. - (MeshCAP): add setupAdjacencies helper and model ownership boolean. - (MeshCAP::setupAdjacencies): always setup complete adjacencies. - (MeshCAP::MeshCAP): use setupAdjacencies. - (MeshCAP::~MeshCAP): only destroy model if owned. - add new CreateCapMesh, makeEmptyMesh, generateCapMesh, disownCapModel function definitions. - test/capVol: reorganize #includes. - use exceptions and add new Args class. - add extensive help text with raw string literal. - refactor main into functions. - add before/after vtk flags. - add flag to force splitter/balancer. - test/testing.cmake: reduce capVol test cases without new flag. Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.cc | 99 ++++++++- apf_cap/apfCAP.h | 51 ++++- gmi_cap/gmi_cap.cc | 11 + gmi_cap/gmi_cap.h | 2 + test/capVol.cc | 500 ++++++++++++++++++++++++++------------------- test/testing.cmake | 45 ++-- 6 files changed, 475 insertions(+), 233 deletions(-) diff --git a/apf_cap/apfCAP.cc b/apf_cap/apfCAP.cc index 1e8022e44..e9f09a871 100644 --- a/apf_cap/apfCAP.cc +++ b/apf_cap/apfCAP.cc @@ -13,6 +13,8 @@ #include #include +#include +#include #include #ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL #include @@ -226,19 +228,31 @@ class MeshCAP : public Mesh2 void clear_() {} void getDgCopies(MeshEntity* e, DgCopies& dgCopies, ModelEntity* me); - MDBI* getMesh() { return meshInterface; } - protected: - gmi_model* model; + MDBI* getMesh() noexcept { return meshInterface; } + void disownModel() noexcept { ownsModel = false; } + private: + void setupAdjacencies(); + gmi_model* model{nullptr}; MDBIP meshOwner; MDBI* meshInterface; int d; + bool ownsModel{true}; std::vector tags; }; +void MeshCAP::setupAdjacencies() { + PCU_ALWAYS_ASSERT(meshInterface); + MG_API_CALL(meshInterface, get_dimension(d)); + MG_API_CALL(meshInterface, set_adjacency_state( + REGION2FACE|REGION2EDGE|REGION2VERTEX| FACE2EDGE|FACE2VERTEX + )); + MG_API_CALL(meshInterface, set_reverse_states()); + MG_API_CALL(meshInterface, compute_adjacency()); +} + MeshCAP::MeshCAP(MDBI* mdb, GDBI* gdb): meshInterface(mdb) { model = gmi_import_cap(gdb); - PCU_ALWAYS_ASSERT(mdb); - MG_API_CALL(mdb, get_dimension(d)); + setupAdjacencies(); } MeshCAP::MeshCAP(gmi_model* mdl, MDBIP mdb): @@ -246,12 +260,12 @@ MeshCAP::MeshCAP(gmi_model* mdl, MDBIP mdb): { PCU_ALWAYS_ASSERT(mdl); PCU_ALWAYS_ASSERT(mdb.is_valid()); - MG_API_CALL(mdb.get(), get_dimension(d)); + setupAdjacencies(); } MeshCAP::~MeshCAP() { - if (model) gmi_destroy(model); + if (ownsModel && model) gmi_destroy(model); model = nullptr; meshInterface = nullptr; } @@ -1092,6 +1106,77 @@ Mesh2* createCapMesh( return m; } +Mesh2* createCapMesh(gmi_model* model, pcu::PCU* PCUObj) { + GDBI* gdb = gmi_export_cap(model); + AppContext* ctx = gdb->get_context(); + MDBIP mdp = get_context_mesh_database_interface(ctx); + M_MModel mmodel; + MG_API_CALL(mdp.get(), get_model_by_index(0, mmodel)); + MG_API_CALL(mdp.get(), set_current_model(mmodel)); + MeshCAP* m = new MeshCAP(model, mdp); + m->init(getLagrange(1), PCUObj); + return m; +} + +Mesh2* makeEmptyCapMesh( + gmi_model* model, const char* meshname, pcu::PCU* PCUObj +) { + PCU_ALWAYS_ASSERT(model); + GDBI* gdb = gmi_export_cap(model); + M_GModel gmodel; + MG_API_CALL(gdb, get_current_model(gmodel)); + AppContext* ctx = gdb->get_context(); + MDBIP mdp = get_context_mesh_database_interface(ctx); + M_MModel mmodel; + MG_API_CALL(mdp.get(), create_associated_model(mmodel, gmodel, meshname)); + MeshCAP* m = new MeshCAP(model, mdp); + m->init(getLagrange(1), PCUObj); + return m; +} + +Mesh2* generateCapMesh( + gmi_model* model, int dimension, pcu::PCU* PCUObj +) { + PCU_ALWAYS_ASSERT(model); + PCU_ALWAYS_ASSERT(0 <= dimension && dimension <= 3); + auto gdb = gmi_export_cap(model); + auto ctx = gdb->get_context(); + FunctionPtr fn; + switch (dimension) { + case 0: + fn = FunctionPtr(get_function(ctx, "GenerateVertexMesh")); + break; + case 1: + fn = FunctionPtr(get_function(ctx, "GenerateEdgeMesh")); + break; + case 2: + fn = FunctionPtr(get_function(ctx, "GenerateFaceMesh")); + break; + case 3: + fn = FunctionPtr(get_function(ctx, "GenerateRegionMesh")); + break; + } + M_GModel gmodel; + MG_API_CALL(gdb, get_current_model(gmodel)); + set_input(fn, "Model", gmodel); + auto proc = get_context_processor(ctx); + if (proc->execute(fn) != STATUS_OK) { + fail("gmi_cap_gen_mesh: failed to mesh the model"); + } + M_MModel mmodel; + get_output(fn, "MeshModel", mmodel); + auto mdp = get_context_mesh_database_interface(ctx, mmodel); + MeshCAP* m = new MeshCAP(model, mdp); + m->init(getLagrange(1), PCUObj); + return m; +} + +void disownCapModel(Mesh2* capMesh) { + auto m = dynamic_cast(capMesh); + if (!m) fail("disownCapModel: not a Capstone mesh"); + m->disownModel(); +} + bool has_smoothCAPAnisoSizes(void) noexcept { #ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL return true; diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index 1b4d4d675..a0e15c861 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -53,7 +53,19 @@ Mesh2* createCapMesh( ); /** - * \brief Create an apf::Mesh2 object from a mesh linked to a loaded + * \brief Create an apf::Mesh2 object from the first mesh linked to a loaded + * geometry model. + * + * The gmi_model should be loaded previously by gmi_load (on a .cre file), + * gmi_cap_load, or gmi_cap_load_some. Try to load the first mesh (by index). + * + * \param model A gmi_model associated with a Capstone geometry. + * \return an apf::Mesh2 interface to the Capstone mesh. + */ +Mesh2* createCapMesh(gmi_model* model, pcu::PCU* PCUObj); + +/** + * \brief Create an apf::Mesh2 object from a mesh linked to a loaded * geometry model. * * The gmi_model should be loaded previously by gmi_load (on a .cre file), @@ -61,11 +73,46 @@ Mesh2* createCapMesh( * meshname can be found by using gmi_cap_probe or direct Capstone interfaces. * * \param model A gmi_model associated with a Capstone geometry. - * \param meshname The name of a mesh associated with + * \param meshname The name of a mesh associated with * \return an apf::Mesh2 interface to the Capstone mesh. */ Mesh2* createCapMesh(gmi_model* model, const char* meshname, pcu::PCU* PCUObj); +/** + * \brief Generate Capstone mesh object on a model linked to a Capstone model. + * + * \param model A Capstone GMI model to generate a mesh on. + * \param meshname Name for the new generated mesh. + * \param PCUObj The PCU object to link to the mesh. + * \return An apf::Mesh2 object with the mesh. + * \bug dimension != 3 is not supported. + */ +Mesh2* generateCapMesh( + gmi_model* model, int dimension, pcu::PCU* PCUObj +); + +/** + * \brief Make an empty Capstone M_MModel associated with the model. + * + * \param model Previously loaded Capstone gmi_model. + * \param meshname Name for new mesh in the Capstone database. + * \param PCUObj The PCU object to associate the new apf::Mesh2 with. + * \return a new apf::Mesh2 object. + */ +Mesh2* makeEmptyCapMesh( + gmi_model* model, const char* meshname, pcu::PCU* PCUObj +); + +/** + * Disown capMesh's gmi_model. + * + * Mark the gmi_model as non-owned so that the destructor does not call + * gmi_destroy. + * + * \param capMesh A Capstone mesh wrapper. + */ +void disownCapModel(Mesh2* capMesh); + /** * \brief Get native Capstone mesh database interface. * \param capMesh Previously loaded capstone mesh. diff --git a/gmi_cap/gmi_cap.cc b/gmi_cap/gmi_cap.cc index 06873304e..6dfbcfee9 100644 --- a/gmi_cap/gmi_cap.cc +++ b/gmi_cap/gmi_cap.cc @@ -494,6 +494,17 @@ struct gmi_model* gmi_cap_load_some( return owned_import(cs_module->get_geometry(), gmodel); } +void gmi_cap_write(struct gmi_model* model, const char* creFileName) { + cap_model* cm = reinterpret_cast(model); + auto ctx = cm->geomInterface->get_context(); + FunctionPtr fn(get_function(ctx, "SaveCreateData")); + set_input(fn, "Model", cm->gmodel); + set_input(fn, "FileName", creFileName); + auto proc = get_context_processor(ctx); + if (proc->execute(fn) != STATUS_OK) + gmi_fail("gmi_cap_write: failed to write the model"); +} + gmi_model* gmi_import_cap(GDBI* gi) { M_GModel gmodel; MG_API_CALL(gi, get_current_model(gmodel)); diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index c98d3870a..bea899ff1 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -80,6 +80,8 @@ void gmi_register_cap(void); */ struct gmi_model* gmi_cap_load(const char* creFileName); +void gmi_cap_write(struct gmi_model* model, const char* creFileName); + #ifdef __cplusplus void gmi_cap_probe( diff --git a/test/capVol.cc b/test/capVol.cc index 9308724ff..5f00a6596 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -1,26 +1,21 @@ -#include -#include +#include +#include -// Output -#include - -// Parallelism #include -#include - -// Mesh interfaces #include -#include #include -#include #include - -// Geometry interfaces +#include +#include +#include +#include +#include #include #include - -// Mesh adapt +#include #include +#include +#include #include #include @@ -31,244 +26,333 @@ using namespace CreateMG; namespace { -void myExit(int exit_code = EXIT_SUCCESS) { - gmi_cap_stop(); - pcu::Finalize(); - exit(exit_code); +/** \brief Print nested exceptions. */ +void print_exception( + const pcu::PCU& PCU, const std::exception& e, int level = 0 +); + +struct Args { + Args() {} + void parse(int argc, char* argv[]); + static void print_usage(const char* argv0); + std::string before, after, in, out; + bool analytic{false}, volume{false}, mds{false}; + int sf{0}; + enum Partitioner { Default, Zoltan, METIS, Parma } splitter, balancer; +}; + +void Args::print_usage(const char *argv0) { + std::cout << "USAGE: " << argv0 << " [-agm] [-B BEFORE.VTK] [-A AFTER.VTK] " + "[-s SPLITTER] [-b BALANCER] " << std::endl; + std::cout << R"help( +Flags: +-B BEFORE.VTK Write the file BEFORE.VTK before adaptation with the + adapt_frames and adapt_scales fields. +-A AFTER.VTK Write the file AFTER.VTK after adaptation. +-a Evaluate size-field analytically during adaptation. The default + is to evaluate once, write the frames/scales, then transfer and + interpolate during adaptation. +-g Force mesh volume generation. +-m Convert mesh to MDS during adaptation (required for parallel + adaptation). +-s SPLITTER Force the selected splitter to be used. Possible values are: + Zoltan, METIS, Parma. The default is to use whatever is + available, in the order listed previously. +-b BALANCER Force the selected balancer to be used during adaptation. + Possible values and default priorities are the same as for + splitter selection. Using this option forces load-balancing + after each step, whereas the default only balances when the + estimated imbalance exceeds a threshold (10%%). + +SIZE-FIELDS: +1, for uniform anisotropic size-field +2, for wing-shock size-field +3, for cube-shock size-field +4, for cylinder boundary-layer size-field +)help"; } -void printUsage(char *argv0) { - printf("USAGE: %s [-agwvm] \n", argv0); - printf("Flags:\n" - "-a\tEvaluate size-field analytically.\n" - "-g\tForce mesh generation.\n" - "-v\tEnable verbose output.\n" - "-w\tWrite before.vtk, after.vtk, and after.cre.\n" - "-m\tConvert mesh to MDS during adaptation.\n" - "SIZE-FIELDS:\n" - "%d, for uniform anisotropic size-field\n" - "%d, for wing-shock size-field\n" - "%d, for cube-shock size-field\n" - "%d, for cylinder boundary-layer size-field\n", 1, 2, 3, 4); -} +ma::Mesh* loadAdaptMesh( + pcu::PCU* PCU, gmi_model* model, bool volume, bool mds +); + +void parallelAdapt( + pcu::PCU* PCU, gmi_model* model, apf::Mesh2* mesh, const Args& args +); } // namespace int main(int argc, char** argv) { + lion_set_verbosity(1); // Initialize logging. + int retval = 0; // Initialize parallelism. pcu::Init(&argc, &argv); { pcu::PCU PCUObj; + try { + // Check arguments or print usage. + Args args; + try { + args.parse(argc, argv); + } catch (const std::exception& e) { + if (PCUObj.Self() == 0) args.print_usage(argv[0]); + std::throw_with_nested( + std::runtime_error("invalid command line arguments") + ); + } - // Initialize logging. - lion_set_verbosity(1); - lion_set_stdout(stdout); - lion_set_stderr(stderr); - - // Check arguments or print usage. - if (argc < 3) { - if (PCUObj.Self() == 0) { - printUsage(argv[0]); + if (PCUObj.Peers() > 1 && !args.mds) + throw std::runtime_error("parallel run without -m flag"); + + // Initialize GMI. + gmi_cap_start(); + gmi_register_cap(); + + try { + gmi_model* capGeomModel = nullptr; + if (PCUObj.Self() == 0) capGeomModel = gmi_cap_load(args.in.c_str()); + else capGeomModel = gmi_cap_load_some(args.in.c_str(), {}); + auto soloPCU = PCUObj.Split(PCUObj.Self(), 0); + ma::Mesh* mesh = nullptr; + if (PCUObj.Self() == 0) { + mesh = loadAdaptMesh( + soloPCU.get(), capGeomModel, args.volume, args.mds + ); + // APF default routine will typically fail to verify surface meshes. + if (args.volume) mesh->verify(); + } + parallelAdapt(&PCUObj, capGeomModel, mesh, args); + if (PCUObj.Self() == 0) { + if (args.volume) mesh->verify(); + if (args.mds) { + apf::Mesh2* mdsMesh = mesh; + mesh = apf::makeEmptyCapMesh( + capGeomModel, "MeshAdapt", soloPCU.get() + ); + apf::disownCapModel(mesh); + apf::convert(mdsMesh, mesh); + apf::destroyMesh(mdsMesh); + } + gmi_cap_write(capGeomModel, args.out.c_str()); + apf::destroyMesh(mesh); + } + gmi_destroy(capGeomModel); + gmi_cap_stop(); + } catch(...) { + gmi_cap_stop(); + std::rethrow_exception(std::current_exception()); } - myExit(EXIT_FAILURE); + } catch (const std::exception& e) { + if (PCUObj.Self() == 0) std::cerr << "ERROR: "; + print_exception(PCUObj, e); + retval = 1; + } catch(...) { + if (PCUObj.Self() == 0) + std::cerr << "ERROR: unspecified error" << std::endl; + retval = 1; } + } // PCUObj scope + pcu::Finalize(); + return retval; +} - // Parse arguments. - bool volume_flag = false, write_flag = false, analytic_flag = false, - verbose_flag = false, mds_flag = false; - for (int i = 1; i < argc - 2; ++i) { +namespace { + +void print_exception(const pcu::PCU& PCU, const std::exception& e, int level) { + if (PCU.Self() == 0) + std::cerr << std::string(level * 2, ' ') << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch (const std::exception& nestedE) { + print_exception(PCU, nestedE, level + 1); + } catch (...) {} +} + +void Args::parse(int argc, char* argv[]) { + constexpr int positional_args = 3; + if (argc < 1 + positional_args) + throw std::runtime_error("missing positional argument(s)"); + auto strarg = [argc, argv](int& i, int& j) -> std::string { + if (argv[i][j + 1]) { + int oldj = j; + while (argv[i][j + 1] != '\0') ++j; + return &argv[i][oldj + 1]; + } else if (i + 1 < argc - positional_args) { + ++i; + for (j = 0; argv[i][j + 1] != '\0'; ++j); + return argv[i]; + } else throw std::runtime_error("missing argument for -" + argv[i][j]); + }; + auto strlower = [](std::string str) { + for (auto& c : str) c = std::tolower(c); + return str; + }; + auto str2part = [strlower](std::string str) -> Partitioner { + str = strlower(str); + if (str == "metis") return METIS; + if (str == "parma") return Parma; + if (str == "zoltan") return Zoltan; + throw std::invalid_argument("invalid partitioner name: " + str); + }; + int i; + for (i = 1; i < argc - positional_args; ++i) { if (*argv[i] == '-') { for (int j = 1; argv[i][j] != '\0'; ++j) { switch(argv[i][j]) { - case 'a': - analytic_flag = true; - break; - case 'g': - volume_flag = true; - break; - case 'v': - verbose_flag = true; - lion_set_verbosity(1); - break; - case 'w': - write_flag = true; - break; - case 'm': - mds_flag = true; - break; - default: - printf("Error: invalid flag.\n"); - printUsage(argv[0]); - myExit(EXIT_FAILURE); + case 'A': after = strarg(i, j); break; + case 'B': before = strarg(i, j); break; + case 'a': analytic = true; break; + case 'g': volume = true; break; + case 'm': mds = true; break; + case 's': splitter = str2part(strarg(i, j)); break; + case 'b': balancer = str2part(strarg(i, j)); break; + default: throw std::runtime_error("unrecognized flag: -" + argv[i][j]); } } - } + } else break; } + sf = std::atoi(argv[i]); + in = argv[i + 1]; + out = argv[i + 2]; +} - const char* createFileName = argv[argc - 1]; - int mode = atoi(argv[argc - 2]); - - // Initialize GMI. - gmi_cap_start(); - gmi_register_cap(); - // create an instance of the Capstone Module activating SMLIB/CREATE/CREATE - // for the Geometry/Mesh/Attribution databases - const std::string gdbName("Geometry Database : SMLIB"); - const std::string mdbName("Mesh Database : Create"); - const std::string adbName("Attribution Database : Create"); - - CapstoneModule cs("capVol", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); - - GDBI *g = cs.get_geometry(); - MDBI *m = cs.get_mesh(); - AppContext *c = cs.get_context(); - - PCU_ALWAYS_ASSERT(g); - PCU_ALWAYS_ASSERT(m); - PCU_ALWAYS_ASSERT(c); - - // Load Capstone mesh. - v_string filenames; - filenames.push_back(createFileName); - M_GModel gmodel = cs.load_files(filenames); +ma::Mesh* loadAdaptMesh( + pcu::PCU* PCU, gmi_model* model, bool volume, bool mds +) { + // Load Capstone mesh (with optional volume generation). + ma::Mesh* capMesh = nullptr; + if (volume) { + int dim = 3; + capMesh = apf::generateCapMesh(model, dim, PCU); + // FIXME: maybe create copy of mesh model to work on (preserve original). + } else capMesh = apf::createCapMesh(model, PCU); + apf::disownCapModel(capMesh); + // Optionally convert to MDS + ma::Mesh* adaptMesh = nullptr; + if (mds) { + adaptMesh = apf::createMdsMesh(model, capMesh, true); + apf::disownMdsModel(adaptMesh); // Model is managed in main. + apf::destroyMesh(capMesh); + } else adaptMesh = capMesh; + return adaptMesh; +} - M_MModel mmodel; - if (volume_flag) { - mmodel = cs.generate_mesh(); - if (mmodel.is_invalid()) { - lion_eprint(1, "FATAL: Failed to mesh the model.\n"); - myExit(EXIT_FAILURE); - } - MG_API_CALL(m, set_current_model(mmodel)); - } else { - // Use the first existing mesh model. - std::vector mmodels; - MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); - PCU_ALWAYS_ASSERT(mmodels.size() == 1); - mmodel = mmodels[0]; - MG_API_CALL(m, set_current_model(mmodel)); +apf::Splitter* makeSplitter(Args::Partitioner ptnr, apf::Mesh2* mesh) { + switch (ptnr) { + case Args::Zoltan: + return apf::makeZoltanSplitter(mesh, apf::GRAPH, apf::PARTITION); + case Args::METIS: return apf::makeMETISsplitter(mesh); + case Args::Parma: return Parma_MakeRibSplitter(mesh); + default: + #if defined(PUMI_HAS_ZOLTAN) + return apf::makeZoltanSplitter( + adaptMesh, apf::GRAPH, apf::PARTITION + ); + #elif defined(PUMI_HAS_METIS) + return apf::makeMETISsplitter(mesh); + #else + return Parma_MakeRibSplitter(mesh); + #endif } +} - if (write_flag) { - cs.save_file("core_capVol_before.cre", gmodel); +ma::AnisotropicFunction* makeUDF(int mode, ma::Mesh* mesh) { + switch (mode) { + case 1: return new UniformAniso(mesh); + case 2: return new WingShock(mesh, 50); + case 3: return new Shock(mesh); + case 4: return new CylBoundaryLayer(mesh); + default: throw std::runtime_error("invalid size-field"); } +} - // Calculate adjacencies. - MG_API_CALL(m, set_adjacency_state(REGION2FACE|REGION2EDGE|REGION2VERTEX| - FACE2EDGE|FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - MG_API_CALL(m, compute_adjacency()); - - // Make APF adapter over Capstone mesh. - ma::Mesh* apfCapMesh = apf::createCapMesh(m, g, &PCUObj); - apf::writeVtkFiles("core_capVol_cap.vtk", apfCapMesh); +void migrateHome(apf::Mesh2* mesh) { + auto t0 = pcu::Time(); + apf::Migration* plan = new apf::Migration(mesh); + apf::MeshIterator* it = mesh->begin(mesh->getDimension()); + for (apf::MeshEntity* e; (e = mesh->iterate(it));) plan->send(e, 0); + mesh->end(it); + mesh->migrate(plan); // destroys plan + auto map0 = apf::Multiply(0); + apf::remapPartition(mesh, map0); + auto t1 = pcu::Time(); + if (mesh->getPCU()->Self() == 0) + std::cout << "INFO: Migrated home in " << t1 - t0 << " seconds" + << std::endl; +} - ma::Mesh* adaptMesh = apfCapMesh; - if (mds_flag) { - adaptMesh = apf::createMdsMesh(apfCapMesh->getModel(), apfCapMesh); - apf::reorderMdsMesh(adaptMesh); - // APF default routine will typically fail to verify surface meshes. - if (volume_flag) adaptMesh->verify(); - if (write_flag) { - apf::writeVtkFiles("core_capVol_mds.vtk", adaptMesh); +void parallelAdapt( + pcu::PCU* PCU, gmi_model* model, apf::Mesh2* mesh, const Args& args +) { + pcu::PCU* oldPCU = nullptr; + if (mesh) oldPCU = mesh->getPCU(); + if (PCU->Peers() > 1) { + apf::Migration* plan = nullptr; + if (PCU->Self() == 0) { + apf::Splitter* splitter = makeSplitter(args.splitter, mesh); + apf::MeshTag* weights = Parma_WeighByMemory(mesh); + plan = splitter->split(weights, 1.10, PCU->Peers()); + apf::removeTagFromDimension( + mesh, weights, mesh->getDimension() + ); + mesh->destroyTag(weights); + delete splitter; + mesh->switchPCU(PCU); } - apfCapMesh->setModel(nullptr); // Disown the model. - delete apfCapMesh; - apfCapMesh = nullptr; + mesh = apf::repeatMdsMesh(mesh, model, plan, PCU->Peers(), PCU); + apf::disownMdsModel(mesh); } - // Choose appropriate size-field. - ma::AnisotropicFunction* sf = nullptr; - switch (mode) { - case 1: - sf = new UniformAniso(adaptMesh); - break; - case 2: - sf = new WingShock(adaptMesh, 50); - break; - case 3: - sf = new Shock(adaptMesh); - break; - case 4: - sf = new CylBoundaryLayer(adaptMesh); - break; - default: - lion_eprint(1, "FATAL: Invalid size-field.\n"); - myExit(EXIT_FAILURE); - } + std::unique_ptr sf(makeUDF(args.sf, mesh)); // Make pumi fields for the frames and scales for anisotropic size-fields. apf::Field* frameField = nullptr; apf::Field* scaleField = nullptr; - ma::Input *in = nullptr; - if (!analytic_flag || write_flag) { - frameField = apf::createFieldOn(adaptMesh, "adapt_frames", apf::MATRIX); - scaleField = apf::createFieldOn(adaptMesh, "adapt_scales", apf::VECTOR); - - ma::Entity *v; - ma::Iterator* it = adaptMesh->begin(0); - while( (v = adaptMesh->iterate(it)) ) { + if (!args.analytic || !args.before.empty()) { + frameField = apf::createFieldOn(mesh, "adapt_frames", apf::MATRIX); + scaleField = apf::createFieldOn(mesh, "adapt_scales", apf::VECTOR); + apf::MeshIterator* it = mesh->begin(0); + for (apf::MeshEntity* v; (v = mesh->iterate(it));) { ma::Vector s; ma::Matrix f; sf->getValue(v, f, s); apf::setVector(scaleField, v, 0, s); apf::setMatrix(frameField, v, 0, f); } - adaptMesh->end(it); + mesh->end(it); - if (write_flag) { - apf::writeVtkFiles("core_capVol_before.vtk", adaptMesh); + if (!args.before.empty()) apf::writeVtkFiles(args.before.c_str(), mesh); + if (args.analytic) { // Cleanup if fields were only for before.vtk + apf::destroyField(frameField); + apf::destroyField(scaleField); + frameField = scaleField = nullptr; } } - if (!analytic_flag) { - // Pass the field data. - in = ma::makeAdvanced(ma::configure(adaptMesh, scaleField, frameField)); - } else { - // Pass the function. - in = ma::makeAdvanced(ma::configure(adaptMesh, sf)); + ma::Input *in = nullptr; + if (args.analytic) in = ma::makeAdvanced(ma::configure(mesh, sf.get())); + else in = ma::makeAdvanced(ma::configure(mesh, scaleField, frameField)); + switch (args.balancer) { + case Args::Zoltan: + in->shouldRunPreZoltan = in->shouldRunMidZoltan = true; + in->shouldRunPostZoltan = true; + break; + case Args::METIS: + in->shouldRunPreMetis = in->shouldRunMidMetis = true; + in->shouldRunPostMetis = true; + break; + case Args::Parma: + in->shouldRunPreParma = in->shouldRunMidParma = true; + in->shouldRunPostParma = true; + break; + default:; } ma::adapt(in); - - if (volume_flag) { - // We can't verify surface meshes. - adaptMesh->verify(); - } - - if (write_flag) { - if (mds_flag) { - apf::writeVtkFiles("core_capVol_after_mds.vtk", adaptMesh); - MG_API_CALL(m, create_associated_model(mmodel, gmodel, "MeshAdapt")); - MG_API_CALL(m, set_adjacency_state(REGION2FACE|REGION2EDGE|REGION2VERTEX| - FACE2EDGE|FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - // MG_API_CALL(m, compute_adjacency()); // unnecessary because no elements? - ma::Mesh* newCapMesh = apf::createCapMesh(m, g, &PCUObj); - apf::convert(adaptMesh, newCapMesh); - apf::writeVtkFiles("core_capVol_after_cap.vtk", newCapMesh); - apf::destroyMesh(newCapMesh); - } else { - apf::writeVtkFiles("core_capVol_after_cap.vtk", adaptMesh); - } - cs.save_file("core_capVol_after.cre", gmodel); - } - - /* PRINT ADAPTED MESH INFO */ - if (verbose_flag) { - M_MModel mmdl; - m->get_current_model(mmdl); - std::string info; - m->print_info(mmdl, info); - lion_oprint(1, "%s", info.c_str()); - } - - // Clean up. - apf::destroyMesh(adaptMesh); - delete sf; - - // Exit calls. - gmi_cap_stop(); - } - pcu::Finalize(); + if (!args.after.empty()) apf::writeVtkFiles(args.after.c_str(), mesh); + if (PCU->Peers() > 1) migrateHome(mesh); + if (PCU->Self() != 0) apf::destroyMesh(mesh); + else mesh->switchPCU(oldPCU); } + +} // namespace diff --git a/test/testing.cmake b/test/testing.cmake index 86eab8143..95b8e2c81 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -979,22 +979,35 @@ if (PCU_COMPRESS) endif() if(ENABLE_CAPSTONE) - mpi_test(capCyl 1 ./capVol -v 1 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capWing 1 ./capVol -v 2 ${MESHES}/cap/wing_surf_only.cre) - mpi_test(capCube 1 ./capVol -v 3 ${MESHES}/cap/cube_surf_only.cre) - mpi_test(capCyl2 1 ./capVol -v 4 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capVolCyl 1 ./capVol -vg 1 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capVolWing 1 ./capVol -vg 2 ${MESHES}/cap/wing_surf_only.cre) - mpi_test(capVolCube 1 ./capVol -vg 3 ${MESHES}/cap/cube_surf_only.cre) - mpi_test(capVolCyl2 1 ./capVol -vg 4 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capCylMds 1 ./capVol -vm 1 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capWingMds 1 ./capVol -vm 2 ${MESHES}/cap/wing_surf_only.cre) - mpi_test(capCubeMds 1 ./capVol -vm 3 ${MESHES}/cap/cube_surf_only.cre) - mpi_test(capCyl2Mds 1 ./capVol -vm 4 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capVolCylMds 1 ./capVol -vgm 1 ${MESHES}/cap/cyl_surf_only.cre) - mpi_test(capVolWingMds 1 ./capVol -vgm 2 ${MESHES}/cap/wing_surf_only.cre) - mpi_test(capVolCubeMds 1 ./capVol -vgm 3 ${MESHES}/cap/cube_surf_only.cre) - mpi_test(capVolCyl2Mds 1 ./capVol -vgm 4 ${MESHES}/cap/cyl_surf_only.cre) + set(MDIR "${MESHES}/cap") + mpi_test(capVol_Wing 1 ./capVol 2 ${MDIR}/wing_surf_only.cre capVol_Wing.cre + ) + mpi_test(capVol_WingMds 1 + ./capVol -m 2 ${MDIR}/wing_surf_only.cre capVol_WingMds.cre + ) + mpi_test(capVol_Cube3D 1 + ./capVol -g 3 ${MDIR}/cube_surf_only.cre capVol_Cube3D.cre + ) + mpi_test(capVol_BLCyl3D 1 + ./capVol -g 4 ${MDIR}/cyl_surf_only.cre capVol_BLCyl3D.cre + ) + mpi_test(capVol_CubeMds3D 1 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + mpi_test(capVol_BLCylMds3D 1 + ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre + ) + if(PUMI_TEST_CAPVOL_FULLMATRIX) + mpi_test(capVol_Cyl3D 1 ./capVol -g 1 ${MDIR}/cyl_surf_only.cre out.cre) + mpi_test(capVol_Cube3D 1 ./capVol -g 3 ${MDIR}/cube_surf_only.cre out.cre) + mpi_test(capVol_BLCyl3D 1 ./capVol -g 4 ${MDIR}/cyl_surf_only.cre out.cre) + mpi_test(capVol_CylMds 1 ./capVol -m 1 ${MDIR}/cyl_surf_only.cre out.cre) + mpi_test(capVol_CubeMds 1 ./capVol -m 3 ${MDIR}/cube_surf_only.cre out.cre) + mpi_test(capVol_BLCylMds 1 ./capVol -m 4 ${MDIR}/cyl_surf_only.cre out.cre) + mpi_test(capVol_CylMds3D 1 ./capVol -gm 1 ${MDIR}/cyl_surf_only.cre out.cre) + mpi_test(capVol_Wing3D 1 ./capVol -g 2 ${MDIR}/wing_surf_only.cre out.cre) + mpi_test(capVol_WingMds3D 1 ./capVol -gm 2 ${MDIR}/wing_surf_only.cre out.cre) + endif() mpi_test(cap_inClosureOf 1 ./cap_inClosureOf ${MESHES}/cap/cube_surf_only.cre) mpi_test(cap_closestPoint 1 ./cap_closestPoint ${MESHES}/cap/cube_surf_only.cre) if(HAVE_CAPSTONE_SIZINGMETRICTOOL) From a713124b417bd39bb984d9579e67e58294943eab Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 23 Jun 2025 19:58:46 -0400 Subject: [PATCH 09/26] capVol: use ma::getBoundingBox - test/capVolSizeFields.h (CylBoundaryLayer): use ma::getBoundingBox which does PCU::Max. Signed-off-by: Aiden Woodruff --- test/capVolSizeFields.h | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/test/capVolSizeFields.h b/test/capVolSizeFields.h index eeb5b13f1..e36339024 100644 --- a/test/capVolSizeFields.h +++ b/test/capVolSizeFields.h @@ -116,20 +116,7 @@ class CylBoundaryLayer : public ma::AnisotropicFunction { CylBoundaryLayer(ma::Mesh* mesh): m(mesh) { ma::Vector bmin(HUGE_VAL, HUGE_VAL, HUGE_VAL), bmax(-HUGE_VAL, -HUGE_VAL, -HUGE_VAL); - ma::Iterator *it = m->begin(0); - ma::Entity *e; - while ((e = m->iterate(it))) { - ma::Vector p = ma::getPosition(m, e); - - if (p.x() < bmin.x()) bmin.x() = p.x(); - if (p.x() > bmax.x()) bmax.x() = p.x(); - if (p.y() < bmin.y()) bmin.y() = p.y(); - if (p.y() > bmax.y()) bmax.y() = p.y(); - if (p.z() < bmin.z()) bmin.z() = p.z(); - if (p.z() > bmax.z()) bmax.z() = p.z(); - } - m->end(it); - + ma::getBoundingBox(mesh, bmin, bmax); ma::Vector len = bmax - bmin; if (len.x() > len.y() && len.x() > len.z()) { lion_oprint(1, "Shock2 along X axis.\n"); From d92206b8474b1d1279d7baa08165116348d92a37 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 23 Jun 2025 19:59:28 -0400 Subject: [PATCH 10/26] capVol: add parallel test cases - test/testing.cmake: add parallel capVol cases. Signed-off-by: Aiden Woodruff --- test/testing.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/testing.cmake b/test/testing.cmake index 95b8e2c81..3d154ab01 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -997,6 +997,12 @@ if(ENABLE_CAPSTONE) mpi_test(capVol_BLCylMds3D 1 ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre ) + mpi_test(capVol_CubeMds3D-4 4 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + mpi_test(capVol_BLCylMds3D-4 4 + ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre + ) if(PUMI_TEST_CAPVOL_FULLMATRIX) mpi_test(capVol_Cyl3D 1 ./capVol -g 1 ${MDIR}/cyl_surf_only.cre out.cre) mpi_test(capVol_Cube3D 1 ./capVol -g 3 ${MDIR}/cube_surf_only.cre out.cre) From 87a142b7b141b2ce743d0adaeca5a1cb7ff2742e Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Wed, 25 Jun 2025 16:12:51 -0400 Subject: [PATCH 11/26] update apf_cap/gmi_cap docs and rename function - gmi_cap/gmi_cap.h: remove #include gmi.h and forward declare gmi_model/gmi_ent. - rename gmi_cap_load_some to gmi_cap_load_selective. - update references to gmi_cap_load_some. - (gmi_cap_write): add documentation. - (gmi_cap_probe): add documentation. - (gmi_cap_load_selective): add documentation. - gmi_cap/gmi_cap.cc: rename gmi_cap_load_some to gmi_cap_load_selective. - apf_cap/apfCAP.h: add \file documentation block. - remove forward declarations (i.e. pcu namespace) from Doxygen with empty \cond. - add apf_cap documentation group. - rename gmi_cap_load_some to gmi_cap_load_selective. - add missing \param documentation - test/capLoadSome.cc: remove #include - replace gmi_cap_load_some with gmi_cap_load_selective. - sort #include blocks. - test/capVol.cc: remove obsolete #include and now that the details have been refactored into apf_cap. - remove using namespace declaration. - replace gmi_cap_load_some with gmi_cap_load_selective. Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.h | 44 +++++++++++++++++++++++++++++----- gmi_cap/gmi_cap.cc | 12 +++++----- gmi_cap/gmi_cap.h | 57 +++++++++++++++++++++++++++++++++++++++++---- test/capLoadSome.cc | 7 ++---- test/capVol.cc | 7 +----- 5 files changed, 100 insertions(+), 27 deletions(-) diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index a0e15c861..1e7e29a2f 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -9,10 +9,27 @@ ******************************************************************************/ #ifndef APF_CAP_H #define APF_CAP_H +/** + * \file apfCAP.h + * \brief Capstone apf::Mesh2 implementation and interface. + * + * Like gmi_cap, the interface is used in two ways: + * + * 1. to import an existing Capstone mesh; and + * 2. to load or generate a mesh associated with a model which was previously + * loaded by gmi_cap. + * + * \note Files which `#include` apfCAP.h should also `#include` + * CreateMG_Framework_Mesh.h (or another Capstone header with the full + * MeshDatabaseInterface definition) to import meshes or use getCapNative. + */ #include -// Forward declarations +/** + * \cond + * Forward declarations + */ struct gmi_model; namespace pcu { class PCU; @@ -23,12 +40,23 @@ namespace CreateMG { typedef Geometry::GeometryDatabaseInterface GDBI; typedef Mesh::MeshDatabaseInterface MDBI; } +/** \endcond */ namespace apf { class Mesh2; class Field; +/** + * \defgroup apf_cap Capstone APF mesh interface + * + * apf_cap provides access to an implementation of apf::Mesh2. The model must + * be loaded by gmi_cap. The interface in apfCAP.h provides additional + * functions to simplify loading and interacting with the underlying mesh. + * + * \{ + */ + /** * \brief Test for compiled Capstone library support. * \return true if apf_cap was compiled with Capstone. otherwise all routines @@ -57,9 +85,11 @@ Mesh2* createCapMesh( * geometry model. * * The gmi_model should be loaded previously by gmi_load (on a .cre file), - * gmi_cap_load, or gmi_cap_load_some. Try to load the first mesh (by index). + * gmi_cap_load, or gmi_cap_load_selective. Try to load the first mesh (by + * index). * * \param model A gmi_model associated with a Capstone geometry. + * \param PCUObj The PCU communicator to define the mesh over * \return an apf::Mesh2 interface to the Capstone mesh. */ Mesh2* createCapMesh(gmi_model* model, pcu::PCU* PCUObj); @@ -69,11 +99,12 @@ Mesh2* createCapMesh(gmi_model* model, pcu::PCU* PCUObj); * geometry model. * * The gmi_model should be loaded previously by gmi_load (on a .cre file), - * gmi_cap_load, or gmi_cap_load_some. The list of acceptable values for + * gmi_cap_load, or gmi_cap_load_selective. The list of acceptable values for * meshname can be found by using gmi_cap_probe or direct Capstone interfaces. * * \param model A gmi_model associated with a Capstone geometry. * \param meshname The name of a mesh associated with + * \param PCUObj The PCU communicator to define the mesh over * \return an apf::Mesh2 interface to the Capstone mesh. */ Mesh2* createCapMesh(gmi_model* model, const char* meshname, pcu::PCU* PCUObj); @@ -82,10 +113,9 @@ Mesh2* createCapMesh(gmi_model* model, const char* meshname, pcu::PCU* PCUObj); * \brief Generate Capstone mesh object on a model linked to a Capstone model. * * \param model A Capstone GMI model to generate a mesh on. - * \param meshname Name for the new generated mesh. + * \param dimension The dimension of mesh to generate * \param PCUObj The PCU object to link to the mesh. * \return An apf::Mesh2 object with the mesh. - * \bug dimension != 3 is not supported. */ Mesh2* generateCapMesh( gmi_model* model, int dimension, pcu::PCU* PCUObj @@ -147,6 +177,8 @@ bool smoothCAPAnisoSizes( apf::Mesh2* m, std::string analysis, apf::Field* scales, apf::Field* frames ); -}//namespace apf +/** \} */ + +} // namespace apf #endif // APF_CAP_H diff --git a/gmi_cap/gmi_cap.cc b/gmi_cap/gmi_cap.cc index 6dfbcfee9..2a63436f4 100644 --- a/gmi_cap/gmi_cap.cc +++ b/gmi_cap/gmi_cap.cc @@ -464,20 +464,20 @@ struct gmi_model* gmi_cap_load(const char* creFileName) { std::vector mesh_names; gmi_cap_probe(creFileName, mesh_names); - return gmi_cap_load_some(creFileName, mesh_names); + return gmi_cap_load_selective(creFileName, mesh_names); } -struct gmi_model* gmi_cap_load_some( +struct gmi_model* gmi_cap_load_selective( const char* creFileName, const std::vector& mesh_names ) { if (!gmi_has_ext(creFileName, "cre")) - gmi_fail("gmi_cap_load_some: CRE file must have .cre extension"); - if (!cs_module) gmi_fail("gmi_cap_load_some: called before gmi_cap_start"); + gmi_fail("gmi_cap_load_selective: CRE file must have .cre extension"); + if (!cs_module) gmi_fail("gmi_cap_load_selective: called before gmi_cap_start"); static bool called = false; if (!called) called = true; else { lion_eprint(1, - "WARNING: gmi_cap_load_some called more than once. gmi_cap operations" + "WARNING: gmi_cap_load_selective called more than once. gmi_cap operations" " may fail.\n" ); } @@ -487,7 +487,7 @@ struct gmi_model* gmi_cap_load_some( set_input(fn, "Meshes", mesh_names); auto proc = get_context_processor(ctx); if (proc->execute(fn) != STATUS_OK) - gmi_fail("gmi_cap_load_some: failed to read CRE file"); + gmi_fail("gmi_cap_load_selective: failed to read CRE file"); M_GModel gmodel; get_output(fn, "Model", gmodel); MG_API_CALL(cs_module->get_geometry(), set_current_model(gmodel)); diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index bea899ff1..c7d3a8a0d 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -26,7 +26,6 @@ * gmi_cap_start (if desired) before gmi_register_cap. */ -#include "gmi.h" #ifdef __cplusplus #include #include @@ -35,6 +34,9 @@ extern "C" { #endif +struct gmi_model; +struct gmi_ent; + /** * \defgroup gmi_cap Capstone geometric model interface * @@ -72,26 +74,73 @@ void gmi_register_cap(void); * * This method is called by gmi_load when the input file has a ".cre" * extension. It loads the file with all associated mesh models. Use - * gmi_cap_load_some to only load certain meshes. + * gmi_cap_load_selective to only load certain meshes. * * \param creFileName the name of the CRE file * \pre gmi_cap_stop must be called before this method. * \pre creFileName ends in ".cre" */ struct gmi_model* gmi_cap_load(const char* creFileName); - +/** + * \brief Write a Capstone gmi_model and associated mesh to a CRE file. + * + * \param model A previously loaded gmi_model from gmi_cap. + * \param creFileName filename for the new CRE file. + */ void gmi_cap_write(struct gmi_model* model, const char* creFileName); #ifdef __cplusplus +/** + * \brief Probe a CRE file for associated mesh names. + * + * This function calls Capstone APIs to probe the CRE file header for meshes. + * It is intended to be used in conjunction with gmi_cap_load_selective. + * + * \param[in] creFileName CRE file to probe + * \param[out] mesh_names A vector to be filled with mesh names + */ void gmi_cap_probe( const char* creFileName, std::vector& mesh_names ); +/** + * \brief Probe a CRE file for associated mesh names and contents. + * + * This function calls Capstone APIs to probe the CRE file header for model + * contents, mesh names, and mesh contents. It is intended to be used in + * conjunction with gmi_cap_load_selective, and/or to assist when there are + * multiple loadable meshes (i.e. by examining contents or displaying an + * interactive menu). + * + * \warning The format of model_content and mesh_contents is governed by the + * Capstone library and we cannot make any claims about its stability. + * + * \param[in] creFileName CRE file to probe + * \param[out] model_content A string indicating mesh contents + * \param[out] mesh_names A vector to be filled with mesh names + * \param[out] mesh_contents A vector to be filled with strings indicating mesh + * contents + */ void gmi_cap_probe( const char* creFileName, std::string& model_content, std::vector& mesh_names, std::vector& mesh_contents ); -struct gmi_model* gmi_cap_load_some( +/** + * \brief Load a gmi_model from a CRE file and select the associated loaded + * meshes. + * + * Unlike other data formats, CRE files contain both geometries and meshes. As + * such, the meshes must be selected at load time. The normal gmi_cap_load + * (which is registered to gmi_load with the ".cre" extension) loads all + * meshes by default. In certain scenarios this is undesirable (e.g. when you + * only want the first mesh in a CRE file multiple meshes, or when you only + * want to load the mesh on one rank but need the geometry on all ranks). + * + * \param creFileName The CRE file to load + * \param mesh_names A vector of mesh names to load, which may be empty. + * \return A new gmi_model linked to a Capstone geometry model + */ +struct gmi_model* gmi_cap_load_selective( const char* creFileName, const std::vector& mesh_names ); diff --git a/test/capLoadSome.cc b/test/capLoadSome.cc index 0a964863b..11ae8637a 100644 --- a/test/capLoadSome.cc +++ b/test/capLoadSome.cc @@ -2,12 +2,9 @@ #include #include -#include -#include -#include +#include #include #include -#include int main(int argc, char* argv[]) { if (argc < 2) { @@ -21,7 +18,7 @@ int main(int argc, char* argv[]) { std::string model_content; std::vector mesh_names; for (int i = 2; i < argc; ++i) mesh_names.push_back(argv[i]); - gmi_model* model = gmi_cap_load_some(argv[1], mesh_names); + gmi_model* model = gmi_cap_load_selective(argv[1], mesh_names); gmi_destroy(model); gmi_cap_stop(); pcu::Finalize(); diff --git a/test/capVol.cc b/test/capVol.cc index 5f00a6596..68fa1fa27 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -17,11 +17,6 @@ #include #include -#include -#include - -using namespace CreateMG; - #include "capVolSizeFields.h" namespace { @@ -111,7 +106,7 @@ int main(int argc, char** argv) { try { gmi_model* capGeomModel = nullptr; if (PCUObj.Self() == 0) capGeomModel = gmi_cap_load(args.in.c_str()); - else capGeomModel = gmi_cap_load_some(args.in.c_str(), {}); + else capGeomModel = gmi_cap_load_selective(args.in.c_str(), {}); auto soloPCU = PCUObj.Split(PCUObj.Self(), 0); ma::Mesh* mesh = nullptr; if (PCUObj.Self() == 0) { From a4ff301a789c4b956a448f43dcb5de534c275d23 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Wed, 25 Jun 2025 23:09:24 -0400 Subject: [PATCH 12/26] test: limit capstone tests even more - test/testing.cmake: further limit default capVol test set. - remove tests that took more than 500 seconds. Signed-off-by: Aiden Woodruff --- test/testing.cmake | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/testing.cmake b/test/testing.cmake index 3d154ab01..4ec8f4c84 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -985,25 +985,27 @@ if(ENABLE_CAPSTONE) mpi_test(capVol_WingMds 1 ./capVol -m 2 ${MDIR}/wing_surf_only.cre capVol_WingMds.cre ) - mpi_test(capVol_Cube3D 1 - ./capVol -g 3 ${MDIR}/cube_surf_only.cre capVol_Cube3D.cre - ) - mpi_test(capVol_BLCyl3D 1 - ./capVol -g 4 ${MDIR}/cyl_surf_only.cre capVol_BLCyl3D.cre - ) - mpi_test(capVol_CubeMds3D 1 - ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre - ) mpi_test(capVol_BLCylMds3D 1 ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre ) - mpi_test(capVol_CubeMds3D-4 4 - ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre - ) mpi_test(capVol_BLCylMds3D-4 4 ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre ) - if(PUMI_TEST_CAPVOL_FULLMATRIX) + if(PUMI_TEST_CAPVOL_FULL) + mpi_test(capVol_BLCyl3D 1 + ./capVol -g 4 ${MDIR}/cyl_surf_only.cre capVol_BLCyl3D.cre + ) + mpi_test(capVol_Cube3D 1 + ./capVol -g 3 ${MDIR}/cube_surf_only.cre capVol_Cube3D.cre + ) + mpi_test(capVol_CubeMds3D 1 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + mpi_test(capVol_CubeMds3D-4 4 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + endif() + if(PUMI_TEST_CAPVOL_EXTRA) mpi_test(capVol_Cyl3D 1 ./capVol -g 1 ${MDIR}/cyl_surf_only.cre out.cre) mpi_test(capVol_Cube3D 1 ./capVol -g 3 ${MDIR}/cube_surf_only.cre out.cre) mpi_test(capVol_BLCyl3D 1 ./capVol -g 4 ${MDIR}/cyl_surf_only.cre out.cre) From 3268190c6fd7e9be2b95c2fae9571ffb4fb820d7 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 12:39:53 -0400 Subject: [PATCH 13/26] test: add alternate cube shock and re-enable - test/capVolSizeFields.h: add Shock2 which is similar to the old cube shock (Shock) but with steeper gradation toward the shock and a minimum size (the minimum has the biggest speed impact). - also the new Shock2 is a little more geometry-agnostic wrt bounds. - (CylBoundaryLayer): update lion_oprint statements now that there is a distinct Shock2 class. - test/capVol.cc: use Shock2 as cube shock (input 3). - test/testing.cmake: re-enable CubeMds3d cases now that they use the faster size-field. - remove PUMI_TEST_CAPVOL_FULL section so that the only additional flag is PUMI_TEST_CAPVOL_EXTRA. Signed-off-by: Aiden Woodruff --- test/capVol.cc | 2 +- test/capVolSizeFields.h | 41 ++++++++++++++++++++++++++++++++++++++--- test/testing.cmake | 16 +++++++--------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/test/capVol.cc b/test/capVol.cc index 68fa1fa27..a6d0fc897 100644 --- a/test/capVol.cc +++ b/test/capVol.cc @@ -255,7 +255,7 @@ ma::AnisotropicFunction* makeUDF(int mode, ma::Mesh* mesh) { switch (mode) { case 1: return new UniformAniso(mesh); case 2: return new WingShock(mesh, 50); - case 3: return new Shock(mesh); + case 3: return new Shock2(mesh); case 4: return new CylBoundaryLayer(mesh); default: throw std::runtime_error("invalid size-field"); } diff --git a/test/capVolSizeFields.h b/test/capVolSizeFields.h index e36339024..cae266fb9 100644 --- a/test/capVolSizeFields.h +++ b/test/capVolSizeFields.h @@ -105,6 +105,41 @@ class Shock : public ma::AnisotropicFunction ma::Mesh* mesh; }; +/** + * \brief Cube shock version 2. + */ +class Shock2 : public ma::AnisotropicFunction +{ + public: + Shock2(ma::Mesh* m) { + mesh = m; + ma::Vector bmin, bmax; + ma::getBoundingBox(mesh, bmin, bmax); + x0 = (bmax.x() + bmin.x()) / 2; + double apothem = (bmax.x() - bmin.x()) / 2; + hmin = apothem / 128, hmax = apothem / 2; + } + virtual void getValue(ma::Entity* v, ma::Matrix& R, ma::Vector& H) + { + ma::Vector p = ma::getPosition(mesh, v); + double x = p[0]; + double s = hmax * (1 - exp(-4*std::abs(x - x0))); + double hx = std::max(hmin, s); + double hy = hmax; + double hz = hmax; + /* // principal directions */ + R = ma::Matrix( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + H = ma::Vector(hx, hy, hz); + } + private: + ma::Mesh* mesh; + double x0, hmin, hmax; +}; + // Shock along a cylinder like the one in // https://www.scorec.rpi.edu/~cwsmith/SCOREC//SimModSuite_2023.1-230428dev/MeshSimAdapt/group__MSAEx__GenAnisoMesh.html class CylBoundaryLayer : public ma::AnisotropicFunction { @@ -119,19 +154,19 @@ class CylBoundaryLayer : public ma::AnisotropicFunction { ma::getBoundingBox(mesh, bmin, bmax); ma::Vector len = bmax - bmin; if (len.x() > len.y() && len.x() > len.z()) { - lion_oprint(1, "Shock2 along X axis.\n"); + lion_oprint(1, "CylBoundaryLayer along X axis.\n"); length = len.x(); axis = Axis::X; // Radial lengths should be the same, but we find average diameter and // then halve that. radius = (len.y() / 2 + len.z() / 2) / 2; } else if (len.y() > len.x() && len.y() > len.z()) { - lion_oprint(1, "Shock2 along Y axis.\n"); + lion_oprint(1, "CylBoundaryLayer along Y axis.\n"); length = len.y(); axis = Axis::Y; radius = (len.x() / 2 + len.z() / 2) / 2; } else { - lion_oprint(1, "Shock2 along Z axis.\n"); + lion_oprint(1, "CylBoundaryLayer along Z axis.\n"); length = len.z(); axis = Axis::Z; radius = (len.x() / 2 + len.y() / 2) / 2; diff --git a/test/testing.cmake b/test/testing.cmake index 4ec8f4c84..36397491d 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -991,21 +991,19 @@ if(ENABLE_CAPSTONE) mpi_test(capVol_BLCylMds3D-4 4 ./capVol -gm 4 ${MDIR}/cyl_surf_only.cre capVol_BLCylMds3D.cre ) - if(PUMI_TEST_CAPVOL_FULL) + mpi_test(capVol_CubeMds3D 1 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + mpi_test(capVol_CubeMds3D-4 4 + ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre + ) + if(PUMI_TEST_CAPVOL_EXTRA) mpi_test(capVol_BLCyl3D 1 ./capVol -g 4 ${MDIR}/cyl_surf_only.cre capVol_BLCyl3D.cre ) mpi_test(capVol_Cube3D 1 ./capVol -g 3 ${MDIR}/cube_surf_only.cre capVol_Cube3D.cre ) - mpi_test(capVol_CubeMds3D 1 - ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre - ) - mpi_test(capVol_CubeMds3D-4 4 - ./capVol -gm 3 ${MDIR}/cube_surf_only.cre capVol_CubeMds3D.cre - ) - endif() - if(PUMI_TEST_CAPVOL_EXTRA) mpi_test(capVol_Cyl3D 1 ./capVol -g 1 ${MDIR}/cyl_surf_only.cre out.cre) mpi_test(capVol_Cube3D 1 ./capVol -g 3 ${MDIR}/cube_surf_only.cre out.cre) mpi_test(capVol_BLCyl3D 1 ./capVol -g 4 ${MDIR}/cyl_surf_only.cre out.cre) From fa5fe5b92684e24eb3c4dabd505b0aa0da6f9554 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 14:05:43 -0400 Subject: [PATCH 14/26] gmi_cap: check for probe failure - gmi_cap/gmi_cap.cc (gmi_cap_probe): check return value of reader->probe for failure (missing file, etc.) and gmi_fail. Signed-off-by: Aiden Woodruff --- gmi_cap/gmi_cap.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gmi_cap/gmi_cap.cc b/gmi_cap/gmi_cap.cc index 2a63436f4..56ca07e75 100644 --- a/gmi_cap/gmi_cap.cc +++ b/gmi_cap/gmi_cap.cc @@ -385,7 +385,8 @@ void gmi_cap_probe( cs_module->get_context(), "Create Native Reader" ); DataFileInfo info; - reader->probe(creFileName, info); + if (!reader->probe(creFileName, info)) + gmi_fail("gmi_cap_probe: failed to read file"); for (std::size_t i = 0; i < info.get_num_sections(); i++) { std::string secType, secName; info.get_section(i, secType, secName); @@ -414,7 +415,8 @@ void gmi_cap_probe( cs_module->get_context(), "Create Native Reader" ); DataFileInfo info; - reader->probe(creFileName, info); + if (!reader->probe(creFileName, info)) + gmi_fail("gmi_cap_probe: failed to read file"); for (std::size_t i = 0; i < info.get_num_sections(); i++) { std::string secType, secName; info.get_section(i, secType, secName); From 511ff0a8a8578ad269dc453525faeaab605dd0a6 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 14:14:27 -0400 Subject: [PATCH 15/26] test: cleanup capstone utilities - test/cap2vtk.cc: remove obsolete #includes. - replace direct Capstone calls with new gmi_cap/apf_cap APIs. - use exceptions to ensure allocated objects are cleaned up. - test/cap_closestPoint.cc: replace direct capstone calls with new APIs. - test/cap_inClosureOf.cc: replace direct capstone calls with new APIs. Signed-off-by: Aiden Woodruff --- test/cap2vtk.cc | 192 +++++++++++++-------------------------- test/cap_closestPoint.cc | 13 +-- test/cap_inClosureOf.cc | 18 ++-- 3 files changed, 77 insertions(+), 146 deletions(-) diff --git a/test/cap2vtk.cc b/test/cap2vtk.cc index 93adb81aa..954b77771 100644 --- a/test/cap2vtk.cc +++ b/test/cap2vtk.cc @@ -1,143 +1,79 @@ +#include +#include + #include +#include #include -#include -#include -#include -#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include - - -#include "CapstoneModule.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Analysis.h" -#include "CreateMG_Framework_Application.h" -#include "CreateMG_Framework_Attributes.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Geometry.h" -#include "CreateMG_Framework_Mesh.h" - -using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; - int main(int argc, char** argv) { + int retval = 0; pcu::Init(&argc, &argv); { pcu::PCU PCUObj; - - if (argc != 3) { - if(0==PCUObj.Self()) - std::cerr << "usage: " << argv[0] - << " \n"; - return EXIT_FAILURE; - } - - - gmi_register_mesh(); - gmi_register_null(); - - const char* creFileName = argv[1]; - const char* folderName = argv[2]; - - // load capstone mesh - // create an instance of the Capstone Module activating CREATE/CREATE/CREATE - // for the Geometry/Mesh/Attribution databases - /* const std::string gdbName("Geometry Database : Create");// Switch Create with SMLIB for CAD */ - const std::string gdbName("Geometry Database : SMLIB");// Switch Create with SMLIB for CAD - const std::string mdbName("Mesh Database : Create"); - const std::string adbName("Attribution Database : Create"); - - CapstoneModule cs("the_module", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); - - GeometryDatabaseInterface *g = cs.get_geometry(); - MeshDatabaseInterface *m = cs.get_mesh(); - AppContext *c = cs.get_context(); - - - PCU_ALWAYS_ASSERT(g); - PCU_ALWAYS_ASSERT(m); - PCU_ALWAYS_ASSERT(c); - - v_string filenames; - filenames.push_back(creFileName); - - M_GModel gmodel = cs.load_files(filenames); - - int numbreps = 0; - MG_CALL(g->get_num_breps(numbreps)); - std::cout << "number of b reps is " << numbreps << std::endl; - if(numbreps == 0) - error(HERE, ERR_INVALID_INPUT, "Model is empty"); - - M_MModel mmodel; - // Pick the volume mesh model from associated mesh models to this geom model - std::vector mmodels; - MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); - for(std::size_t i = 0; i < mmodels.size(); ++i) - { - M_MModel ammodel = mmodels[i]; - std::size_t numregs = 0; - std::size_t numfaces = 0; - std::size_t numedges = 0; - std::size_t numverts = 0; - MG_API_CALL(m, set_current_model(ammodel)); - MG_API_CALL(m, get_num_topos(TOPO_REGION, numregs)); - MG_API_CALL(m, get_num_topos(TOPO_FACE, numfaces)); - MG_API_CALL(m, get_num_topos(TOPO_EDGE, numedges)); - MG_API_CALL(m, get_num_topos(TOPO_VERTEX, numverts)); - std::cout << "num regions is " << numregs << std::endl; - std::cout << "num faces is " << numfaces << std::endl; - std::cout << "num edges is " << numedges << std::endl; - std::cout << "num verts is " << numverts << std::endl; - std::cout << "-----------" << std::endl; - if(numregs > 0) - { - mmodel = ammodel; - break; + lion_set_verbosity(1); + try { + if (PCUObj.Peers() > 1) + throw std::runtime_error("Capstone meshes are (currently) only serial"); + + if (argc != 3 && argc != 4) { + if (PCUObj.Self() == 0) { + std::cerr << "USAGE: " << argv[0] + << " [meshname] " << std::endl; } + throw std::invalid_argument("invalid arguments"); + } + + const char* creFileName = argv[1]; + const char* meshName = argc == 4 ? argv[2] : nullptr; + const char* folderName = argc == 4 ? argv[3] : argv[2]; + + gmi_cap_start(); + gmi_register_cap(); + try { + std::string model_content; + std::vector mesh_names, mesh_contents; + gmi_cap_probe(creFileName, model_content, mesh_names, mesh_contents); + if (meshName) { + if (std::count(mesh_names.begin(), mesh_names.end(), meshName) == 0) { + throw std::invalid_argument( + "mesh named " + std::string(meshName) + " not found" + ); + } + } else if (mesh_names.size() > 1) { + std::cout << "More than one mesh in CRE file." + " Run again with meshname argument after CRE filename." << std::endl; + for (size_t i = 0; i < mesh_names.size(); ++i) { + std::cout << "Mesh " << i << " `" << mesh_names[i] << "`: " + << mesh_contents[i] << std::endl; + } + throw std::runtime_error( + "meshname not provided for file with multiple meshes" + ); + } else meshName = mesh_names.front().c_str(); + PCU_DEBUG_ASSERT(meshName); + gmi_model* model = gmi_cap_load_selective(creFileName, {meshName}); + apf::Mesh2* mesh = apf::createCapMesh(model, meshName, &PCUObj); + apf::writeVtkFiles(folderName, mesh); + apf::destroyMesh(mesh); + } catch (...) { + gmi_cap_stop(); + std::rethrow_exception(std::current_exception()); + } + gmi_cap_stop(); + } catch (const std::exception& e) { + if (PCUObj.Self() == 0) std::cerr << "ERROR: " << e.what() << std::endl; + retval = 1; + } catch (...) { + if (PCUObj.Self() == 0) std::cerr << "UNKNOWN ERROR" << std::endl; + retval = 1; } - - /* SET THE ADJACENCIES */ - MG_API_CALL(m, set_adjacency_state(REGION2FACE| - REGION2EDGE| - REGION2VERTEX| - FACE2EDGE| - FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - MG_API_CALL(m, set_adjacency_scope(TOPO_EDGE, SCOPE_FULL)); - MG_API_CALL(m, set_adjacency_scope(TOPO_FACE, SCOPE_FULL)); - MG_API_CALL(m, compute_adjacency()); - - - gmi_cap_start(); - gmi_register_cap(); - - // convert the mesh to apf/mds mesh - - apf::Mesh2* mesh = apf::createCapMesh(m,g,&PCUObj); - - apf::writeVtkFiles(folderName, mesh); - - gmi_cap_stop(); - } + } // PCU Object scope pcu::Finalize(); + return retval; } diff --git a/test/cap_closestPoint.cc b/test/cap_closestPoint.cc index 39d746346..0902af2f7 100644 --- a/test/cap_closestPoint.cc +++ b/test/cap_closestPoint.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include int main (int argc, char* argv[]) { @@ -12,18 +11,15 @@ int main (int argc, char* argv[]) { { // pcu object scope pcu::PCU PCUObj; lion_set_verbosity(1); + gmi_cap_start(); gmi_register_cap(); PCU_ALWAYS_ASSERT(argc == 2); - std::string creFile(argv[1]); + const char* creFile(argv[1]); // 1. Load model. - CapstoneModule cs("cap_inClosureOf", "Geometry Database : SMLIB", - "Mesh Database : Create", "Attribution Database : Create"); - cs.load_files(v_string(1, creFile)); + gmi_model* model = gmi_cap_load(creFile); // 2. CreateMesh. - apf::Mesh2* m = apf::createCapMesh( - cs.get_mesh(), cs.get_geometry(), &PCUObj - ); + apf::Mesh2* m = apf::createCapMesh(model, &PCUObj); PCU_ALWAYS_ASSERT(m->canGetClosestPoint()); @@ -38,6 +34,7 @@ int main (int argc, char* argv[]) { PCU_ALWAYS_ASSERT((to - apf::Vector3(0.5, 0.34, 0.5)).getLength() < 0.0001); apf::destroyMesh(m); + gmi_cap_stop(); } // pcu object scope pcu::Finalize(); } diff --git a/test/cap_inClosureOf.cc b/test/cap_inClosureOf.cc index 05a042a82..87e7a4a6f 100644 --- a/test/cap_inClosureOf.cc +++ b/test/cap_inClosureOf.cc @@ -1,29 +1,26 @@ +#include #include -#include #include +#include +#include #include #include #include -#include -#include int main (int argc, char* argv[]) { pcu::Init(&argc, &argv); { // pcu object scope pcu::PCU PCUObj; lion_set_verbosity(1); + gmi_cap_start(); gmi_register_cap(); PCU_ALWAYS_ASSERT(argc == 2); - std::string creFile(argv[1]); + const char* creFile(argv[1]); // 1. Load model. - CapstoneModule cs("cap_inClosureOf", "Geometry Database : SMLIB", - "Mesh Database : Create", "Attribution Database : Create"); - cs.load_files(v_string(1, creFile)); + gmi_model* model = gmi_load(creFile); // 2. CreateMesh. - apf::Mesh2* m = apf::createCapMesh( - cs.get_mesh(), cs.get_geometry(), &PCUObj - ); + apf::Mesh2* m = apf::createCapMesh(model, &PCUObj); // 3. Get region 1 ModelEntity*. apf::ModelEntity* rgn = m->findModelEntity(3, 1); PCU_ALWAYS_ASSERT(rgn); @@ -48,6 +45,7 @@ int main (int argc, char* argv[]) { PCU_ALWAYS_ASSERT(m->isInClosureOf(m->findModelEntity(0, 8), f1)); apf::destroyMesh(m); + gmi_cap_stop(); } // pcu object scope pcu::Finalize(); } From e1e6ccb04e1476fbcb8d728ded66528102ebf8c1 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 16:33:33 -0400 Subject: [PATCH 16/26] apf_cap: define getCapNative Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apf_cap/apfCAP.cc b/apf_cap/apfCAP.cc index e9f09a871..b01e27490 100644 --- a/apf_cap/apfCAP.cc +++ b/apf_cap/apfCAP.cc @@ -1177,6 +1177,12 @@ void disownCapModel(Mesh2* capMesh) { m->disownModel(); } +CreateMG::MDBI* getCapNative(Mesh2* capMesh) { + auto m = dynamic_cast(capMesh); + if (!m) fail("disownCapModel: not a Capstone mesh"); + return m->getMesh(); +} + bool has_smoothCAPAnisoSizes(void) noexcept { #ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL return true; From a187c9817bcc04c10066f006df16bc53e569b86f Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 16:35:31 -0400 Subject: [PATCH 17/26] test: cleanup capCheckParam and capGeomTest - test/capCheckParam.cc: remove most direct capstone #includes, except Geometry/Mesh Frameworks (and only directly). - use new apf_cap APIs to load geometry/mesh. - (checkParameterization): fix indentation. - replace printf with cout. - test/capGeomTest.cc: remove obsolete and direct Capstone #includes. - use new apf_cap APIs to load geometry/mesh. - replace ma::Vector with apf::Vector3, ma::MeshEntity with apf::MeshEntity, ma::Iterator with apf::MeshIterator, and remove #include - these are all typedefs, but should be regarded as internal to ma. Signed-off-by: Aiden Woodruff --- test/capCheckParam.cc | 183 +++++++++++++----------------------------- test/capGeomTest.cc | 162 ++++++++++--------------------------- 2 files changed, 98 insertions(+), 247 deletions(-) diff --git a/test/capCheckParam.cc b/test/capCheckParam.cc index a349bff2c..2d790223f 100644 --- a/test/capCheckParam.cc +++ b/test/capCheckParam.cc @@ -1,29 +1,16 @@ +#include + #include -#include #include +#include +#include +#include #include -#include -#include -#include -#include -#include +#include +#include -#include "CapstoneModule.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Analysis.h" -#include "CreateMG_Framework_Application.h" -#include "CreateMG_Framework_Attributes.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Geometry.h" -#include "CreateMG_Framework_Mesh.h" - -using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; - -void checkParametrization(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb); +void checkParametrization(CreateMG::MDBI* mdb, CreateMG::GDBI* gdb); int main(int argc, char** argv) { @@ -39,117 +26,61 @@ int main(int argc, char** argv) const char* creFileName = argv[1]; - // load capstone mesh - // create an instance of the Capstone Module activating CREATE/CREATE/CREATE - // for the Geometry/Mesh/Attribution databases - /* const std::string gdbName("Geometry Database : Create");// Switch Create with SMLIB for CAD */ - const std::string gdbName("Geometry Database : SMLIB");// Switch Create with SMLIB for CAD - const std::string mdbName("Mesh Database : Create"); - const std::string adbName("Attribution Database : Create"); - - CapstoneModule cs("the_module", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); - - GeometryDatabaseInterface *g = cs.get_geometry(); - MeshDatabaseInterface *m = cs.get_mesh(); - AppContext *c = cs.get_context(); - - - PCU_ALWAYS_ASSERT(g); - PCU_ALWAYS_ASSERT(m); - PCU_ALWAYS_ASSERT(c); - - v_string filenames; - filenames.push_back(creFileName); - - M_GModel gmodel = cs.load_files(filenames); - - int numbreps = 0; - MG_CALL(g->get_num_breps(numbreps)); - std::cout << "number of b reps is " << numbreps << std::endl; - if(numbreps == 0) - error(HERE, ERR_INVALID_INPUT, "Model is empty"); - - M_MModel mmodel; - // Pick the volume mesh model from associated mesh models to this geom model - std::vector mmodels; - MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); - for(std::size_t i = 0; i < mmodels.size(); ++i) - { - M_MModel ammodel = mmodels[i]; - std::size_t numregs = 0; - std::size_t numfaces = 0; - std::size_t numedges = 0; - std::size_t numverts = 0; - MG_API_CALL(m, set_current_model(ammodel)); - MG_API_CALL(m, get_num_topos(TOPO_REGION, numregs)); - MG_API_CALL(m, get_num_topos(TOPO_FACE, numfaces)); - MG_API_CALL(m, get_num_topos(TOPO_EDGE, numedges)); - MG_API_CALL(m, get_num_topos(TOPO_VERTEX, numverts)); - std::cout << "num regions is " << numregs << std::endl; - std::cout << "num faces is " << numfaces << std::endl; - std::cout << "num edges is " << numedges << std::endl; - std::cout << "num verts is " << numverts << std::endl; - std::cout << "-----------" << std::endl; - if(numregs > 0) - { - mmodel = ammodel; - break; - } - } - - /* SET THE ADJACENCIES */ - MG_API_CALL(m, set_adjacency_state(REGION2FACE| - REGION2EDGE| - REGION2VERTEX| - FACE2EDGE| - FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - MG_API_CALL(m, set_adjacency_scope(TOPO_EDGE, SCOPE_FULL)); - MG_API_CALL(m, set_adjacency_scope(TOPO_FACE, SCOPE_FULL)); - MG_API_CALL(m, compute_adjacency()); + gmi_cap_start(); + gmi_register_cap(); + gmi_model* model = gmi_cap_load(creFileName); + apf::Mesh2* mesh = apf::createCapMesh(model, &PCUobj); // check parametrization using capstone apis - checkParametrization(m, g); - + checkParametrization(apf::getCapNative(mesh), gmi_export_cap(model)); + apf::destroyMesh(mesh); + gmi_cap_stop(); } // pcu object scope pcu::Finalize(); } -void checkParametrization(MeshDatabaseInterface* mdb, GeometryDatabaseInterface* gdb) -{ - MeshSmartIterator miter(mdb); - mdb->get_topo_iterator(TOPO_VERTEX, miter); - int count = 0; - double sum = 0.0; - for(mdb->iterator_begin(miter); !mdb->iterator_end(miter); mdb->iterator_next(miter)) { - M_MTopo vert = mdb->iterator_value(miter); - M_GTopo geom; - GeometryTopoType gtype; - mdb->get_geom_entity(vert, gtype, geom); - if (!gdb->is_face(geom)) continue; - double range_u[2]; - double range_v[2]; - gdb->get_parametrization_range(geom, 0, range_u[0], range_u[1]); - gdb->get_parametrization_range(geom, 1, range_v[0], range_v[1]); - GeometryTopoType gtype1; +void checkParametrization(CreateMG::MDBI* mdb, CreateMG::GDBI* gdb) { + using namespace CreateMG; + using namespace CreateMG::Mesh; + MeshSmartIterator miter(mdb); + mdb->get_topo_iterator(TOPO_VERTEX, miter); + int count = 0; + double sum = 0.0; + for(mdb->iterator_begin(miter); !mdb->iterator_end(miter); mdb->iterator_next(miter)) { + M_MTopo vert = mdb->iterator_value(miter); + M_GTopo geom; + GeometryTopoType gtype; + mdb->get_geom_entity(vert, gtype, geom); + if (!gdb->is_face(geom)) continue; + double range_u[2]; + double range_v[2]; + gdb->get_parametrization_range(geom, 0, range_u[0], range_u[1]); + gdb->get_parametrization_range(geom, 1, range_v[0], range_v[1]); + GeometryTopoType gtype1; double u,v; - mdb->get_vertex_uv_parameters(vert, u, v, gtype1); - PCU_ALWAYS_ASSERT(gtype1 == gtype); - - // coordinate from mesh - apf::Vector3 coord; - mdb->get_vertex_coord(vert, &(coord[0])); - - // coordinate from surface - vec3d x; - gdb->get_point(geom, vec3d(u, v, 0.0), x); - apf::Vector3 pcoord(x[0], x[1], x[2]); - - if (count < 50) - printf("%d, %e, %e, %e, %e, %e, %e, %e\n", count, u, v, range_u[0], range_u[1], range_v[0], range_v[1], (coord-pcoord).getLength()); - sum += (coord-pcoord) * (coord-pcoord); - count++; - } - printf("norm of the difference vector is %e\n", std::sqrt(sum)); + mdb->get_vertex_uv_parameters(vert, u, v, gtype1); + PCU_ALWAYS_ASSERT(gtype1 == gtype); + + // coordinate from mesh + apf::Vector3 coord; + mdb->get_vertex_coord(vert, &(coord[0])); + + // coordinate from surface + vec3d x; + gdb->get_point(geom, vec3d(u, v, 0.0), x); + apf::Vector3 pcoord(x[0], x[1], x[2]); + + if (count < 50) { + std::cout << count << ", " + << u << ", " << v << ", " + << range_u[0] << ", " << range_u[1] + << ", " << range_v[0] << ", " << range_v[1] + << ", " << (coord - pcoord).getLength() << std::endl; + } + sum += (coord-pcoord) * (coord-pcoord); + count++; + } + std::cout << "norm of the difference vector is " << std::sqrt(sum) + << std::endl; } diff --git a/test/capGeomTest.cc b/test/capGeomTest.cc index eda350d48..589f47fc4 100644 --- a/test/capGeomTest.cc +++ b/test/capGeomTest.cc @@ -1,36 +1,17 @@ +#include +#include + #include +#include #include #include +#include #include #include -#include #include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include -#include - - -#include "CapstoneModule.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Analysis.h" -#include "CreateMG_Framework_Application.h" -#include "CreateMG_Framework_Attributes.h" -#include "CreateMG_Framework_Core.h" -#include "CreateMG_Framework_Geometry.h" -#include "CreateMG_Framework_Mesh.h" - -using namespace CreateMG; -using namespace CreateMG::Attribution; -using namespace CreateMG::Mesh; -using namespace CreateMG::Geometry; - char const* const typeName[4] = {"vertex", @@ -44,85 +25,24 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* void visualizeEdge(gmi_model* model, gmi_ent* entity, int n, const char* fileName, pcu::PCU *PCUObj); void visualizeEdges(gmi_model* model, int n, const char* fileName, pcu::PCU *PCUObj); - - int main(int argc, char** argv) { pcu::Init(&argc, &argv); { // pcu object scope pcu::PCU PCUObj; + PCU_ALWAYS_ASSERT(argc == 2); + gmi_register_mesh(); gmi_register_null(); - - // load capstone mesh - // create an instance of the Capstone Module activating CREATE/CREATE/CREATE - // for the Geometry/Mesh/Attribution databases - /* const std::string gdbName("Geometry Database : Create");// Switch Create with SMLIB for CAD */ - const std::string gdbName("Geometry Database : SMLIB");// Switch Create with SMLIB for CAD - const std::string mdbName("Mesh Database : Create"); - const std::string adbName("Attribution Database : Create"); - - CapstoneModule cs("the_module", gdbName.c_str(), mdbName.c_str(), adbName.c_str()); - - GeometryDatabaseInterface *g = cs.get_geometry(); - MeshDatabaseInterface *m = cs.get_mesh(); - AppContext *c = cs.get_context(); - - - PCU_ALWAYS_ASSERT(g); - PCU_ALWAYS_ASSERT(m); - PCU_ALWAYS_ASSERT(c); - - v_string filenames; - filenames.push_back(argv[1]); - - M_GModel gmodel = cs.load_files(filenames); - - int numbreps = 0; - MG_CALL(g->get_num_breps(numbreps)); - std::cout << "number of b reps is " << numbreps << std::endl; - if(numbreps == 0) - error(HERE, ERR_INVALID_INPUT, "Model is empty"); - - M_MModel mmodel; - // Pick the volume mesh model from associated mesh models to this geom model - std::vector mmodels; - MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); - for(std::size_t i = 0; i < mmodels.size(); ++i) - { - M_MModel ammodel = mmodels[i]; - std::size_t numregs = 0; - std::size_t numfaces = 0; - std::size_t numedges = 0; - std::size_t numverts = 0; - MG_API_CALL(m, set_current_model(ammodel)); - MG_API_CALL(m, get_num_topos(TOPO_REGION, numregs)); - MG_API_CALL(m, get_num_topos(TOPO_FACE, numfaces)); - MG_API_CALL(m, get_num_topos(TOPO_EDGE, numedges)); - MG_API_CALL(m, get_num_topos(TOPO_VERTEX, numverts)); - std::cout << "num regions is " << numregs << std::endl; - std::cout << "num faces is " << numfaces << std::endl; - std::cout << "num edges is " << numedges << std::endl; - std::cout << "num verts is " << numverts << std::endl; - std::cout << "-----------" << std::endl; - if(numregs > 0) - { - mmodel = ammodel; - break; - } - } - - gmi_cap_start(); gmi_register_cap(); - - apf::Mesh2* mesh0 = apf::createCapMesh(m,g,&PCUObj); + gmi_model* model = gmi_load(argv[1]); + apf::Mesh2* mesh0 = apf::createCapMesh(model, &PCUObj); + apf::disownCapModel(mesh0); apf::writeVtkFiles("mesh_no_param", mesh0); - gmi_model* model = gmi_import_cap(g); - printInfo(model, 0); printf("\n"); printInfo(model, 1); @@ -147,7 +67,7 @@ int main(int argc, char** argv) printf("creating mesh with param field\n"); - apf::Mesh2* mesh = apf::createCapMesh(m,g,&PCUObj); + apf::Mesh2* mesh = apf::createCapMesh(model, &PCUObj); apf::Field* pf = apf::createFieldOn(mesh, "param_field", apf::VECTOR); apf::Field* idf = apf::createFieldOn(mesh, "id", apf::SCALAR); apf::MeshEntity* e; @@ -255,8 +175,8 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* double dv = (v_range[1] - v_range[0]) / (m-1); // make the array of vertex coordinates in the physical space - std::vector ps; - std::vector uvs; + std::vector ps; + std::vector uvs; for (int j = 0; j < m; j++) { for (int i = 0; i < n; i++) { double params[2]; @@ -264,18 +184,18 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* params[1] = v_range[0] + j * dv; double position[3]; gmi_eval(model, entity, ¶ms[0], &position[0]); - ma::Vector p(position[0], position[1], position[2]); + apf::Vector3 p(position[0], position[1], position[2]); ps.push_back(p); - ma::Vector uv(params[0], params[1], 0.); + apf::Vector3 uv(params[0], params[1], 0.); uvs.push_back(uv); } } // make the vertexes and set the coordinates using the array - std::vector vs; + std::vector vs; apf::Mesh2* mesh = apf::makeEmptyMdsMesh(gmi_load(".null"), 2, false, PCUObj); for (size_t i = 0; i < ps.size(); i++) { - ma::Entity* vert = mesh->createVert(0); + apf::MeshEntity* vert = mesh->createVert(0); mesh->setPoint(vert, 0, ps[i]); vs.push_back(vert); } @@ -283,7 +203,7 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* assert(vs.size() == ps.size()); - ma::Entity* v[3]; + apf::MeshEntity* v[3]; // make the lower/upper t elems for (int i = 0; i < n-1; i++) { for (int j = 0; j < m-1; j++) { @@ -307,8 +227,8 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* // parametric coordinates apf::Field* f = apf::createFieldOn(mesh, "param_coords", apf::VECTOR); - ma::Entity* e; - ma::Iterator* it; + apf::MeshEntity* e; + apf::MeshIterator* it; it = mesh->begin(0); int count = 0; while ( (e = mesh->iterate(it)) ) { @@ -330,9 +250,9 @@ void visualizeFace(gmi_model* model, gmi_ent* entity, int n, int m, const char* double param[2] = {uvs[count].x(), uvs[count].y()}; gmi_first_derivative(model, entity, param, uTangent, vTangent); gmi_normal(model, entity, param, normal); - apf::setVector(ut, e, 0, ma::Vector(uTangent[0], uTangent[1], uTangent[2])); - apf::setVector(vt, e, 0, ma::Vector(vTangent[0], vTangent[1], vTangent[2])); - apf::setVector(nv, e, 0, ma::Vector(normal[0], normal[1], normal[2])); + apf::setVector(ut, e, 0, apf::Vector3(uTangent[0], uTangent[1], uTangent[2])); + apf::setVector(vt, e, 0, apf::Vector3(vTangent[0], vTangent[1], vTangent[2])); + apf::setVector(nv, e, 0, apf::Vector3(normal[0], normal[1], normal[2])); count++; } @@ -353,24 +273,24 @@ void visualizeEdge(gmi_model* model, gmi_ent* entity, int n, const char* fileNam double du = (u_range[1] - u_range[0]) / (n-1); // make the array of vertex coordinates in the physical space - std::vector ps; - std::vector us; + std::vector ps; + std::vector us; for (int i = 0; i < n; i++) { double params[2]; params[0] = u_range[0] + i * du; double position[3]; gmi_eval(model, entity, ¶ms[0], &position[0]); - ma::Vector p(position[0], position[1], position[2]); + apf::Vector3 p(position[0], position[1], position[2]); ps.push_back(p); - ma::Vector uv(params[0], params[1], 0.); + apf::Vector3 uv(params[0], params[1], 0.); us.push_back(uv); } // make the vertexes and set the coordinates using the array - std::vector vs; + std::vector vs; apf::Mesh2* mesh = apf::makeEmptyMdsMesh(gmi_load(".null"), 1, false, PCUObj); for (size_t i = 0; i < ps.size(); i++) { - ma::Entity* vert = mesh->createVert(0); + apf::MeshEntity* vert = mesh->createVert(0); mesh->setPoint(vert, 0, ps[i]); vs.push_back(vert); } @@ -378,7 +298,7 @@ void visualizeEdge(gmi_model* model, gmi_ent* entity, int n, const char* fileNam assert(vs.size() == ps.size()); - ma::Entity* v[2]; + apf::MeshEntity* v[2]; // make the lower/upper t elems for (int i = 0; i < n-1; i++) { v[0] = vs[i]; @@ -392,8 +312,8 @@ void visualizeEdge(gmi_model* model, gmi_ent* entity, int n, const char* fileNam apf::printStats(mesh); apf::Field* f = apf::createFieldOn(mesh, "param_coords", apf::VECTOR); - ma::Entity* e; - ma::Iterator* it; + apf::MeshEntity* e; + apf::MeshIterator* it; it = mesh->begin(0); int count = 0; while ( (e = mesh->iterate(it)) ) { @@ -420,30 +340,30 @@ void visualizeEdges(gmi_model* model, int n, const char* fileName, pcu::PCU *PCU double du = (u_range[1] - u_range[0]) / (n-1); // make the array of vertex coordinates in the physical space - std::vector ps; - std::vector us; + std::vector ps; + std::vector us; for (int i = 0; i < n; i++) { double params[2]; params[0] = u_range[0] + i * du; double position[3]; gmi_eval(model, entity, ¶ms[0], &position[0]); - ma::Vector p(position[0], position[1], position[2]); + apf::Vector3 p(position[0], position[1], position[2]); ps.push_back(p); - ma::Vector uv(params[0], params[1], 0.); + apf::Vector3 uv(params[0], params[1], 0.); us.push_back(uv); } // make the vertexes and set the coordinates using the array - std::vector vs; + std::vector vs; for (size_t i = 0; i < ps.size(); i++) { - ma::Entity* vert = mesh->createVert(0); + apf::MeshEntity* vert = mesh->createVert(0); mesh->setPoint(vert, 0, ps[i]); vs.push_back(vert); } assert(vs.size() == ps.size()); - ma::Entity* v[2]; + apf::MeshEntity* v[2]; // make the lower/upper t elems for (int i = 0; i < n-1; i++) { v[0] = vs[i]; From 32d57be28ef080f785d3cc65604ca2efa1ae6d05 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Thu, 26 Jun 2025 18:07:13 -0400 Subject: [PATCH 18/26] gmi_cap: fix #include brackets - gmi_cap/gmi_cap.h: prioritize system files over local ones. Signed-off-by: Aiden Woodruff --- gmi_cap/gmi_cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index c7d3a8a0d..85b353db7 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -29,7 +29,7 @@ #ifdef __cplusplus #include #include -#include "CreateMG_Framework_Geometry.h" +#include #else extern "C" { #endif From a878a59048755004b13147fafc380d3005f16c8e Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 09:43:13 -0400 Subject: [PATCH 19/26] apf_cap: add sizing functions - gmi_cap/gmi_cap.h: use CreateMG_CoreDefs.h for M_GTopo i.e. Handle and forward declare GDBI. - gmi_cap/gmi_cap.cc: move CreateMG_Framework_Geometry.h here instead to reduce TU size. - sort #include blocks. - gmi_cap/CMakeLists.txt: update public link libraries. - apf_cap/apfCAP.h: rename getCapNative to exportCapNative (which is congruent with gmi_export_cap). - (disownCapModel): fix doxygen block. - (getCapId): add function to facilitate conversion from apf::MeshEntity to M_MTopo. forward declare MeshEntity. - (getCapEntity): add function to convert M_MTopo to MeshEntity. - (loadCapSizing): add function to load Capstone sizing from a vector of Capstone Metric6s. - add convenience overload to create `apf::Field`s. - (loadCapSizingFile): add function to load Capstone sizing from a bulk sizing file. - add convenience overload to create `apf::Field`s. - (has_smoothCapAnisoSizes): change CAP to Cap to unify convention. - (smoothCapAnisoSizes): change CAP to Cap to unify convention. - apf_cap/apfCAPsizing.cc: add separate file with sizing-related functions that generally do not need to access the full MeshCAP interface (although at some point the interface may be moved to an internal apfCAPmesh.h file). - (load_file): move here from test/capAdapt.cc. - (doSmoothing): refactor from smoothCapAnisoSizes for reuse in loadCapSizingFile. - (loadCapSizing): add function definition, moved from test/capAdapt.cc convertMetric6ToAnisoFields. - add overload definition. - (loadCapSizingFile): add function definition from test/capAdapt loadSizing. - (has_smoothCapAnisoSizes): move from apfCAP.cc. - (smoothCapAnisoSizes): move from apfCAP.cc and use doSmoothing and loadCapSizing. - apf_cap/apfCAP.cc (getCapId, getCapEntity): define functions. - (exportCapNative): rename function. fix error message. - apf_cap/CMakeLists.txt: add apfCAPsizing.cc. - update link libraries. - fix HAVE_CAPSTONE_SIZINGMETRICTOOL check for Capstone 12.5.2. - test/capAdapt.cc: remove direct Capstone #includes and clean other unneeded headers. - (load_file, decompose_metric, loadSizing, convertMetric6toAnisoFields, makeApfInterfaceWithSizing): remove functions and use new apf_cap interfaces. - remove CapstoneModule. - load gmi_model everywhere and only rank 0 gets full mesh. - use apf::makeEmptyCapMesh instead of direct Capstone API. - destroy model (and always mark unowned). - test/capCheckParam.cc: rename getCapNative to exportCapNative. - test/cap_smooth.cc: rename has_smoothCAPAnisoSizes to has_smoothCapAnisoSizes. - test/CMakeLists.txt: update capAdapt and capCheckParam linking (the latter needs real Capstone headers now). Signed-off-by: Aiden Woodruff --- apf_cap/CMakeLists.txt | 14 ++- apf_cap/apfCAP.cc | 116 +++++------------ apf_cap/apfCAP.h | 56 ++++++++- apf_cap/apfCAPsizing.cc | 204 ++++++++++++++++++++++++++++++ gmi_cap/CMakeLists.txt | 2 +- gmi_cap/gmi_cap.cc | 5 +- gmi_cap/gmi_cap.h | 9 +- test/CMakeLists.txt | 2 +- test/capAdapt.cc | 272 +++++----------------------------------- test/capCheckParam.cc | 2 +- test/cap_smooth.cc | 2 +- 11 files changed, 337 insertions(+), 347 deletions(-) create mode 100644 apf_cap/apfCAPsizing.cc diff --git a/apf_cap/CMakeLists.txt b/apf_cap/CMakeLists.txt index 156d3a65e..2ef4140e3 100644 --- a/apf_cap/CMakeLists.txt +++ b/apf_cap/CMakeLists.txt @@ -12,16 +12,26 @@ include(CheckIncludeFileCXX) cmake_policy(SET CMP0075 NEW) # Observe CMAKE_REQUIRED_LIBRARIES. #Sources & Headers -set(SOURCES apfCAP.cc) +set(SOURCES apfCAP.cc apfCAPsizing.cc) set(HEADERS apfCAP.h) #Library add_library(apf_cap ${SOURCES}) -target_link_libraries(apf_cap PUBLIC apf gmi_cap capstone_module) +target_link_libraries(apf_cap PUBLIC apf gmi_cap) +target_link_libraries(apf_cap PRIVATE framework_mesh framework_application) set(CMAKE_CXX_OLD_STANDARD "${CMAKE_CXX_STANDARD}") cmake_push_check_state(RESET) set(CMAKE_CXX_STANDARD 14) + +# CreateFT_Vector.h via CreateMG_SizingMetricTool.h is missing +# `#include ` in Capstone 12.5.2 so add it explicitly in this check. +if(MSVC) + set(CMAKE_REQUIRED_FLAGS "/FI limits") +else() + set(CMAKE_REQUIRED_FLAGS "-include limits") +endif() + set(CMAKE_REQUIRED_LIBRARIES "framework_meshing") check_include_file_cxx("CreateMG_SizingMetricTool.h" HAVE_CAPSTONE_SIZINGMETRICTOOL) cmake_pop_check_state() diff --git a/apf_cap/apfCAP.cc b/apf_cap/apfCAP.cc index b01e27490..88ce841e2 100644 --- a/apf_cap/apfCAP.cc +++ b/apf_cap/apfCAP.cc @@ -14,11 +14,9 @@ #include #include -#include +#include #include -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL -#include -#endif +#include using namespace CreateMG; namespace MeshMG = CreateMG::Mesh; @@ -1177,94 +1175,38 @@ void disownCapModel(Mesh2* capMesh) { m->disownModel(); } -CreateMG::MDBI* getCapNative(Mesh2* capMesh) { + +size_t getCapId(Mesh2* capMesh, MeshEntity* e) { auto m = dynamic_cast(capMesh); - if (!m) fail("disownCapModel: not a Capstone mesh"); - return m->getMesh(); + if (!m) fail("getCapId: not a Capstone mesh"); + MDBI* mdbi = m->getMesh(); + auto mtopo = fromEntity(e); + size_t id; + MG_API_CALL(mdbi, get_id(mtopo, id)); + return id; } -bool has_smoothCAPAnisoSizes(void) noexcept { -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL - return true; -#else - return false; -#endif +MeshEntity* getCapEntity(Mesh2* capMesh, int dimension, size_t id) { + auto m = dynamic_cast(capMesh); + if (!m) fail("getCapEntity: not a Capstone mesh"); + MDBI* mdbi = m->getMesh(); + MeshMG::MeshTopo topo = MeshMG::TOPO_UNKNOWN; + switch (dimension) { + case 0: topo = MeshMG::TOPO_VERTEX; break; + case 1: topo = MeshMG::TOPO_EDGE; break; + case 2: topo = MeshMG::TOPO_FACE; break; + case 3: topo = MeshMG::TOPO_REGION; break; + default: return nullptr; + } + auto mtopo = mdbi->get_topo_by_id(topo, id); + return mtopo.is_valid() ? toEntity(mtopo) : nullptr; } -bool smoothCAPAnisoSizes(apf::Mesh2* mesh, std::string analysis, - apf::Field* scales, apf::Field* frames) { -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL - // Ensure input is a MeshCAP. - apf::MeshCAP* m = dynamic_cast(mesh); - if (!m) { - lion_eprint(1, "ERROR: smoothCAPAnisoSizes: mesh is not an apf::MeshCAP*\n"); - return false; - } - // Extract metric tensors from MeshAdapt frames and scales. - std::vector sizing6(m->count(0)); - apf::Matrix3x3 Q; - apf::Vector3 H; - apf::MeshIterator* it = m->begin(0); - for (apf::MeshEntity* e = m->iterate(it); e; e = m->iterate(it)) { - apf::getVector(scales, e, 0, H); // Desired element lengths. - apf::getMatrix(frames, e, 0, Q); // MeshAdapt uses column vectors. - apf::Matrix3x3 L(1.0/(H[0]*H[0]), 0, 0, - 0, 1.0/(H[1]*H[1]), 0, - 0, 0, 1.0/(H[2]*H[2])); - apf::Matrix3x3 t = Q * L * apf::transpose(Q); // Invert orthogonal frames. - size_t id; - MG_API_CALL(m->getMesh(), get_id(fromEntity(e), id)); - PCU_DEBUG_ASSERT(id != 0); - --id; - sizing6[id][0] = t[0][0]; - sizing6[id][1] = t[0][1]; - sizing6[id][2] = t[0][2]; - sizing6[id][3] = t[1][1]; - sizing6[id][4] = t[1][2]; - sizing6[id][5] = t[2][2]; - } - m->end(it); - auto smooth_tool = get_sizing_metric_tool(m->getMesh()->get_context(), - "CreateSmoothingBase"); - if (smooth_tool == nullptr) { - lion_eprint(1, "ERROR: Unable to find \"CreateSmoothingBase\"\n"); - return false; - } - smooth_tool->set_context(m->getMesh()->get_context()); - M_MModel mmodel; - MG_API_CALL(m->getMesh(), get_current_model(mmodel)); - smooth_tool->set_metric(mmodel, "sizing6", sizing6); - std::vector ometric; - smooth_tool->smooth_metric(mmodel, analysis, "sizing6", ometric); - it = m->begin(0); - for (apf::MeshEntity* e = m->iterate(it); e; e = m->iterate(it)) { - size_t id; - MG_API_CALL(m->getMesh(), get_id(fromEntity(e), id)); - PCU_DEBUG_ASSERT(id != 0); - --id; - const Metric6& m = ometric[id]; - apf::Matrix3x3 t(m[0], m[1], m[2], - m[1], m[3], m[4], - m[2], m[4], m[5]); - int n = apf::eigen(t, &Q[0], &H[0]); // Eigenvectors in rows of Q. - PCU_DEBUG_ASSERT(n == 3); - Q = apf::transpose(Q); // Put eigenvectors back into columns for MeshAdapt. - for (int i = 0; i < 3; ++i) { - H[i] = 1.0/sqrt(H[i]); - } - apf::setMatrix(frames, e, 0, Q); - apf::setVector(scales, e, 0, H); - } - m->end(it); - return true; -#else - (void) mesh; - (void) analysis; - (void) scales; - (void) frames; - apf::fail("smoothCAPAnisoSizes: Capstone does not have SizingMetricTool."); -#endif +CreateMG::MDBI* exportCapNative(Mesh2* capMesh) { + auto m = dynamic_cast(capMesh); + if (!m) fail("exportCapNative: not a Capstone mesh"); + return m->getMesh(); } -}//namespace apf +} // namespace apf diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index 1e7e29a2f..48edbe293 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -21,10 +21,11 @@ * * \note Files which `#include` apfCAP.h should also `#include` * CreateMG_Framework_Mesh.h (or another Capstone header with the full - * MeshDatabaseInterface definition) to import meshes or use getCapNative. + * MeshDatabaseInterface definition) to import meshes or use exportCapNative. */ #include +#include /** * \cond @@ -39,13 +40,15 @@ namespace CreateMG { namespace Mesh { class MeshDatabaseInterface; } typedef Geometry::GeometryDatabaseInterface GDBI; typedef Mesh::MeshDatabaseInterface MDBI; + class Metric6; } /** \endcond */ namespace apf { -class Mesh2; class Field; +class Mesh2; +class MeshEntity; /** * \defgroup apf_cap Capstone APF mesh interface @@ -134,7 +137,7 @@ Mesh2* makeEmptyCapMesh( ); /** - * Disown capMesh's gmi_model. + * \brief Disown capMesh's gmi_model. * * Mark the gmi_model as non-owned so that the destructor does not call * gmi_destroy. @@ -143,12 +146,53 @@ Mesh2* makeEmptyCapMesh( */ void disownCapModel(Mesh2* capMesh); +/** + * \brief Get the native Capstone id of an APF entity. + * + * \param m A Capstone mesh + * \param e A MeshEntity on m + * \return Unique id associated with the underlying Capstone Topo. + */ +size_t getCapId(Mesh2* m, MeshEntity* e); + +/** + * \brief Get an entity from a Capstone mesh by dimension and native id. + * + * \param m A Capstone mesh + * \param dimension The dimension of the entity to retrieve + * \param id The native Capstone id + * \return The corresponding MeshEntity or nullptr if no such entity exists + */ +MeshEntity* getCapEntity(Mesh2* m, int dimension, size_t id); + /** * \brief Get native Capstone mesh database interface. * \param capMesh Previously loaded capstone mesh. * \return Underlying Capstone mesh database interface from capMesh. */ -CreateMG::MDBI* getCapNative(Mesh2* capMesh); +CreateMG::MDBI* exportCapNative(Mesh2* capMesh); + +bool loadCapSizing( + apf::Mesh2* m, const std::vector& sizing, + apf::Field* scales, apf::Field* frames +); + +bool loadCapSizing( + apf::Mesh2* m, const std::vector& sizing, + const char* scales, const char* frames +); + +bool loadCapSizingFile( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + apf::Field* scales, apf::Field* frames, + bool smooth = false, const std::string& analysis = "" +); + +bool loadCapSizingFile( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + const char* scales, const char* frames, + bool smooth = false, const std::string& analysis = "" +); /** * \brief Test for smoothCAPAnisoSizes support. @@ -161,7 +205,7 @@ CreateMG::MDBI* getCapNative(Mesh2* capMesh); * against. Otherwise the call will always apf::fail. Use this * function to programmatically test for the capability. */ -bool has_smoothCAPAnisoSizes(void) noexcept; +bool has_smoothCapAnisoSizes(void) noexcept; /** * \brief Use the SizingMetricTool to smooth a size field on a Capstone mesh. @@ -173,7 +217,7 @@ bool has_smoothCAPAnisoSizes(void) noexcept; * \return A boolean indicating success. * \pre m must be a Capstone mesh. */ -bool smoothCAPAnisoSizes( +bool smoothCapAnisoSizes( apf::Mesh2* m, std::string analysis, apf::Field* scales, apf::Field* frames ); diff --git a/apf_cap/apfCAPsizing.cc b/apf_cap/apfCAPsizing.cc new file mode 100644 index 000000000..ba3accabe --- /dev/null +++ b/apf_cap/apfCAPsizing.cc @@ -0,0 +1,204 @@ +#include "apfCAP.h" + +#include + +#include +#include +#include +#include + +#include +#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL +#include +#endif + +namespace { + +/** \brief Load bulk file of one type into std::vector. */ +template +typename std::enable_if::value, void>::type +load_file(const std::string& fname, std::vector& data) { + try { + std::ifstream file(fname, std::ios::in | std::ios::binary); + T val; + while (file) { + file.read(reinterpret_cast(std::addressof(val)), sizeof(T)); + if (file.gcount() == sizeof(T)) data.push_back(val); + } + } catch (...) { + std::throw_with_nested( + std::runtime_error("failed to load file `" + fname + "`") + ); + } +} + +bool doSmoothing( + apf::Mesh2* m, const std::string& analysis, + const std::vector& in, std::vector& out +) { + auto smooth_tool = CreateMG::get_sizing_metric_tool( + apf::exportCapNative(m)->get_context(), "CreateSmoothingBase" + ); + if (smooth_tool == nullptr) { + lion_eprint(1, "ERROR: Unable to find \"CreateSmoothingBase\"\n"); + return false; + } + smooth_tool->set_context(apf::exportCapNative(m)->get_context()); + CreateMG::M_MModel mmodel; + MG_API_CALL(apf::exportCapNative(m), get_current_model(mmodel)); + out.resize(in.size()); + smooth_tool->set_metric(mmodel, "apf_cap_sizing6", in); + smooth_tool->smooth_metric(mmodel, analysis, "apf_cap_sizing6", out); + smooth_tool->remove(mmodel, "apf_cap_sizing6"); + return true; +} + +} // namespace + +namespace apf { + +bool loadCapSizing( + apf::Mesh2* m, const std::vector& sizing, + apf::Field* scales, apf::Field* frames +) { + PCU_DEBUG_ASSERT(m); + PCU_DEBUG_ASSERT(scales); + PCU_DEBUG_ASSERT(frames); + apf::Vector3 scale; + apf::Matrix3x3 frame; + for (std::size_t i = 0; i < sizing.size(); ++i) { + // field eigen values + apf::Matrix3x3 mat( + sizing[i][0], sizing[i][1], sizing[i][2], + sizing[i][1], sizing[i][3], sizing[i][4], + sizing[i][2], sizing[i][4], sizing[i][5] + ); + int n = apf::eigen(mat, &frame[0], &scale[0]); + PCU_DEBUG_ASSERT(n == 3); + for (int i = 0; i < 3; i++) { + scale[i] = 1.0/sqrt(scale[i]); + } + // MeshAdapt wants frames on the columns. + frame = apf::transpose(frame); + apf::MeshEntity* e = getCapEntity(m, 0, i + 1); + apf::setVector(scales, e, 0, scale); + apf::setMatrix(frames, e, 0, frame); + } + return true; +} + +bool loadCapSizing( + apf::Mesh2* m, const std::vector& sizing, + const char* scales, const char* frames +) { + apf::Field* scalesField = apf::createFieldOn(m, scales, VECTOR); + apf::Field* framesField = apf::createFieldOn(m, frames, MATRIX); + return loadCapSizing(m, sizing, scalesField, framesField); +} + +bool loadCapSizingFile( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + apf::Field* scales, apf::Field* frames, + bool smooth, const std::string& analysis +) { + if (smooth && !has_smoothCapAnisoSizes()) { + lion_eprint(1, + "WARNING: loadCapSizingFile: smoothing requested but apf_cap was" + " compiled without support for smoothing\n"); + return false; + } + std::vector vmap; + std::vector sizing; + load_file(vmapFile, vmap); + load_file(sizingFile, sizing); + // Validate sizing + if (sizing.size() / 6 != m->count(0)) { + std::string msg = "loadCapSizingFile: sizing file size does not match mesh" + " vertex count: " + std::to_string(sizing.size() / 6) + " / " + + std::to_string(m->count(0)); + fail(msg.c_str()); + } + // Validate vmap + if ( + vmap.size() < 1 || vmap[0] != vmap.size() - 1 || vmap[0] != m->count(0) + ) { + fail("loadCapSizingFile: loaded invalid vmap"); + } + // Copy sizefield to Metric6 + std::vector sizing6(vmap[0]); + for (size_t si = 0, vi = 1; vi < vmap.size(); ++vi) { + size_t tid = vmap[vi]; + PCU_DEBUG_ASSERT(tid != 0); + for (size_t j = 0; j < 6; ++j, ++si) { + sizing6[tid - 1][j] = sizing[si]; + } + } + if (smooth) { + std::vector ometric; + if (!doSmoothing(m, analysis, sizing6, ometric)) return false; + std::swap(sizing6, ometric); + } + return loadCapSizing(m, sizing6, scales, frames); +} + +bool loadCapSizingFile( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + const char* scales, const char* frames, + bool smooth, const std::string& analysis +) { + apf::Field* scalesField = apf::createFieldOn(m, scales, VECTOR); + apf::Field* framesField = apf::createFieldOn(m, frames, MATRIX); + return loadCapSizingFile( + m, sizingFile, vmapFile, scalesField, framesField, smooth, analysis + ); +} + +bool has_smoothCapAnisoSizes(void) noexcept { +#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL + return true; +#else + return false; +#endif +} + +bool smoothCapAnisoSizes(apf::Mesh2* mesh, std::string analysis, + apf::Field* scales, apf::Field* frames) { +#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL + // Extract metric tensors from MeshAdapt frames and scales. + std::vector sizing6(mesh->count(0)); + apf::Matrix3x3 Q; + apf::Vector3 H; + apf::MeshIterator* it = mesh->begin(0); + for (apf::MeshEntity* e = mesh->iterate(it); e; e = mesh->iterate(it)) { + apf::getVector(scales, e, 0, H); // Desired element lengths. + apf::getMatrix(frames, e, 0, Q); // MeshAdapt uses column vectors. + apf::Matrix3x3 L( + 1.0/(H[0]*H[0]), 0, 0, + 0, 1.0/(H[1]*H[1]), 0, + 0, 0, 1.0/(H[2]*H[2]) + ); + apf::Matrix3x3 t = Q * L * apf::transpose(Q); // Invert orthogonal frames. + size_t id = getCapId(mesh, e); + PCU_DEBUG_ASSERT(id != 0); + --id; + sizing6[id][0] = t[0][0]; + sizing6[id][1] = t[0][1]; + sizing6[id][2] = t[0][2]; + sizing6[id][3] = t[1][1]; + sizing6[id][4] = t[1][2]; + sizing6[id][5] = t[2][2]; + } + mesh->end(it); + std::vector ometric; + if (!doSmoothing(mesh, analysis, sizing6, ometric)) return false; + return loadCapSizing(mesh, ometric, frames, scales); +#else + (void) mesh; + (void) analysis; + (void) scales; + (void) frames; + apf::fail("smoothCAPAnisoSizes: Capstone does not have SizingMetricTool."); +#endif +} + +} // namespace apf diff --git a/gmi_cap/CMakeLists.txt b/gmi_cap/CMakeLists.txt index 3c616071d..a932b79f6 100644 --- a/gmi_cap/CMakeLists.txt +++ b/gmi_cap/CMakeLists.txt @@ -13,7 +13,7 @@ set(HEADERS gmi_cap.h) add_library(gmi_cap ${SOURCES}) -target_link_libraries(gmi_cap PUBLIC gmi framework_geometry) +target_link_libraries(gmi_cap PUBLIC gmi framework_core) target_link_libraries(gmi_cap PRIVATE pcu lion capstone_module) # Include directories diff --git a/gmi_cap/gmi_cap.cc b/gmi_cap/gmi_cap.cc index 56ca07e75..9996caed6 100644 --- a/gmi_cap/gmi_cap.cc +++ b/gmi_cap/gmi_cap.cc @@ -9,16 +9,17 @@ *******************************************************************************/ #include "gmi_cap.h" -#include #include +#include #include -#include #include #include +#include #include #include +#include #include #include diff --git a/gmi_cap/gmi_cap.h b/gmi_cap/gmi_cap.h index 85b353db7..6ee457198 100644 --- a/gmi_cap/gmi_cap.h +++ b/gmi_cap/gmi_cap.h @@ -29,11 +29,16 @@ #ifdef __cplusplus #include #include -#include +#include +// Forward declaration +namespace CreateMG { + namespace Geometry { class GeometryDatabaseInterface; } + typedef Geometry::GeometryDatabaseInterface GDBI; +} #else extern "C" { #endif - +// Forward declarations struct gmi_model; struct gmi_ent; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 52556a534..8a6915688 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -227,8 +227,8 @@ if(ENABLE_CAPSTONE) util_exe_func(cap2vtk cap2vtk.cc) util_exe_func(capGeomTest capGeomTest.cc) util_exe_func(capCheckParam capCheckParam.cc) + target_link_libraries(capCheckParam framework_geometry framework_mesh) util_exe_func(capAdapt capAdapt.cc) - target_link_libraries(capAdapt framework_meshing) util_exe_func(capProbe capProbe.cc) util_exe_func(capLoadSome capLoadSome.cc) endif() diff --git a/test/capAdapt.cc b/test/capAdapt.cc index 4e09ea591..52ceb9973 100644 --- a/test/capAdapt.cc +++ b/test/capAdapt.cc @@ -9,12 +9,9 @@ * \author Eric Mestreau */ #include -#include #include #include #include -#include -#include #include #include @@ -33,76 +30,19 @@ #include #include -#include "CapstoneModule.h" -#include "CreateMG_Framework_Mesh.h" -#include "CreateMG_SizingMetricTool.h" - -using namespace CreateMG; - namespace { /** \brief Print nested exceptions. */ void print_exception(const std::exception& e, int level = 0); -/** \brief Load bulk file of one type into std::vector. */ -template -typename std::enable_if::value, void>::type -load_file(const std::string& fname, std::vector& data) { - try { - std::ifstream file(fname, std::ios::in | std::ios::binary); - T val; - while (file) { - file.read(reinterpret_cast(std::addressof(val)), sizeof(T)); - if (file.gcount() == sizeof(T)) data.push_back(val); - } - } catch(...) { - std::throw_with_nested( - std::runtime_error("failed to load the file `" + fname + "`") - ); - } -} - /** \brief Return the filename without the extension. */ std::string get_no_extension(const std::string& fname) { return fname.substr(0, fname.find_last_of(".")); } -/** \brief Decompose a 6 component metric tensor into frames and scales. */ -void decompose_metric(const v_double& metric, apf::Matrix3x3& frame, - apf::Vector3& scalar) { - apf::Matrix3x3 m(metric[0], metric[1], metric[2], - metric[1], metric[3], metric[4], - metric[2], metric[4], metric[5]); - int n = apf::eigen(m, &frame[0], &scalar[0]); - PCU_DEBUG_ASSERT(n == 3); -} - -/** - * \brief Load sizing metric in mesh vertex iteration order. - * - * \param m Capstone mesh database. - * \param mapFileName path to vmap file with vertex mappings (for ordering). - * \param sizFielName path to bulk sizing file. - * */ -std::vector loadSizing( - MDBI *m, std::string mapFileName, std::string sizFileName -); - -/** \brief Load Metric6 vector into APF frame and scale fields. */ -void convertMetric6toAnisoFields( - MDBI *m, const std::vector& sizing6, - apf::Field* frameField, apf::Field* scaleField -); - /** \brief Print edge cuts. */ void printEdgeCuts(apf::Mesh2 *m); -/** \brief Create an apf::Mesh2* from CapstoneModule and bulk sizing file. */ -ma::Mesh* makeApfInterfaceWithSizing( - CapstoneModule& cs, std::unique_ptr& pcu, bool original, - const std::string& vmapFile, const std::string& sizingFile, bool smooth -); - /** \brief Migrate all elements back to rank 0. */ void migrateHome(apf::Mesh2* mesh); @@ -165,67 +105,47 @@ int main(int argc, char** argv) { /* LOAD CAPSTONE MESH */ if (PCU.Self() == 0) std::cout << "STATUS: Init Capstone" << std::endl; - const std::string analysis("Kestrel"); - CapstoneModule cs("capAdapt", - "Geometry Database : SMLIB", - "Mesh Database : Create", - "Attribution Database : Create" - ); - MDBI *m = cs.get_mesh(); - PCU_ALWAYS_ASSERT(m); if (PCU.Self() == 0) - std::cout << "STATUS: Loading CRE file : " << args.creFilename() - << std::endl; - M_GModel gmodel; + std::cout << "STATUS: Loading CRE file : " << args.creFilename(); + gmi_model* model = nullptr; // Load CRE file sequentially to avoid NFS errors. - for (int p = 0; p < PCU.Peers(); ++p) { - if (PCU.Self() == p) { - gmodel = cs.load_files(v_string(1, args.creFilename())); + for (int i = 0; i < PCU.Peers(); ++i) { + if (PCU.Self() == i) { + if (PCU.Self() == 0) model = gmi_load(args.creFilename().c_str()); + else model = gmi_cap_load_selective(args.creFilename().c_str(), {}); } PCU.Barrier(); } - // Load mesh. - M_MModel mmodel; - std::vector mmodels; - MG_API_CALL(m, get_associated_mesh_models(gmodel, mmodels)); - PCU_ALWAYS_ASSERT(mmodels.size() == 1); - MG_API_CALL(m, set_current_model(mmodels[0])); - mmodel = mmodels[0]; - cs.set_analysis_name(analysis); - - /* SET THE ADJACENCIES */ - if (PCU.Self() == 0) { - std::cout << "STATUS: Compute required adjacencies" << std::endl; - MG_API_CALL(m, set_adjacency_state(REGION2FACE | REGION2EDGE | - REGION2VERTEX | FACE2EDGE | FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - MG_API_CALL(m, compute_adjacency()); - } - // Create apf::Mesh2 wrapper. if (PCU.Self() == 0) std::cout << "STATUS: Create apf::Mesh interface" << std::endl; - ma::Mesh* apfCapMesh = nullptr, *apfMesh = nullptr; + ma::Mesh* apfMesh = nullptr; bool original = PCU.Self() == 0; - int parts = PCU.Peers(); auto soloPCU = PCU.Split(PCU.Self(), 0); - apfCapMesh = makeApfInterfaceWithSizing( - cs, soloPCU, original, - get_no_extension(args.creFilename()) + ".avm.vmap", - args.sizingFilename(), args.smooth() - ); if (original) { + ma::Mesh* apfCapMesh = apf::createCapMesh(model, soloPCU.get()); + apf::disownCapModel(apfCapMesh); + if (!apf::loadCapSizingFile( + apfCapMesh, args.sizingFilename(), + get_no_extension(args.creFilename()) + ".avm.vmap", + "adapt_scales", "adapt_frames", true, "Kestrel" + )) { + throw std::runtime_error("failed to load sizing file"); + } + apf::writeVtkFiles("before", apfCapMesh); /* CONVERT TO APF MDS MESH AND WRITE VTK */ std::cout << "STATUS: Convert to APF MDS Mesh" << std::endl; double t_start = pcu::Time(); - apfMesh = apf::createMdsMesh(apfCapMesh->getModel(), apfCapMesh, true); + apfMesh = apf::createMdsMesh(model, apfCapMesh, true); double t_end = pcu::Time(); std::cout << "INFO: Mesh converted in " << t_end - t_start << " seconds." << std::endl; + apf::disownMdsModel(apfMesh); + apf::destroyMesh(apfCapMesh); apfMesh->verify(); apf::printStats(apfMesh); apf::writeVtkFiles("before-mds", apfMesh); @@ -233,44 +153,27 @@ int main(int argc, char** argv) { PCU.Barrier(); - parallelAdapt(apfMesh, apfCapMesh->getModel(), PCU, args.maxiter()); + parallelAdapt(apfMesh, model, PCU, args.maxiter()); if (original) { - /* COPY THE MESH MODEL TO KEEP */ - MG_API_CALL(m, create_associated_model(mmodel, gmodel, "MeshAdapt")); - - /* SETUP NEW MESH ADJACENCIES */ - MG_API_CALL(m, set_adjacency_state(REGION2FACE|REGION2EDGE| - REGION2VERTEX|FACE2EDGE|FACE2VERTEX)); - MG_API_CALL(m, set_reverse_states()); - MG_API_CALL(m, compute_adjacency()); - - // Destroy old fields. - apf::destroyField(apfCapMesh->findField("adapt_scales")); - apf::destroyField(apfCapMesh->findField("adapt_frames")); - + apf::Mesh2* mdsMesh = apfMesh; + apfMesh = apf::makeEmptyCapMesh(model, "MeshAdapt", soloPCU.get()); + apf::disownCapModel(apfMesh); auto t0 = pcu::Time(); /* WRITE MDS MESH TO CRE */ - apf::convert(apfMesh, apfCapMesh); + apf::convert(mdsMesh, apfMesh); auto t1 = pcu::Time(); std::cout << "Converting MDS to CRE: " << t1 - t0 << " seconds" << std::endl; + apf::destroyMesh(mdsMesh); /* SAVE FINAL CRE FILE */ - cs.save_mesh_file("after_cap.vtk", mmodel); auto creOFileName = get_no_extension(args.creFilename()) + "_adapted.cre"; - cs.save_file(creOFileName,gmodel); - - /* PRINT ADAPTED MESH INFO */ - std::string info; - m->print_info(mmodel, info); - std::cout << info << std::endl; - - apf::destroyMesh(apfCapMesh); + gmi_cap_write(model, creOFileName.c_str()); apf::destroyMesh(apfMesh); } - PCU.Barrier(); + gmi_destroy(model); } catch (const std::exception& e) { std::cerr << "FATAL: "; print_exception(e); @@ -295,125 +198,6 @@ void print_exception(const std::exception& e, int level) { } catch (...) {} } -std::vector loadSizing( - MDBI *m, std::string mapFileName, std::string sizFileName -) { - std::vector vmap; - std::vector sizing; - std::cout << "STATUS: Loading VMap file: " << mapFileName << std::endl; - load_file(mapFileName, vmap); - std::cout << "INFO: VMap number of vertices = " << vmap.size() - 1 - << " vmap[0] = " << vmap[0] << std::endl; - std::cout << "STATUS: Loading Sizing File: " << sizFileName - << std::endl; - load_file(sizFileName, sizing); - std::cout << "INFO: Sizing File number of vertices = " - << sizing.size()/6 << std::endl; - std::size_t numVertices; - MG_API_CALL(m, get_num_topos(Mesh::TOPO_VERTEX, numVertices)); - if (sizing.size() / 6 != numVertices) { - throw std::runtime_error( - "sizing field does not match mesh: " + std::to_string(sizing.size() / 6) - + " / " + std::to_string(numVertices) - ); - } - - /* COPY SIZEFIELD TO METRIC6 */ - std::vector sizing6(numVertices); - for (size_t si = 0, i = 1; i < vmap.size(); ++i) { - size_t tid = vmap[i]; - PCU_DEBUG_ASSERT(tid != 0); - M_MTopo mtopo = m->get_topo_by_id(Mesh::TOPO_VERTEX, tid); - for (size_t j = 0; j < 6; ++j, ++si) { - sizing6[tid - 1][j] = sizing[si]; - } - } - return sizing6; -} - -void convertMetric6toAnisoFields( - MDBI *m, const std::vector& sizing6, - apf::Field* frameField, apf::Field* scaleField -) { - v_double sz(6); - apf::Vector3 scalar; - apf::Matrix3x3 frame; - for (std::size_t i=0; iget_topo_by_id(CreateMG::Mesh::TOPO_VERTEX,i + 1); - for (std::size_t j=0; j<6; j++) sz[j] = sizing6[i][j]; - - // field eigen values - decompose_metric(sz,frame,scalar); - for (int i=0; i<3; i++) { - scalar[i] = 1.0/sqrt(scalar[i]); - } - // MeshAdapt wants frames on the columns. - frame = apf::transpose(frame); - apf::MeshEntity* ent = (apf::MeshEntity*)mtopo.get(); - apf::setVector(scaleField, ent, 0, scalar); - apf::setMatrix(frameField, ent, 0, frame); - } -} - -ma::Mesh* makeApfInterfaceWithSizing( - CapstoneModule& cs, std::unique_ptr& pcu, bool original, - const std::string& vmapFile, const std::string& sizingFile, bool smooth -) { - // All ranks need apfCapMesh->getModel(). - ma::Mesh* apfCapMesh = apf::createCapMesh( - cs.get_mesh(), cs.get_geometry(), pcu.get() - ); - - if (original) { - /* LOAD THE SIZING FILE AND VMAP FILE */ - /* CHECK VALIDITY OF THE SIZING FIELD AGAINST THE MESH MODEL */ - std::cout << "STATUS: Loading sizing" << std::endl; - std::vector sizing6 = loadSizing( - cs.get_mesh(), vmapFile, sizingFile - ); - - if (smooth) { - /* SMOOTH SIZING FIELD */ - std::cout << "STATUS: Smoothing sizing field." << std::endl; - auto smooth_tool = get_sizing_metric_tool( - cs.get_context(), "CreateSmoothingBase" - ); - if (smooth_tool == nullptr) { - throw std::runtime_error("unable to find \"CreateSmoothingBase\""); - } else { - smooth_tool->set_context(cs.get_context()); - M_MModel mmodel; - MG_API_CALL(cs.get_mesh(), get_current_model(mmodel)); - smooth_tool->set_metric(mmodel, "sizing6", sizing6); - std::vector ometric; - auto analysis = cs.get_analysis_name(); - smooth_tool->smooth_metric(mmodel, analysis, "sizing6", ometric); - sizing6 = ometric; - } - } else { - std::cout << "INFO: not smoothing sizing field." << std::endl; - } - - /* CREATE THE ANISOTROPIC FIELD */ - std::cout << "STATUS: Creating Anisotropic size-field from the file: " - << sizingFile << std::endl; - apf::Field* frameField = apf::createFieldOn( - apfCapMesh, "adapt_frames", apf::MATRIX - ); - apf::Field* scaleField = apf::createFieldOn( - apfCapMesh, "adapt_scales", apf::VECTOR - ); - convertMetric6toAnisoFields( - cs.get_mesh(), sizing6, frameField, scaleField - ); - - /* WRITE THE BEFORE ADAPT MESH TO VTK USING APF VTK WRITER */ - std::cout << "STATUS: Writing VTK file (before)" << std::endl; - apf::writeVtkFiles("before", apfCapMesh); - } - return apfCapMesh; -} - void migrateHome(apf::Mesh2* mesh) { auto t0 = pcu::Time(); apf::Migration* plan = new apf::Migration(mesh); diff --git a/test/capCheckParam.cc b/test/capCheckParam.cc index 2d790223f..2fec6892b 100644 --- a/test/capCheckParam.cc +++ b/test/capCheckParam.cc @@ -33,7 +33,7 @@ int main(int argc, char** argv) apf::Mesh2* mesh = apf::createCapMesh(model, &PCUobj); // check parametrization using capstone apis - checkParametrization(apf::getCapNative(mesh), gmi_export_cap(model)); + checkParametrization(apf::exportCapNative(mesh), gmi_export_cap(model)); apf::destroyMesh(mesh); gmi_cap_stop(); } // pcu object scope diff --git a/test/cap_smooth.cc b/test/cap_smooth.cc index c66cb6115..b84b742b9 100644 --- a/test/cap_smooth.cc +++ b/test/cap_smooth.cc @@ -2,7 +2,7 @@ #include int main (void) { - PCU_ALWAYS_ASSERT(apf::has_smoothCAPAnisoSizes()); + PCU_ALWAYS_ASSERT(apf::has_smoothCapAnisoSizes()); // FIXME: Test apf::smoothCAPAnisoSizes. return 0; } From d836b5cab64839e6b19d3039ffc539da5c2c0aee Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 10:49:18 -0400 Subject: [PATCH 20/26] apf_cap: use PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL - change HAVE_CAPSTONE_SIZINGMETRICTOOL to PUMI_HAS_CAPSTONEMETRICTOOL to get ahead of #477. Signed-off-by: Aiden Woodruff --- apf_cap/CMakeLists.txt | 6 ++++-- apf_cap/apfCAPsizing.cc | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apf_cap/CMakeLists.txt b/apf_cap/CMakeLists.txt index 2ef4140e3..373b893ce 100644 --- a/apf_cap/CMakeLists.txt +++ b/apf_cap/CMakeLists.txt @@ -33,12 +33,14 @@ else() endif() set(CMAKE_REQUIRED_LIBRARIES "framework_meshing") -check_include_file_cxx("CreateMG_SizingMetricTool.h" HAVE_CAPSTONE_SIZINGMETRICTOOL) +check_include_file_cxx( + "CreateMG_SizingMetricTool.h" PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL +) cmake_pop_check_state() set(CMAKE_CXX_STANDARD "${CMAKE_CXX_OLD_STANDARD}") if(HAVE_CAPSTONE_SIZINGMETRICTOOL) -target_compile_definitions(apf_cap PRIVATE HAVE_CAPSTONE_SIZINGMETRICTOOL) +target_compile_definitions(apf_cap PRIVATE PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) target_compile_features(framework_meshing INTERFACE cxx_std_14) target_link_libraries(apf_cap PRIVATE framework_meshing) endif() diff --git a/apf_cap/apfCAPsizing.cc b/apf_cap/apfCAPsizing.cc index ba3accabe..13247ad21 100644 --- a/apf_cap/apfCAPsizing.cc +++ b/apf_cap/apfCAPsizing.cc @@ -8,7 +8,7 @@ #include #include -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL +#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL #include #endif @@ -154,7 +154,7 @@ bool loadCapSizingFile( } bool has_smoothCapAnisoSizes(void) noexcept { -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL +#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL return true; #else return false; @@ -163,7 +163,7 @@ bool has_smoothCapAnisoSizes(void) noexcept { bool smoothCapAnisoSizes(apf::Mesh2* mesh, std::string analysis, apf::Field* scales, apf::Field* frames) { -#ifdef HAVE_CAPSTONE_SIZINGMETRICTOOL +#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL // Extract metric tensors from MeshAdapt frames and scales. std::vector sizing6(mesh->count(0)); apf::Matrix3x3 Q; From 99aeb5d096195d6e8769feea42265e3949903e1b Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 11:20:27 -0400 Subject: [PATCH 21/26] apf_cap: use PUMI_ENABLE_CAPSTONE - CMakeLists.txt: change capstone option to PUMI_ENABLE_CAPSTONE to avoid collision, but still recognize old ENABLE_CAPSTONE flag. - rename compile definition to PUMI_HAS_CAPSTONE - apf_cap/CMakeLists.txt, gmi_cap/CMakeLists.txt: use PUMI_ENABLE_CAPSTONE. - fix PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL. - apf/apfMesh.cc, ma/maSnap.cc: use PUMI_HAS_CAPSTONE - test/CMakeLists.txt, test/testing.cmake: use PUMI_ENABLE_CAPSTONE and PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL. Signed-off-by: Aiden Woodruff --- CMakeLists.txt | 16 ++++++++++------ apf/apfMesh.cc | 4 ++-- apf_cap/CMakeLists.txt | 4 ++-- gmi_cap/CMakeLists.txt | 2 +- ma/maSnap.cc | 8 ++++---- pumi-meshes | 2 +- test/CMakeLists.txt | 4 ++-- test/testing.cmake | 4 ++-- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b48f1d71..621afc77b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,14 +105,18 @@ set(Trilinos_PREFIX "" CACHE STRING "Trilinos installation directory") option(SKIP_SIMMETRIX_VERSION_CHECK "enable at your own risk; it may result in undefined behavior" OFF) option(ENABLE_SIMMETRIX "Build with Simmetrix support" OFF) message(STATUS "ENABLE_SIMMETRIX: ${ENABLE_SIMMETRIX}") -option(ENABLE_CAPSTONE "Build with Capstone support" OFF) -message(STATUS "ENABLE_CAPSTONE: ${ENABLE_CAPSTONE}") +option(PUMI_ENABLE_CAPSTONE "Build PUMI with Capstone support" OFF) +# For compatibility and to inherit from owning projects: +if(ENABLE_CAPSTONE) + set(PUMI_ENABLE_CAPSTONE ON) +endif() +message(STATUS "PUMI_ENABLE_CAPSTONE: ${PUMI_ENABLE_CAPSTONE}") if(ENABLE_SIMMETRIX) add_definitions(-DHAVE_SIMMETRIX) endif() -if(ENABLE_CAPSTONE) - add_definitions(-DHAVE_CAPSTONE) +if(PUMI_ENABLE_CAPSTONE) + add_definitions(-DPUMI_HAS_CAPSTONE) endif() option(ENABLE_FPP "Build with snapping to first problem plane" OFF) @@ -144,8 +148,8 @@ if(ENABLE_SIMMETRIX) find_package(SimModSuite MODULE REQUIRED) endif() -if(ENABLE_CAPSTONE) - set(SCOREC_USE_CreateMG_DEFAULT ${ENABLE_CAPSTONE}) +if(PUMI_ENABLE_CAPSTONE) + set(SCOREC_USE_CreateMG_DEFAULT ${PUMI_ENABLE_CAPSTONE}) bob_public_dep(CreateMG) endif() diff --git a/apf/apfMesh.cc b/apf/apfMesh.cc index 7f57fc983..80131a6e4 100644 --- a/apf/apfMesh.cc +++ b/apf/apfMesh.cc @@ -51,7 +51,7 @@ int const quad_edge_verts[4][2] = */ int const tet_edge_verts[6][2] = -#ifndef HAVE_CAPSTONE +#ifndef PUMI_HAS_CAPSTONE {{0,1} ,{1,2} ,{2,0} @@ -78,7 +78,7 @@ int const pyramid_edge_verts[8][2] = ,{0,4},{1,4},{2,4},{3,4}}; int const tet_tri_verts[4][3] = -#ifndef HAVE_CAPSTONE +#ifndef PUMI_HAS_CAPSTONE {{0,1,2} ,{0,1,3} ,{1,2,3} diff --git a/apf_cap/CMakeLists.txt b/apf_cap/CMakeLists.txt index 373b893ce..7ae827496 100644 --- a/apf_cap/CMakeLists.txt +++ b/apf_cap/CMakeLists.txt @@ -3,7 +3,7 @@ if(DEFINED TRIBITS_PACKAGE) return() endif() -if(NOT ENABLE_CAPSTONE) +if(NOT PUMI_ENABLE_CAPSTONE) return() endif() @@ -39,7 +39,7 @@ check_include_file_cxx( cmake_pop_check_state() set(CMAKE_CXX_STANDARD "${CMAKE_CXX_OLD_STANDARD}") -if(HAVE_CAPSTONE_SIZINGMETRICTOOL) +if(PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) target_compile_definitions(apf_cap PRIVATE PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) target_compile_features(framework_meshing INTERFACE cxx_std_14) target_link_libraries(apf_cap PRIVATE framework_meshing) diff --git a/gmi_cap/CMakeLists.txt b/gmi_cap/CMakeLists.txt index a932b79f6..8df250997 100644 --- a/gmi_cap/CMakeLists.txt +++ b/gmi_cap/CMakeLists.txt @@ -3,7 +3,7 @@ if(DEFINED TRIBITS_PACKAGE) return() endif() -if(NOT ENABLE_CAPSTONE) +if(NOT PUMI_ENABLE_CAPSTONE) return() endif() diff --git a/ma/maSnap.cc b/ma/maSnap.cc index e754af553..4fea96021 100644 --- a/ma/maSnap.cc +++ b/ma/maSnap.cc @@ -68,7 +68,7 @@ static size_t isSurfUnderlyingFaceDegenerate( m->getFirstDerivative(g, p, uTan, vTan); double uTanSize = uTan.getLength(); double vTanSize = vTan.getLength(); -#ifdef HAVE_CAPSTONE +#ifdef PUMI_HAS_CAPSTONE uTanSize = uTan * uTan; vTanSize = vTan * vTan; #endif @@ -150,7 +150,7 @@ static void interpolateParametricCoordinateOnEdge( p[1] = 0.0; p[2] = 0.0; -#ifdef HAVE_CAPSTONE +#ifdef PUMI_HAS_CAPSTONE // account for non-uniform parameterization of model-edge Vector X[3]; Vector para[2] = {a, b}; @@ -491,7 +491,7 @@ static void interpolateParametricCoordinatesOnRegularFace( * 2) we only check for faces that are periodic */ -#ifndef HAVE_CAPSTONE +#ifndef PUMI_HAS_CAPSTONE // this need to be done for faces, only if (dim != 2) return; @@ -526,7 +526,7 @@ static void interpolateParametricCoordinatesOnFace( size_t num = isSurfUnderlyingFaceDegenerate(m, g, axes, vals); if (num > 0) { // the underlying surface is degenerate -#ifndef HAVE_CAPSTONE +#ifndef PUMI_HAS_CAPSTONE interpolateParametricCoordinatesOnDegenerateFace(m, g, t, a, b, axes, vals, p); #else // account for non-uniform parameterization of model-edge diff --git a/pumi-meshes b/pumi-meshes index 7be0e261d..27dca7523 160000 --- a/pumi-meshes +++ b/pumi-meshes @@ -1 +1 @@ -Subproject commit 7be0e261d69d45a6f0614c76c66eff2bba81f4ba +Subproject commit 27dca75239ed386594b7eb35d69c2d58fb28cfe3 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8a6915688..2b7dc303b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -216,12 +216,12 @@ if(ENABLE_SIMMETRIX) test_exe_func(highOrderSizeFields highOrderSizeFields.cc) endif() -if(ENABLE_CAPSTONE) +if(PUMI_ENABLE_CAPSTONE) util_exe_func(capVol capVol.cc) target_include_directories(capVol PRIVATE "${PROJECT_SOURCE_DIR}/capstone_clis") test_exe_func(cap_inClosureOf cap_inClosureOf.cc) test_exe_func(cap_closestPoint cap_closestPoint.cc) - if(HAVE_CAPSTONE_SIZINGMETRICTOOL) + if(PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) util_exe_func(cap_smooth cap_smooth.cc) endif() util_exe_func(cap2vtk cap2vtk.cc) diff --git a/test/testing.cmake b/test/testing.cmake index 36397491d..507420cbe 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -978,7 +978,7 @@ if (PCU_COMPRESS) WORKING_DIRECTORY ${MDIR}/run) endif() -if(ENABLE_CAPSTONE) +if(PUMI_ENABLE_CAPSTONE) set(MDIR "${MESHES}/cap") mpi_test(capVol_Wing 1 ./capVol 2 ${MDIR}/wing_surf_only.cre capVol_Wing.cre ) @@ -1016,7 +1016,7 @@ if(ENABLE_CAPSTONE) endif() mpi_test(cap_inClosureOf 1 ./cap_inClosureOf ${MESHES}/cap/cube_surf_only.cre) mpi_test(cap_closestPoint 1 ./cap_closestPoint ${MESHES}/cap/cube_surf_only.cre) - if(HAVE_CAPSTONE_SIZINGMETRICTOOL) + if(PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) mpi_test(cap_smooth 1 ./cap_smooth) endif() endif() From b6c2f9e68e2867bec65f7ebe7bc1fdcae30f3d9f Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 14:28:04 -0400 Subject: [PATCH 22/26] apf_cap: add extractCapSizing - apf_cap/apfCAP.h: add extractCapSizing. - apf_cap/apfCAPsizing.cc: refactor first part of smoothCapAnisoSizes into extractCapSizing and then use the function for smoothing. Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.h | 6 ++++++ apf_cap/apfCAPsizing.cc | 46 +++++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index 48edbe293..438bcc5b1 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -194,6 +194,12 @@ bool loadCapSizingFile( bool smooth = false, const std::string& analysis = "" ); +// Extract metric tensors from MeshAdapt frames and scales. +void extractCapSizing( + apf::Mesh2* m, apf::Field* scales, apf::Field* frames, + std::vector& sizing +); + /** * \brief Test for smoothCAPAnisoSizes support. * diff --git a/apf_cap/apfCAPsizing.cc b/apf_cap/apfCAPsizing.cc index 13247ad21..75c958249 100644 --- a/apf_cap/apfCAPsizing.cc +++ b/apf_cap/apfCAPsizing.cc @@ -153,21 +153,13 @@ bool loadCapSizingFile( ); } -bool has_smoothCapAnisoSizes(void) noexcept { -#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL - return true; -#else - return false; -#endif -} - -bool smoothCapAnisoSizes(apf::Mesh2* mesh, std::string analysis, - apf::Field* scales, apf::Field* frames) { -#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL - // Extract metric tensors from MeshAdapt frames and scales. - std::vector sizing6(mesh->count(0)); +void extractCapSizing( + apf::Mesh2* mesh, apf::Field* scales, apf::Field* frames, + std::vector& sizing +) { apf::Matrix3x3 Q; apf::Vector3 H; + sizing.resize(mesh->count(0)); apf::MeshIterator* it = mesh->begin(0); for (apf::MeshEntity* e = mesh->iterate(it); e; e = mesh->iterate(it)) { apf::getVector(scales, e, 0, H); // Desired element lengths. @@ -181,15 +173,29 @@ bool smoothCapAnisoSizes(apf::Mesh2* mesh, std::string analysis, size_t id = getCapId(mesh, e); PCU_DEBUG_ASSERT(id != 0); --id; - sizing6[id][0] = t[0][0]; - sizing6[id][1] = t[0][1]; - sizing6[id][2] = t[0][2]; - sizing6[id][3] = t[1][1]; - sizing6[id][4] = t[1][2]; - sizing6[id][5] = t[2][2]; + sizing[id][0] = t[0][0]; + sizing[id][1] = t[0][1]; + sizing[id][2] = t[0][2]; + sizing[id][3] = t[1][1]; + sizing[id][4] = t[1][2]; + sizing[id][5] = t[2][2]; } mesh->end(it); - std::vector ometric; +} + +bool has_smoothCapAnisoSizes(void) noexcept { +#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL + return true; +#else + return false; +#endif +} + +bool smoothCapAnisoSizes(apf::Mesh2* mesh, std::string analysis, + apf::Field* scales, apf::Field* frames) { +#ifdef PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL + std::vector sizing6, ometric; + extractCapSizing(mesh, scales, frames, sizing6); if (!doSmoothing(mesh, analysis, sizing6, ometric)) return false; return loadCapSizing(mesh, ometric, frames, scales); #else From 01aaa8b47e4d086ea2ca40a6e9e9341dca51a90f Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 16:05:14 -0400 Subject: [PATCH 23/26] apf_cap: add documentation for new functions - apf_cap/apfCAP.h: add apf_cap_sizing subgroup and document new functions. added direction since it's non-obvious based on const-ness alone. Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.h | 85 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index 438bcc5b1..ca6fee572 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -172,29 +172,108 @@ MeshEntity* getCapEntity(Mesh2* m, int dimension, size_t id); */ CreateMG::MDBI* exportCapNative(Mesh2* capMesh); +/** + * \defgroup apf_cap_sizing Capstone APF mesh sizing utilities + * \{ + */ + +/** + * \brief Load Capstone bulk sizing into apf::Fields. + * + * \param[in] m An apf Capstone mesh + * \param[in] sizing A bulk sizing vector of Metric6 from Capstone + * \param[out] scales An apf::Field of apf::Vector3's on vertices for + * anisotropic sizing scales + * \param[out] frames An apf::Field of apf::Matrix3x3's on vertices for + * anisotropic sizing frames + * \return true on success, false otherwise + */ bool loadCapSizing( apf::Mesh2* m, const std::vector& sizing, apf::Field* scales, apf::Field* frames ); +/** + * \brief Load Capstone bulk sizing into apf::Fields. + * + * This overload is a convenience wrapper to simplify Field creation. + * + * \param[in] m An apf Capstone mesh + * \param[in] sizing A bulk sizing vector of Metric6 from Capstone + * \param[out] scales The name of a new apf::Field to create with anisotropic + * sizing scales + * \param[out] frames The name of a new apf::Field to create with anisotropic + * sizing frames + * \return true on success, false otherwise + */ bool loadCapSizing( apf::Mesh2* m, const std::vector& sizing, const char* scales, const char* frames ); +/** + * \brief Load Capstone bulk sizing into apf::Fields from a sizing file. + * + * The bulk sizing file contains 6 doubles for each vertex. The vmap file + * contains 1 size_t for each vertex, plus an extra one with vertex count + * information. The vmap contains the info to convert solver ids to mesh ids. + * + * \param[in] m An apf Capstone mesh + * \param[in] sizingFile The name of the bulk sizing file. + * \param[in] vmapFile The name of the vmap file. + * \param[out] scales An apf::Field of apf::Vector3's on vertices with + * anisotropic sizing scales + * \param[out] frames An apf::Field of apf::Matrix3x3's on vertices with + * anisotropic sizing frames + * \param[in] smooth A boolean to request smoothing before loading to + * apf::Fields + * \param[in] analysis The analysis which may contain additional sizing + * information (passed to Capstone smoothing routine) + * \return true on success, false on failure or if smoothing is requested but + * not supported + */ bool loadCapSizingFile( apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, apf::Field* scales, apf::Field* frames, bool smooth = false, const std::string& analysis = "" ); +/** + * \brief Load Capstone bulk sizing into apf::Fields from a sizing file. + * + * The bulk sizing file contains 6 doubles for each vertex. The vmap file + * contains 1 size_t for each vertex, plus an extra one with vertex count + * information. The vmap contains the info to convert solver ids to mesh ids. + * + * This overload is a convenience wrapper to simplify Field creation. + * + * \param[in] m An apf Capstone mesh + * \param[in] sizingFile The name of the bulk sizing file. + * \param[in] vmapFile The name of the vmap file. + * \param[out] scales The name of a new apf::Field to create with anisotropic + * sizing scales + * \param[out] frames The name of a new apf::Field to create with anisotropic + * sizing frames + * \param[in] smooth Whether to request smoothing before loading to apf::Fields + * \param[in] analysis The analysis which may contain additional sizing + * information (passed to Capstone smoothing routine) + * \return true on success, false on failure or if smoothing is requested but + * not supported + */ bool loadCapSizingFile( apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, const char* scales, const char* frames, bool smooth = false, const std::string& analysis = "" ); -// Extract metric tensors from MeshAdapt frames and scales. +/** + * \brief Extract Capstone metric tensors from MeshAdapt frames and scales. + * + * \param[in] m An apf Capstone mesh + * \param[in] scales An apf::Field with anisotropic sizing scales + * \param[in] frames An apf::Field with anisotropic sizing frames + * \param[out] sizing A vector to output metric tensors to + */ void extractCapSizing( apf::Mesh2* m, apf::Field* scales, apf::Field* frames, std::vector& sizing @@ -227,7 +306,9 @@ bool smoothCapAnisoSizes( apf::Mesh2* m, std::string analysis, apf::Field* scales, apf::Field* frames ); -/** \} */ +/** \} apf_cap_sizing */ + +/** \} apf_cap */ } // namespace apf From 1b30ee3459fc2bddef148c76015d3efdfb501702 Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Mon, 30 Jun 2025 17:04:42 -0400 Subject: [PATCH 24/26] test: add capAdapt -B and -A flags - test/capAdapt.cc: make Args a struct and remove accessors in favor of simple members. - add before.vtk and after.vtk prefix name arguments - only write vtks if those arguments are given Signed-off-by: Aiden Woodruff --- test/capAdapt.cc | 83 ++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/test/capAdapt.cc b/test/capAdapt.cc index 52ceb9973..f194691bc 100644 --- a/test/capAdapt.cc +++ b/test/capAdapt.cc @@ -46,38 +46,31 @@ void printEdgeCuts(apf::Mesh2 *m); /** \brief Migrate all elements back to rank 0. */ void migrateHome(apf::Mesh2* mesh); -/** \brief Take a serial MDS mesh, partition, adapt, then localize. */ -void parallelAdapt(ma::Mesh* mesh, gmi_model* mod, pcu::PCU& PCU, int maxiter); - /** \brief Command line argument processing class. */ -class Args { +struct Args { public: Args() {} Args(int argc, char* argv[]) { parse(argc, argv); } void parse(int argc, char* argv[]); void print_usage(const char* argv0) const; /** - * \brief Get maximum iterations argument. + * \brief maximum iterations argument. * * Pass -1 to trust MeshAdapt or a number <= 10 to specify max iterations. */ - int maxiter() const noexcept { return maxiter_; } + int maxiter{-1}; /** * \brief Get smoothing argument. If true, request smoothing if supported. */ - bool smooth() const noexcept { return smooth_; } - - const std::string& creFilename() const noexcept { return creFilename_; } - const std::string& sizingFilename() const noexcept { - return sizingFilename_; - } -private: - int maxiter_{-1}; - bool smooth_{false}; - std::string creFilename_; - std::string sizingFilename_; + bool smooth{false}; + std::string creFilename, sizingFilename, before, after; }; +/** \brief Take a serial MDS mesh, partition, adapt, then localize. */ +void parallelAdapt( + ma::Mesh* mesh, gmi_model* mod, pcu::PCU& PCU, const Args& args +); + } // namespace int main(int argc, char** argv) { @@ -107,13 +100,13 @@ int main(int argc, char** argv) { std::cout << "STATUS: Init Capstone" << std::endl; if (PCU.Self() == 0) - std::cout << "STATUS: Loading CRE file : " << args.creFilename(); + std::cout << "STATUS: Loading CRE file : " << args.creFilename; gmi_model* model = nullptr; // Load CRE file sequentially to avoid NFS errors. for (int i = 0; i < PCU.Peers(); ++i) { if (PCU.Self() == i) { - if (PCU.Self() == 0) model = gmi_load(args.creFilename().c_str()); - else model = gmi_cap_load_selective(args.creFilename().c_str(), {}); + if (PCU.Self() == 0) model = gmi_load(args.creFilename.c_str()); + else model = gmi_cap_load_selective(args.creFilename.c_str(), {}); } PCU.Barrier(); } @@ -130,13 +123,12 @@ int main(int argc, char** argv) { ma::Mesh* apfCapMesh = apf::createCapMesh(model, soloPCU.get()); apf::disownCapModel(apfCapMesh); if (!apf::loadCapSizingFile( - apfCapMesh, args.sizingFilename(), - get_no_extension(args.creFilename()) + ".avm.vmap", + apfCapMesh, args.sizingFilename, + get_no_extension(args.creFilename) + ".avm.vmap", "adapt_scales", "adapt_frames", true, "Kestrel" )) { throw std::runtime_error("failed to load sizing file"); } - apf::writeVtkFiles("before", apfCapMesh); /* CONVERT TO APF MDS MESH AND WRITE VTK */ std::cout << "STATUS: Convert to APF MDS Mesh" << std::endl; double t_start = pcu::Time(); @@ -148,12 +140,11 @@ int main(int argc, char** argv) { apf::destroyMesh(apfCapMesh); apfMesh->verify(); apf::printStats(apfMesh); - apf::writeVtkFiles("before-mds", apfMesh); } PCU.Barrier(); - parallelAdapt(apfMesh, model, PCU, args.maxiter()); + parallelAdapt(apfMesh, model, PCU, args); if (original) { apf::Mesh2* mdsMesh = apfMesh; @@ -168,7 +159,7 @@ int main(int argc, char** argv) { apf::destroyMesh(mdsMesh); /* SAVE FINAL CRE FILE */ - auto creOFileName = get_no_extension(args.creFilename()) + auto creOFileName = get_no_extension(args.creFilename) + "_adapted.cre"; gmi_cap_write(model, creOFileName.c_str()); apf::destroyMesh(apfMesh); @@ -218,7 +209,9 @@ void migrateHome(apf::Mesh2* mesh) { << " seconds" << std::endl; } -void parallelAdapt(ma::Mesh* mesh, gmi_model* model, pcu::PCU& PCU, int maxiter) { +void parallelAdapt( + ma::Mesh* mesh, gmi_model* model, pcu::PCU& PCU, const Args& args +) { auto t0 = pcu::Time(); bool original = false; pcu::PCU* oldPCU = nullptr; @@ -256,10 +249,12 @@ void parallelAdapt(ma::Mesh* mesh, gmi_model* model, pcu::PCU& PCU, int maxiter) ); plan = nullptr; // plan is freed by apf::repeatMdsMesh. apf::printStats(mesh); - apf::writeVtkFiles("before-mds-split", mesh); } apf::disownMdsModel(mesh); printEdgeCuts(mesh); + if (!args.before.empty()) { + apf::writeVtkFiles(args.before.c_str(), mesh); + } apf::Field* mdsScaleField = mesh->findField("adapt_scales"); apf::Field* mdsFrameField = mesh->findField("adapt_frames"); @@ -273,14 +268,16 @@ void parallelAdapt(ma::Mesh* mesh, gmi_model* model, pcu::PCU& PCU, int maxiter) ma::configure(mesh, mdsScaleField, mdsFrameField) ); in->shouldForceAdaptation = true; - if (maxiter != -1) in->maximumIterations = maxiter; + if (args.maxiter != -1) in->maximumIterations = args.maxiter; if (original) std::cout << "STATUS: Adapting" << std::endl; ma::adapt(in); /* WRITE THE AFTER ADAPT MESH TO VTK USING APF VTK WRITER */ - if (original) std::cout << "STATUS: Writing VTK file (after)" << std::endl; - apf::writeVtkFiles("after-mds", mesh); + if (!args.after.empty()) { + if (original) std::cout << "STATUS: Writing VTK file (after)" << std::endl; + apf::writeVtkFiles(args.after.c_str(), mesh); + } if (parts > 1) migrateHome(mesh); if (original) { @@ -324,20 +321,30 @@ void Args::parse(int argc, char* argv[]) { for (int i = 1; i < argc; ++i) { if (argv[i][0] == '-') { switch (argv[i][1]) { + case 'B': + if (argv[i][2]) before = &argv[i][2]; + else if (++i < argc) before = argv[i]; + else throw std::invalid_argument("missing argument to -B"); + break; + case 'A': + if (argv[i][2]) after = &argv[i][2]; + else if (++i < argc) after = argv[i]; + else throw std::invalid_argument("missing argument to -A"); + break; case 'i': - if (argv[i][2]) maxiter_ = std::stoi(&argv[i][2]); - else if (++i < argc) maxiter_ = std::stoi(argv[i]); + if (argv[i][2]) maxiter = std::stoi(&argv[i][2]); + else if (++i < argc) maxiter = std::stoi(argv[i]); else throw std::invalid_argument("missing argument to -i"); break; case 's': - smooth_ = true; + smooth = true; break; default: throw std::invalid_argument("invalid option -" + argv[i][1]); } } else { - if (creFilename_.empty()) creFilename_ = argv[i]; - else if (sizingFilename_.empty()) sizingFilename_ = argv[i]; + if (creFilename.empty()) creFilename = argv[i]; + else if (sizingFilename.empty()) sizingFilename = argv[i]; else { throw std::invalid_argument( std::string("invalid argument `") + argv[i] + "`" @@ -348,8 +355,8 @@ void Args::parse(int argc, char* argv[]) { } void Args::print_usage(const char* argv0) const { - std::cout << "USAGE: " << argv0 << " [-i MAXITER] [-s] INPUT.CRE SIZING.DAT" - << std::endl; + std::cout << "USAGE: " << argv0 << " [-B BEFORE.VTK] [-A AFTER.VTK]" + " [-i MAXITER] [-s] INPUT.CRE SIZING.DAT" << std::endl; } } // namsepace From 32c246e252b77d093eb173fdc1981e27607eef4a Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Tue, 1 Jul 2025 12:40:03 -0400 Subject: [PATCH 25/26] test: update capCheckParam - test/capCheckParam.cc: use apf APIs to call the same underlying Capstone ones. - reformat table. - check edges as well as faces. - enable lion error messages - test/CMakeLists.txt: remove obsolete capVol include dir. - remove obsolete capCheckParam link libraries. Signed-off-by: Aiden Woodruff --- test/CMakeLists.txt | 2 -- test/capCheckParam.cc | 83 +++++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2b7dc303b..094ecc8c0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -218,7 +218,6 @@ endif() if(PUMI_ENABLE_CAPSTONE) util_exe_func(capVol capVol.cc) - target_include_directories(capVol PRIVATE "${PROJECT_SOURCE_DIR}/capstone_clis") test_exe_func(cap_inClosureOf cap_inClosureOf.cc) test_exe_func(cap_closestPoint cap_closestPoint.cc) if(PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) @@ -227,7 +226,6 @@ if(PUMI_ENABLE_CAPSTONE) util_exe_func(cap2vtk cap2vtk.cc) util_exe_func(capGeomTest capGeomTest.cc) util_exe_func(capCheckParam capCheckParam.cc) - target_link_libraries(capCheckParam framework_geometry framework_mesh) util_exe_func(capAdapt capAdapt.cc) util_exe_func(capProbe capProbe.cc) util_exe_func(capLoadSome capLoadSome.cc) diff --git a/test/capCheckParam.cc b/test/capCheckParam.cc index 2fec6892b..d008f3dbe 100644 --- a/test/capCheckParam.cc +++ b/test/capCheckParam.cc @@ -1,16 +1,15 @@ #include +#include #include #include #include #include #include +#include #include -#include -#include - -void checkParametrization(CreateMG::MDBI* mdb, CreateMG::GDBI* gdb); +void checkParametrization(apf::Mesh* mesh); int main(int argc, char** argv) { @@ -26,6 +25,7 @@ int main(int argc, char** argv) const char* creFileName = argv[1]; + lion_set_verbosity(1); gmi_cap_start(); gmi_register_cap(); @@ -33,53 +33,58 @@ int main(int argc, char** argv) apf::Mesh2* mesh = apf::createCapMesh(model, &PCUobj); // check parametrization using capstone apis - checkParametrization(apf::exportCapNative(mesh), gmi_export_cap(model)); + checkParametrization(mesh); apf::destroyMesh(mesh); gmi_cap_stop(); } // pcu object scope pcu::Finalize(); } -void checkParametrization(CreateMG::MDBI* mdb, CreateMG::GDBI* gdb) { - using namespace CreateMG; - using namespace CreateMG::Mesh; - MeshSmartIterator miter(mdb); - mdb->get_topo_iterator(TOPO_VERTEX, miter); - int count = 0; +void checkParametrization(apf::Mesh* mesh) { + int count[2] = {0, 0}; double sum = 0.0; - for(mdb->iterator_begin(miter); !mdb->iterator_end(miter); mdb->iterator_next(miter)) { - M_MTopo vert = mdb->iterator_value(miter); - M_GTopo geom; - GeometryTopoType gtype; - mdb->get_geom_entity(vert, gtype, geom); - if (!gdb->is_face(geom)) continue; - double range_u[2]; - double range_v[2]; - gdb->get_parametrization_range(geom, 0, range_u[0], range_u[1]); - gdb->get_parametrization_range(geom, 1, range_v[0], range_v[1]); - GeometryTopoType gtype1; - double u,v; - mdb->get_vertex_uv_parameters(vert, u, v, gtype1); - PCU_ALWAYS_ASSERT(gtype1 == gtype); - + std::cout << + "ct, ( u, v), ( umin, umax), " + "( vmin, vmax), diff\n"; + apf::MeshIterator* it = mesh->begin(0); + for (apf::MeshEntity* vtx; (vtx = mesh->iterate(it));) { + apf::ModelEntity* me = mesh->toModel(vtx); + int dim = mesh->getModelType(me); + if (dim != 1 && dim != 2) continue; + double range_u[2], range_v[2]; + mesh->getPeriodicRange(me, 0, range_u); + mesh->getPeriodicRange(me, 1, range_v); + apf::Vector3 xi; + mesh->getParam(vtx, xi); // coordinate from mesh apf::Vector3 coord; - mdb->get_vertex_coord(vert, &(coord[0])); - + mesh->getPoint(vtx, 0, coord); // coordinate from surface - vec3d x; - gdb->get_point(geom, vec3d(u, v, 0.0), x); - apf::Vector3 pcoord(x[0], x[1], x[2]); + apf::Vector3 pcoord; + mesh->snapToModel(me, xi, pcoord); + apf::Vector3 diff = coord - pcoord; - if (count < 50) { - std::cout << count << ", " - << u << ", " << v << ", " - << range_u[0] << ", " << range_u[1] - << ", " << range_v[0] << ", " << range_v[1] - << ", " << (coord - pcoord).getLength() << std::endl; + constexpr int dim_printmax = 25; + if (count[dim - 1] < dim_printmax) { + std::cout << std::setw(2) << count[dim - 1] << ", " + << std::setprecision(3) << std::scientific + << '(' << std::setw(8) << xi.x() << ", " + << std::setw(8) << xi.y() << "), " + << '(' << std::setw(4) << range_u[0] << ", " + << std::setw(4) << range_u[1] << "), "; + if (dim == 2) + std::cout << '(' << std::setw(4) << range_v[0] << ", " + << std::setw(4) << range_v[1] << "), "; + else + std::cout << "----------------------, "; + std::cout << std::setw(4) << std::defaultfloat << diff.getLength() + << std::endl; + } else if (count[dim - 1] == dim_printmax) { + std::cout << "Skipping printing remaining " + << (dim == 1 ? "edges" : "faces") << std::endl; } - sum += (coord-pcoord) * (coord-pcoord); - count++; + sum += diff * diff; + count[dim - 1]++; } std::cout << "norm of the difference vector is " << std::sqrt(sum) << std::endl; From 912a7ddb198b062942cecdda93123888a9c3b94b Mon Sep 17 00:00:00 2001 From: Aiden Woodruff Date: Wed, 2 Jul 2025 13:50:05 -0400 Subject: [PATCH 26/26] test: add cap native adapt test - test/capNativeAdapt.cc: similar to simxAnisoAdapt, add utility to run native capstone adaptation for testing/timing purposes. - test/CMakeLists.txt: add capNativeAdapt. - apf_cap/apfCAP.h: add loadCapSizingFileMetrics to facilitate loading sizing/vmap files. - apf_cap/apfCAPsizing.cc (load_file): add error checking for file open failure. - (loadCapSizingFileMetrics): refactor file loading/remapping part of loadCapSizingFile into this function. doesn't require writing new code and allows me to keep the SFINAE load_file function limited to apfCAPsizing.cc Signed-off-by: Aiden Woodruff --- apf_cap/apfCAP.h | 13 +++++ apf_cap/apfCAPsizing.cc | 33 ++++++++---- test/CMakeLists.txt | 6 +++ test/capNativeAdapt.cc | 114 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 test/capNativeAdapt.cc diff --git a/apf_cap/apfCAP.h b/apf_cap/apfCAP.h index ca6fee572..59feba225 100644 --- a/apf_cap/apfCAP.h +++ b/apf_cap/apfCAP.h @@ -211,6 +211,19 @@ bool loadCapSizing( const char* scales, const char* frames ); +/** + * \brief Load metrics from a sizing file and vmap file. + * + * \param m An apf Capstone mesh + * \param sizingFile The name of the bulk sizing file + * \param vmapFile The name of the vmap file + * \param sizing6 A vector to fill with bulk sizing metrics + */ +void loadCapSizingFileMetrics( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + std::vector& sizing6 +); + /** * \brief Load Capstone bulk sizing into apf::Fields from a sizing file. * diff --git a/apf_cap/apfCAPsizing.cc b/apf_cap/apfCAPsizing.cc index 75c958249..38b08e5e7 100644 --- a/apf_cap/apfCAPsizing.cc +++ b/apf_cap/apfCAPsizing.cc @@ -1,6 +1,8 @@ #include "apfCAP.h" +#include #include +#include #include #include @@ -20,6 +22,9 @@ typename std::enable_if::value, void>::type load_file(const std::string& fname, std::vector& data) { try { std::ifstream file(fname, std::ios::in | std::ios::binary); + if (!file.is_open()) { + throw std::runtime_error("failed to open file `" + fname + "`"); + } T val; while (file) { file.read(reinterpret_cast(std::addressof(val)), sizeof(T)); @@ -96,17 +101,10 @@ bool loadCapSizing( return loadCapSizing(m, sizing, scalesField, framesField); } -bool loadCapSizingFile( +void loadCapSizingFileMetrics( apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, - apf::Field* scales, apf::Field* frames, - bool smooth, const std::string& analysis + std::vector& sizing6 ) { - if (smooth && !has_smoothCapAnisoSizes()) { - lion_eprint(1, - "WARNING: loadCapSizingFile: smoothing requested but apf_cap was" - " compiled without support for smoothing\n"); - return false; - } std::vector vmap; std::vector sizing; load_file(vmapFile, vmap); @@ -125,7 +123,7 @@ bool loadCapSizingFile( fail("loadCapSizingFile: loaded invalid vmap"); } // Copy sizefield to Metric6 - std::vector sizing6(vmap[0]); + sizing6.resize(vmap[0]); for (size_t si = 0, vi = 1; vi < vmap.size(); ++vi) { size_t tid = vmap[vi]; PCU_DEBUG_ASSERT(tid != 0); @@ -133,6 +131,21 @@ bool loadCapSizingFile( sizing6[tid - 1][j] = sizing[si]; } } +} + +bool loadCapSizingFile( + apf::Mesh2* m, const std::string& sizingFile, const std::string& vmapFile, + apf::Field* scales, apf::Field* frames, + bool smooth, const std::string& analysis +) { + if (smooth && !has_smoothCapAnisoSizes()) { + lion_eprint(1, + "WARNING: loadCapSizingFile: smoothing requested but apf_cap was" + " compiled without support for smoothing\n"); + return false; + } + std::vector sizing6; + loadCapSizingFileMetrics(m, sizingFile, vmapFile, sizing6); if (smooth) { std::vector ometric; if (!doSmoothing(m, analysis, sizing6, ometric)) return false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 094ecc8c0..f6d2ec6aa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -229,6 +229,12 @@ if(PUMI_ENABLE_CAPSTONE) util_exe_func(capAdapt capAdapt.cc) util_exe_func(capProbe capProbe.cc) util_exe_func(capLoadSome capLoadSome.cc) + if(PUMI_HAS_CAPSTONE_SIZINGMETRICTOOL) + util_exe_func(capNativeAdapt capNativeAdapt.cc) + target_link_libraries(capNativeAdapt + framework_application framework_mesh framework_meshing + ) + endif() endif() # send all the newly added utility executable targets diff --git a/test/capNativeAdapt.cc b/test/capNativeAdapt.cc new file mode 100644 index 000000000..a9afc9bad --- /dev/null +++ b/test/capNativeAdapt.cc @@ -0,0 +1,114 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace { + +/** \brief Print nested exceptions. */ +void print_exception(const std::exception& e, int level = 0); + +} // namespace + +int main(int argc, char* argv[]) { + lion_set_verbosity(1); + pcu::Init(&argc, &argv); + try { + pcu::PCU PCU; + if (argc < 4) { + std::cerr << "USAGE: " << argv[0] << " " + " " << std::endl; + throw 1; + } + + const char *creFile = argv[1], *outFile = argv[2], *sizingFile = argv[3], + *vmapFile = argv[4]; + + gmi_cap_start(); + gmi_register_cap(); + try { + gmi_model* model = gmi_cap_load(creFile); + apf::Mesh2* mesh = apf::createCapMesh(model, &PCU); + try { + auto mdbi = apf::exportCapNative(mesh); + auto ctx = mdbi->get_context(); + auto proc = CreateMG::get_context_processor(ctx); + auto fn = CreateMG::get_function(ctx, "AdaptMesh"); + CreateMG::M_MModel mmodel; + MG_API_CALL(mdbi, get_current_model(mmodel)); + CreateMG::set_input(fn, "MeshModel", mmodel); + // CreateMG::set_input(fn, "Analysis", mmodel); + auto sTool = CreateMG::get_sizing_metric_tool( + ctx, "CreateSmoothingBase" + ); + PCU_ALWAYS_ASSERT(sTool); + sTool->set_context(ctx); + std::vector sizing6; + apf::loadCapSizingFileMetrics(mesh, sizingFile, vmapFile, sizing6); + sTool->set_metric(mmodel, "sizing6", sizing6); + CreateMG::set_input(fn, "TensorData", "sizing6"); + // DiscreteCurvature, Complexity, MaxComplexity + auto t0 = pcu::Time(); + if (proc->execute(fn) != CreateMG::STATUS_OK) { + std::cerr << "ERROR: failed to adapt mesh" << std::endl; + throw 1; + } + auto t1 = pcu::Time(); + std::cout << "Capstone AdaptMesh ran in " << t1 - t0 << std::endl; + CreateMG::get_output(fn, "Output", mmodel); + std::string oname; + MG_API_CALL(mdbi, get_model_name(mmodel, oname)); + apf::Mesh2* omesh = apf::createCapMesh(model, oname.c_str(), &PCU); + apf::disownCapModel(omesh); + apf::writeVtkFiles((std::string(outFile) + ".vtk").c_str(), omesh); + gmi_cap_write(model, outFile); + apf::destroyMesh(omesh); + } catch (...) { + apf::destroyMesh(mesh); + std::rethrow_exception(std::current_exception()); + } + apf::destroyMesh(mesh); + } catch (const std::exception& e) { + if (PCU.Self() == 0) { + std::cerr << "ERROR: "; + print_exception(e); + } + gmi_cap_stop(); + throw 1; + } catch (...) { + gmi_cap_stop(); + throw 1; + } + gmi_cap_stop(); + } catch (...) { + pcu::Finalize(); + return 1; + } + pcu::Finalize(); + return 0; +} + +namespace { + +void print_exception(const std::exception& e, int level) { + std::cerr << std::string(level * 2, ' ') << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch (const std::exception& nestedE) { + print_exception(nestedE, level + 1); + } catch (...) {} +} + +} // namespace