From bae6ee30cdd2fcf9c82bea38e7b940c28bd396f2 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Fri, 23 May 2025 14:25:23 +0200 Subject: [PATCH 1/5] Added: Find rule for FMX library --- cmake/Modules/FindFMX.cmake | 12 ++++++++++++ cmake/Modules/FindIFEMDeps.cmake | 33 +++++++++++++++++++------------- cmake/Modules/IFEMOptions.cmake | 6 +++--- 3 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 cmake/Modules/FindFMX.cmake diff --git a/cmake/Modules/FindFMX.cmake b/cmake/Modules/FindFMX.cmake new file mode 100644 index 000000000..0c55ccd93 --- /dev/null +++ b/cmake/Modules/FindFMX.cmake @@ -0,0 +1,12 @@ +IF(FMX_LIBRARY) + SET(FMX_FIND_QUIETLY TRUE) +ENDIF(FMX_LIBRARY) + +FIND_LIBRARY(FMX_LIBRARY NAMES fmxWriter + PATHS $ENV{HOME}/lib $ENV{HOME}/.local/lib /usr/local/lib +) + +MARK_AS_ADVANCED(FMX_LIBRARY) + +INCLUDE(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FMX DEFAULT_MSG FMX_LIBRARY) diff --git a/cmake/Modules/FindIFEMDeps.cmake b/cmake/Modules/FindIFEMDeps.cmake index 3dd8215ba..11f5d0e45 100644 --- a/cmake/Modules/FindIFEMDeps.cmake +++ b/cmake/Modules/FindIFEMDeps.cmake @@ -190,10 +190,9 @@ ENDIF(SPR_USE_INT4 OR SPR_USE_INT8) IF(IFEM_USE_SAMG) FIND_PACKAGE(SAMG) IF(SAMG_LIBRARIES AND SAMG_INCLUDES) - SET(IFEM_DEPLIBS ${IFEM_DEPLIBS} ${SAMG_LIBRARIES}) - SET(IFEM_DEPINCLUDES ${IFEM_DEPINCLUDES} ${SAMG_INCLUDES}) - SET(IFEM_BUILD_CXX_FLAGS "${IFEM_BUILD_CXX_FLAGS} -DHAS_SAMG -DSAMG_UNIX_LINUX=1 -DSAMG_LCASE_USCORE") - list(APPEND IFEM_DEFINITIONS -DHAS_SAMG -DSAMG_UNIX_LINUX=1 -DSAMG_LCASE_USCORE) + list(APPEND IFEM_DEPLIBS ${SAMG_LIBRARIES}) + list(APPEND IFEM_DEPINCLUDES ${SAMG_INCLUDES}) + string(APPEND IFEM_BUILD_CXX_FLAGS " -DHAS_SAMG -DSAMG_UNIX_LINUX=1 -DSAMG_LCASE_USCORE") ENDIF(SAMG_LIBRARIES AND SAMG_INCLUDES) ENDIF(IFEM_USE_SAMG) @@ -201,10 +200,8 @@ ENDIF(IFEM_USE_SAMG) IF(IFEM_USE_VTFWRITER) FIND_PACKAGE(VTFWriter) IF(VTFWRITER_LIBRARIES AND VTFWRITER_INCLUDES) - SET(IFEM_DEPLIBS ${IFEM_DEPLIBS} ${VTFWRITER_LIBRARIES}) - SET(IFEM_DEPINCLUDES ${IFEM_DEPINCLUDES} ${VTFWRITER_INCLUDES}) - SET(IFEM_CXX_FLAGS "${IFEM_CXX_FLAGS} -DHAS_VTFAPI=${VTFAPI}") - SET(IFEM_BUILD_CXX_FLAGS "${IFEM_BUILD_CXX_FLAGS} -DHAS_VTFAPI=${VTFAPI}") + list(APPEND IFEM_DEPLIBS ${VTFWRITER_LIBRARIES}) + list(APPEND IFEM_DEPINCLUDES ${VTFWRITER_INCLUDES}) list(APPEND IFEM_DEFINITIONS -DHAS_VTFAPI=${VTFAPI}) ENDIF(VTFWRITER_LIBRARIES AND VTFWRITER_INCLUDES) ENDIF(IFEM_USE_VTFWRITER) @@ -213,8 +210,8 @@ ENDIF(IFEM_USE_VTFWRITER) IF(IFEM_USE_OPENMP) FIND_PACKAGE(OpenMP) IF(OPENMP_FOUND) - SET(IFEM_CXX_FLAGS "${IFEM_CXX_FLAGS} ${OpenMP_CXX_FLAGS} -DUSE_OPENMP") - SET(IFEM_BUILD_CXX_FLAGS "${IFEM_BUILD_CXX_FLAGS} ${OpenMP_CXX_FLAGS} -DUSE_OPENMP") + string(APPEND IFEM_CXX_FLAGS " ${OpenMP_CXX_FLAGS} -DUSE_OPENMP") + string(APPEND IFEM_BUILD_CXX_FLAGS " ${OpenMP_CXX_FLAGS} -DUSE_OPENMP") list(APPEND IFEM_DEFINITIONS -DUSE_OPENMP=1) ENDIF(OPENMP_FOUND) ENDIF(IFEM_USE_OPENMP) @@ -274,13 +271,23 @@ if(IFEM_USE_TRACY) endif() endif() +# FMX matrix files +if(IFEM_USE_FMX) + find_package(FMX) + if(FMX_FOUND) + list(APPEND IFEM_DEPLIBS ${FMX_LIBRARY}) + string(APPEND IFEM_BUILD_CXX_FLAGS " -DHAS_FMXREADER=1") + message(STATUS "FMX matrix file support enabled") + endif() +endif() + # Portability issues include(CheckFunctionExists) set(CMAKE_REQUIRED_DEFINITIONS) set(CMAKE_REQUIRED_FLAGS) check_function_exists(get_current_dir_name unistd.h HAVE_GET_CURRENT_DIR_NAME) # lacks on osx if(HAVE_GET_CURRENT_DIR_NAME) - set(IFEM_CXX_FLAGS "${IFEM_CXX_FLAGS} -DHAVE_GET_CURRENT_DIR_NAME=1") + string(APPEND IFEM_CXX_FLAGS " -DHAVE_GET_CURRENT_DIR_NAME=1") list(APPEND IFEM_DEFINITIONS -DHAVE_GET_CURRENT_DIR_NAME=1) endif() @@ -298,10 +305,10 @@ include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-Wall HAS_WALL) check_cxx_compiler_flag(-Wno-parentheses HAS_PARENTHESES) if(HAS_WALL) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + string(APPEND CMAKE_CXX_FLAGS " -Wall") endif() if(HAS_PARENTHESES) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-parentheses") + string(APPEND CMAKE_CXX_FLAGS " -Wno-parentheses") endif() set(IFEM_REGTEST_SCRIPT ${IFEM_PATH}/Apps/Common/scripts/regtest.sh.in) diff --git a/cmake/Modules/IFEMOptions.cmake b/cmake/Modules/IFEMOptions.cmake index 9ac47d6ae..938b07efb 100644 --- a/cmake/Modules/IFEMOptions.cmake +++ b/cmake/Modules/IFEMOptions.cmake @@ -8,13 +8,13 @@ OPTION(IFEM_USE_ISTL "Compile with dune-istl support?" ON) OPTION(IFEM_USE_SLEPC "Compile with SLEPc support?" OFF) set(IFEM_USE_SPR "OFF" CACHE STRING "Compile with SPR support?") set_property(CACHE IFEM_USE_SPR PROPERTY STRINGS ON I32 I64 OFF) -OPTION(IFEM_USE_SPR "Compile with SPR support?" OFF) OPTION(IFEM_USE_SAMG "Compile with SAMG support?" OFF) -OPTION(IFEM_USE_HDF5 "Compile with HDF5 support?" ON) -OPTION(IFEM_USE_VTFWRITER "Compile with VTFWriter support?" ON) OPTION(IFEM_USE_UMFPACK "Compile with UMFPACK support?" ON) +OPTION(IFEM_USE_VTFWRITER "Compile with VTFWriter support?" ON) +OPTION(IFEM_USE_HDF5 "Compile with HDF5 support?" ON) OPTION(IFEM_USE_CEREAL "Compile with cereal support?" ON) OPTION(IFEM_USE_ZOLTAN "Compile with zoltan support?" OFF) +OPTION(IFEM_USE_FMX "Compile with FMX-file support?" OFF) OPTION(IFEM_AS_SUBMODULE "Compile IFEM as a submodule of apps?" OFF) OPTION(IFEM_WHOLE_PROG_OPTIM "Compile IFEM with link-time optimizations?" OFF) OPTION(IFEM_TEST_MEMCHECK "Run tests through valgrind?" OFF) From c140f6299d4fcebb608f8f9ad1b137634421b3b6 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Fri, 23 May 2025 15:44:06 +0200 Subject: [PATCH 2/5] Added: Read FEDEM superelement matrix files using fmx library. Added: ASMsupel::recoverInternals() for recovery of full solution. --- src/ASM/ASMsupel.C | 295 ++++++++++++++++++++++++-- src/ASM/ASMsupel.h | 41 +++- src/ASM/Test/TestASMsupel.C | 8 +- src/ASM/Test/refdata/CQUAD04.dat | 8 + src/ASM/Test/refdata/CQUAD04__B.fmx | Bin 0 -> 12142 bytes src/ASM/Test/refdata/CQUAD04__G.fmx | Bin 0 -> 334 bytes src/ASM/Test/refdata/CQUAD04__M.fmx | Bin 0 -> 1198 bytes src/ASM/Test/refdata/CQUAD04__S.fmx | Bin 0 -> 1198 bytes src/ASM/Test/refdata/CQUAD04__SAM.fsm | Bin 0 -> 3674 bytes 9 files changed, 330 insertions(+), 22 deletions(-) create mode 100644 src/ASM/Test/refdata/CQUAD04.dat create mode 100644 src/ASM/Test/refdata/CQUAD04__B.fmx create mode 100644 src/ASM/Test/refdata/CQUAD04__G.fmx create mode 100644 src/ASM/Test/refdata/CQUAD04__M.fmx create mode 100644 src/ASM/Test/refdata/CQUAD04__S.fmx create mode 100644 src/ASM/Test/refdata/CQUAD04__SAM.fsm diff --git a/src/ASM/ASMsupel.C b/src/ASM/ASMsupel.C index 8e10aabb5..cd6931a62 100644 --- a/src/ASM/ASMsupel.C +++ b/src/ASM/ASMsupel.C @@ -19,6 +19,19 @@ #include "Vec3Oper.h" #include +#ifdef HAS_FMXREADER +extern "C" { + void initfmx_(); + int readfmx_(const char* fname, const int* itype, + double* data, const int* nval, const int nchar); + int readfsm_(const char* fname, + int* ndof, int* ndof1, int* ndof2, int* nceq, int* nmmceq, + int* meqn, int* meqn1, int* meqn2, + int* mmceq, int* mpmceq, double* ttcc, + const int nchar = 0); +} +#endif + bool ASMsupel::read (std::istream& is) { @@ -31,55 +44,243 @@ bool ASMsupel::read (std::istream& is) for (Vec3& Xn : Xsup) is >> Xn; }; - myElmMat.resize(1,1); + // Lambda function for reading a FEDEM superelement matrix file. + auto&& readFMX = [&is](Matrix& M, int itype) -> int + { + int m, n, ierr = -99; + std::string fileName; + is >> m >> n >> fileName; + M.resize(m,n); +#ifdef HAS_FMXREADER + initfmx_(); + int ndim = m*n; + ierr = readfmx_(fileName.c_str(),&itype,M.ptr(),&ndim,fileName.size()); +#endif + if (ierr < 0) + std::cerr <<" *** readFMX: Failed to read matrix file \"" + << fileName <<"\" of type "<< itype + <<" (ierr = "<< ierr <<")" << std::endl; + + return ierr; + }; + + // Lambda function for reading a SAM data file from FEDEM. + auto&& readFSM = [&is](MiniSAM& sam) -> int + { + int ierr = -99; + std::string fileName; + is >> fileName; +#ifdef HAS_FMXREADER + initfmx_(); + double dummy = 0.0; + int ndof, ndof1, ndof2, nceq, nmmceq; + ierr = readfsm_(fileName.c_str(),&ndof,&ndof1,&ndof2,&nceq,&nmmceq, + &ierr,&ierr,&ierr,&ierr,&ierr,&dummy,fileName.size()); + if (ierr >= 0) + { + sam.meqn.resize(ndof); + sam.meqn1.resize(ndof1); + sam.meqn2.resize(ndof2); + sam.ttcc.resize(nmmceq); + sam.mmceq.resize(nmmceq); + sam.mpmceq.resize(nceq+1); + ierr = readfsm_("",&ndof,&ndof1,&ndof2,&nceq,&nmmceq, + sam.meqn.data(),sam.meqn1.data(),sam.meqn2.data(), + sam.mmceq.data(),sam.mpmceq.data(),sam.ttcc.data()); + } +#endif + if (ierr < 0) + std::cerr <<" *** readFSM: Failed to read SAM arrays from file \"" + << fileName <<"\" (ierr = "<< ierr <<")" << std::endl; + else + sam.findDofP2(); + + return ierr; + }; + + // Lambda function checking if myElmMat needs to be allocated, + // and whether we are reading a binary file or not. + auto&& checkMatrix = [&is,this](bool fmx = false) -> bool + { + if (myElmMat.empty()) + myElmMat.resize(2,1,3); + if (fmx) + { + char c = 0; + if (is.get(c) && c == 'x') + return true; + else if (is) + is.putback(c); + } + return false; + }; - char c; - int readMat = 0; - while (readMat < 7 && is.get(c)) + char c = 0; + Matrix tmpMat; + std::string comment; + int readMat = 0, ierr = 0; + while (readMat < 7 && is.get(c) && ierr >= 0) switch (c) { case 'K': case 'k': - is >> myElmMat.A.front(); - if (c == 'K') // assume stored column-wise - myElmMat.A.front().transpose(); + // Read stiffness matrix file + if (checkMatrix(true)) + ierr = readFMX(myElmMat.A.front(),1); + else + { + is >> myElmMat.A.front(); + if (c == 'K') // assume stored column-wise + myElmMat.A.front().transpose(); + } readMat |= 1; break; + + case 'M': + case 'm': + // Read mass matrix file + if (checkMatrix(true)) + ierr = readFMX(myElmMat.A.back(),2); + else + { + is >> myElmMat.A.back(); + if (c == 'M') // assume stored column-wise + myElmMat.A.back().transpose(); + } + readMat |= 1; + break; + + case 'B': + // Read recovery matrix file (binary only) + if (is.get(c) && c == 'x') + ierr = readFMX(myRecMat,4); + else if (is) + is.putback(c); + break; + + case 'S': + // Read SAM matrix file (binary only) + if (is.get(c) && c == 'x') + ierr = readFSM(mySAM); + else if (is) + is.putback(c); + break; + + case 'g': + // Read gravity forces file (binary only) + if (checkMatrix(true)) + { + if ((ierr = readFMX(tmpMat,3)) >= 0 && !gravity.isZero()) + if (!tmpMat.multiply(gravity.vec(),myElmMat.b.front())) + return false; + readMat |= 2; + } + break; + case 'R': + // Read right-hand-side vector + checkMatrix(); is >> myElmMat.b.front(); readMat |= 2; break; + case 'L': - { - // The load vector is stored as a ndof x 1 matrix and not a vector - Matrix tmpMat; - is >> tmpMat; - myElmMat.b.front() = tmpMat.getColumn(1); - } + // Read load vector. + // It is assumed stored as a ndof x 1 matrix and not a vector. + checkMatrix(); + is >> tmpMat; + myElmMat.b.front() = tmpMat.getColumn(1); readMat |= 2; break; + case 'G': + // Read supernode coordinates readCoord(myNodes); readMat |= 4; break; + + case '#': + // Comment line - print and ignore + if (std::getline(is,comment)) + std::cout << comment << std::endl; + break; + case '\n': + // Blank line - skip break; + default: is.putback(c); if (readMat) { - std::cerr <<" *** ASMsupel::read: Unknown label "<< c << std::endl; + std::cerr <<" *** ASMsupel::read: Unknown label "<< c + <<" ("<< static_cast(c) <<")"<< std::endl; return false; } else { // Assuming the order G, K, L but without the labels + checkMatrix(); readCoord(myNodes); is >> myElmMat.A.front() >> myElmMat.b.front(); readMat = 7; } } - return readMat == 7 && is.good(); + // Calculate the total external force + for (size_t i = 0; i < myElmMat.c.size() && i < 3; i++) + myElmMat.c[i] = myElmMat.b.front().sum(i,nf); + + return readMat == 7 && is.good() && ierr >= 0; +} + + +void ASMsupel::MiniSAM::findDofP2 () +{ + dofP2.clear(); + dofP2.reserve(meqn2.size()); + + int ipos = 0; + for (int ieq : meqn) + if ((ipos = utl::findIndex(meqn2,ieq)) >= 0) + dofP2.push_back(ipos+1); +} + + +void ASMsupel::MiniSAM::print (std::ostream& os) const +{ + if (meqn.empty()) + return; + + if (mpmceq.size() > 1) + { + os <<"\nSAM constraints:"; + for (size_t iceq = 1; iceq < mpmceq.size(); iceq++) + { + int ip = mpmceq[iceq-1]; + if (ip < mpmceq[iceq]) + { + os <<"\n"<< iceq <<": "<< mmceq[ip-1]; + if (fabs(ttcc[ip-1]) > 1.0e-15) + os <<"*("<< ttcc[ip-1] <<")"; + while (++ip < mpmceq[iceq]) + os <<" "<< mmceq[ip-1] <<"*("<< ttcc[ip-1] <<")"; + } + } + } + + os <<"\nSAM meqn:"; + for (size_t idof = 0; idof < meqn.size(); idof++) + os << (idof%6 ? " " : "\n") << meqn[idof]; + os <<"\nSAM meqn1:"; + for (size_t i1 = 0; i1 < meqn1.size(); i1++) + os << (i1%6 ? " " : "\n") << meqn1[i1]; + os <<"\nSAM meqn2:"; + for (size_t i2 = 0; i2 < meqn2.size(); i2++) + os << (i2%6 ? " " : "\n") << meqn2[i2]; + os <<"\nSAM dofP2:"; + for (size_t i = 0; i < dofP2.size(); i++) + os << (i%6 ? " " : "\n") << dofP2[i]; + os << std::endl; } @@ -144,6 +345,15 @@ const IntVec& ASMsupel::getNodeSet (int iset) const } +bool ASMsupel::isInNodeSet (int iset, int inod) const +{ + if (iset < 1 || iset > static_cast(nodeSets.size())) + return false; + + return utl::findIndex(nodeSets[iset-1].second,inod) >= 0; +} + + int ASMsupel::parseNodeSet (const std::string& setName, const char* cset) { int iset = this->getNodeSetIdx(setName)-1; @@ -281,3 +491,58 @@ bool ASMsupel::transform (const Matrix& Tlg) return true; } + + +bool ASMsupel::recoverInternals (const Vector& supSol, Vector& fullSol) const +{ +#if SP_DEBUG > 2 + mySAM.print(std::cout); +#endif + + // Reorder supSol to match the expected ordering of the recovery matrix + Vector ve(mySAM.meqn2.size()), vi; + for (size_t i = 1; i <= mySAM.dofP2.size(); i++) + ve(mySAM.dofP2[i-1]) = supSol(i); + + // Calculate the internal DOFs; vi = myRecMat * ve + if (!myRecMat.multiply(ve,vi)) + return false; + + // Establish the total equation-order solution vector, svec + int nceq = mySAM.mpmceq.size()-1; + int ndof = mySAM.meqn.size(); + int neq = *std::max_element(mySAM.meqn.begin(),mySAM.meqn.end()); + Vector sveq(neq); + for (size_t i1 = 1; i1 <= mySAM.meqn1.size(); i1++) + sveq(mySAM.meqn1[i1-1]) = vi(i1); + for (size_t i2 = 1; i2 <= mySAM.meqn2.size(); i2++) + sveq(mySAM.meqn2[i2-1]) = ve(i2); + +#if SP_DEBUG > 2 + std::cout <<"ve:"<< std::scientific << ve; + std::cout.unsetf(std::ios_base::floatfield); + std::cout <<"vi:"<< vi <<"sveq:"<< sveq; +#endif + + // Expand to DOF-order while accounting for constraint equations + fullSol.resize(ndof,true); + for (int idof = 1; idof <= ndof; idof++) + { + int ieq = mySAM.meqn[idof-1]; + int iceq = -ieq; + int jdof = 0; + if (ieq > 0 && ieq <= neq) + fullSol(idof) = sveq(ieq); + else if (iceq > 0 && iceq <= nceq) + for (int ip = mySAM.mpmceq[iceq-1]; ip < mySAM.mpmceq[iceq]-1; ip++) + if ((jdof = mySAM.mmceq[ip]) > 0 && jdof <= ndof) + if ((ieq = mySAM.meqn[jdof-1]) > 0 && ieq <= neq) + fullSol(idof) += mySAM.ttcc[ip]*sveq[ieq-1]; + } + +#ifdef SP_DEBUG + std::cout <<"\nExpanded solution vector for substructure "<< idx+1 + << fullSol; +#endif + return true; +} diff --git a/src/ASM/ASMsupel.h b/src/ASM/ASMsupel.h index 8596585a4..3adb44200 100644 --- a/src/ASM/ASMsupel.h +++ b/src/ASM/ASMsupel.h @@ -24,6 +24,7 @@ \brief Driver for assembly of general superelements. \details This class contains methods for assembly of superelements resulting from static condensation or reduced order modeling. + It also supports reading superelement matrix files from FEDEM (fmx-files). */ class ASMsupel : public ASMbase @@ -35,8 +36,6 @@ class ASMsupel : public ASMbase ASMsupel(const ASMsupel& patch, unsigned char n_f) : ASMbase(patch,n_f) {} //! \brief Default copy constructor copying everything. ASMsupel(const ASMsupel& patch) : ASMbase(patch) {} - //! \brief Empty destructor. - virtual ~ASMsupel() {} //! \brief Creates an instance by reading the given input stream. virtual bool read(std::istream& is); @@ -51,6 +50,8 @@ class ASMsupel : public ASMbase virtual int getNodeSetIdx(const std::string& setName) const; //! \brief Returns an indexed pre-defined node set. virtual const IntVec& getNodeSet(int iset) const; + //! \brief Checks if node \a inod is within predefined node set \a iset. + virtual bool isInNodeSet(int iset, int inod) const; //! \brief Defines a node set by parsing a list of node numbers. virtual int parseNodeSet(const std::string& setName, const char* cset); @@ -92,7 +93,7 @@ class ASMsupel : public ASMbase virtual bool integrate(Integrand& integrand, GlobalIntegral& glbInt, const TimeDomain&); - //! \brief Dummy method (patch patch boundaries are not defined). + //! \brief Dummy method doing nothing (patch boundaries are not defined). virtual bool integrate(Integrand&, int, GlobalIntegral&, const TimeDomain&) { return false; } @@ -117,11 +118,43 @@ class ASMsupel : public ASMbase //! \brief Applies a transformation matrix from local to global system. virtual bool transform(const Matrix& Tlg); + //! \brief Assigns the acceleration vector of gravity. + void setGravity(const Vec3& g) { gravity = g; } + + //! \brief Checks if recovery data has been read from file. + bool hasRecovery() const { return !myRecMat.empty() && !mySAM.meqn.empty(); } + + //! \brief Recovers the internal DOFs from the supernode DOFs. + //! \param[in] supSol Supernode values of the solution vector + //! \param[out] fullSol Solution values for all FE DOFs + bool recoverInternals(const Vector& supSol, Vector& fullSol) const; + private: + //! \brief Struct holding the SAM data needed for superelement recovery. + struct MiniSAM + { + RealArray ttcc; //!< Table of tables of constraint coefficients + IntVec mmceq; //!< Matrix of matrices of constraint equations + IntVec mpmceq; //!< Matrix of pointers to MCEQs + IntVec meqn; //!< Matrix of equation numbers for all DOFs + IntVec meqn1; //!< Matrix of status 1 equation numbers + IntVec meqn2; //!< Matrix of status 2 equation numbers + IntVec dofP2; //!< DOF-positions in the Schur-complement matrix + + //! \brief Computes the \ref dofP2 array. + void findDofP2(); + //! \brief Prints the SAM data to the given output stream. + void print(std::ostream& os) const; + }; + + MiniSAM mySAM; //!< Subset of SAM data needed for recovery Vec3Vec myNodes; //!< Supernode coordinates - ElmMats myElmMat; //!< Duperelement matrices + ElmMats myElmMat; //!< Superelement matrices + Matrix myRecMat; //!< Recovery matrix std::vector nodeSets; //!< Node sets for Dirichlet BCs + + Vec3 gravity; //!< Gravitation vector (for calculation of g-force load vector) }; #endif diff --git a/src/ASM/Test/TestASMsupel.C b/src/ASM/Test/TestASMsupel.C index 700d2def2..a91ca6ad8 100644 --- a/src/ASM/Test/TestASMsupel.C +++ b/src/ASM/Test/TestASMsupel.C @@ -24,14 +24,13 @@ struct TestCase class TestASMsup : public testing::Test, - public testing::WithParamInterface -{ -}; + public testing::WithParamInterface {}; TEST_P(TestASMsup, Read) { ASMsupel pch; + pch.setGravity({0.0,0.0,-9.81}); ASMbase::resetNumbering(); std::cout <<"Checking "<< GetParam().file << std::endl; std::ifstream is(GetParam().file); @@ -43,6 +42,9 @@ TEST_P(TestASMsup, Read) const std::vector testFiles = { +#ifdef HAS_FMXREADER + { "src/ASM/Test/refdata/CQUAD04.dat", 2U }, +#endif { "src/ASM/Test/refdata/Supel.dat", 2U }, { "src/ASM/Test/refdata/kjoint.dat", 4U }}; diff --git a/src/ASM/Test/refdata/CQUAD04.dat b/src/ASM/Test/refdata/CQUAD04.dat new file mode 100644 index 000000000..80a65e221 --- /dev/null +++ b/src/ASM/Test/refdata/CQUAD04.dat @@ -0,0 +1,8 @@ +Kx 12 12 src/ASM/Test/refdata/CQUAD04_ +Mx 12 12 src/ASM/Test/refdata/CQUAD04_ +gx 12 3 src/ASM/Test/refdata/CQUAD04_ +Bx 126 12 src/ASM/Test/refdata/CQUAD04_ +Sx src/ASM/Test/refdata/CQUAD04_ +G 2 +0.0 0.0 -1.0 +0.0 0.0 0.0 diff --git a/src/ASM/Test/refdata/CQUAD04__B.fmx b/src/ASM/Test/refdata/CQUAD04__B.fmx new file mode 100644 index 0000000000000000000000000000000000000000..f77ec776105f98cfe7ec32b46d2c8a42d2cd982a GIT binary patch literal 12142 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 DpQ;)s literal 0 HcmV?d00001 diff --git a/src/ASM/Test/refdata/CQUAD04__G.fmx b/src/ASM/Test/refdata/CQUAD04__G.fmx new file mode 100644 index 0000000000000000000000000000000000000000..22b0c9dac7c826b87410aab825ff55b82b9fe060 GIT binary patch literal 334 zcmY#(b9Hg`RY)&NEXypZR7lG&N={WMOHD4xFDh10FcD$^f{7cuT&)fD46M04%0(`m z=?QiKiNe8?u)Q$0%2Hj{#PgaCFupq$^$njR;OaFX>JPxw!)T`pRD0?}OuqCz(L_}z z&E(Q`MMVS=}NWesh0SG2;>~gg>)HAT=`f=!1#}5Zz2aqTfG;A#M z*$d-X1W&*4>6p9&jQ>Auj-Jy-V>lluzTjcDH(dSeZ-*_uZxtV)_z6Uv-!&8*D6KA4lsEjozeW=9Hbt_y_qG_mUS6k z$m$rx6V>7F1FBc(koJJ{i@r#mGfT2WQLnLH748n8dN4qDZ&d2_KEvc6_Aq%6?GP>l zH}^Kgy$4|OxAGsqoTd9|Ka3Bg6WR`m?nMt@kUX+`1%ppC%GUpc+w%qzz78ODFd+35 z;lIDvsvmv%fnpC#8A|wq1d-j_{F~86!0i&;9+3Q$w{|GuaHK{W&S%+jy3$qgfIXJ< zfD*pw?){0G-X5f=`ohD_29n+mfb2&G=;;AHd_iiF-CKc{-V}1c>CFMj%nXJVRv`bN zq+e|50Xcku>QUT_CA~H1vS`A?31%O9`o)qS7?8sk-M!l}(;LTUZE;&{`3*V!qNfKe q;fvy43AFSEb3c%V9&Al22x#hkKW~16OWh&fUnoZDjLoQo4K#m4n2Iq;z;h@ zu#{ylS8E4c-(N`h9)vgwO6G5b+pBqT>bKon%i!u!)FFj0SRBc{24PKR4~xT4^(|P7 z8V(7)aDGxaSp7k4=>a)>(cSBC0yDieFg}I5-vN@|Q2hsVEP8rC4_~l8*Y{Je)x4K>$7dVoeW7;fwCx69+KU zTf?`mec19Fa{5J24_Lz&$-T?a(i_C}5E7Q(;9|h^3uD951B?%%;o%Dv1i2R`{~#2S F-T-iP@ZbOd literal 0 HcmV?d00001 diff --git a/src/ASM/Test/refdata/CQUAD04__SAM.fsm b/src/ASM/Test/refdata/CQUAD04__SAM.fsm new file mode 100644 index 0000000000000000000000000000000000000000..1fedcdba2aad2dcc4bfa7d10a9793ce8bca62f48 GIT binary patch literal 3674 zcmeI!Rc{qR7=U41in~J#6qgn&rC4bT6nD4c?rz1UTorfy1zd8)-@)Cjye~PElmi<$ zp}k|1C$l@dGdnvo-wT{DVPE0!xk@qCmA>cozZ{imZ2#3!2aV7I?U0J@NJj>S zAP4!Fi0PP%0<6F~Y{4!R;wVm`7#DC2cM!hEcUM7GRD)xvftpA{E%>dDt1jxHJ{rJz zcdU)k1WnNl&ZFaR3D+mQ%d)(b*Qs6z8!0bd1F1)S^5pee#isHf2gxG510=;_nzMy>MYF-=7^9l%oCx` z(tP3kgdAe-ur7SB^AK{1&)NQ8IM1@*FrUtu-|sy5{o%VDlkap)uAjNNJyOvbu48w& zmTBk<*E0jIsr|V&&Ykn`+}nS6pY!V4_&(q7{P~Pyvz>Dg+Bw$H&i!CJ*W7llyX~&w z0Ivp6FkNvJj4SW z<=!zI#|fN75ngle6<*>6p5qxRa<399qx3pf=eh=JA_?X?^PG9roN6vLhc-Y%M9qJq q>lig}iOzr2Iwraw^7*zg7>m;TGtqrsR_k6?@g9|DqV`Xs`{X;G4 Date: Wed, 28 May 2025 11:49:47 +0200 Subject: [PATCH 3/5] Added: Assign gravity vector to ASMsupel patch before parsing it --- src/SIM/SIMsupel.C | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/SIM/SIMsupel.C b/src/SIM/SIMsupel.C index 12a5443ed..48991b946 100644 --- a/src/SIM/SIMsupel.C +++ b/src/SIM/SIMsupel.C @@ -12,9 +12,9 @@ //============================================================================== #include "SIMsupel.h" -#include "ASMbase.h" +#include "ASMsupel.h" #include "ASM3D.h" -#include "IntegrandBase.h" +#include "HasGravityBase.h" #include "SAM.h" #include "Utilities.h" #include "IFEM.h" @@ -105,6 +105,10 @@ ASMbase* SIMsupel::readPatch (std::istream& isp, int pchInd, const CharVec&, ASMbase* pch = ASM3D::create(ASM::SuperElm,ncmp); if (pch) { + // Need to assign gravity vector before parsing superelement data + HasGravityBase* itgr = dynamic_cast(myProblem); + if (itgr && !itgr->getGravity().isZero()) + static_cast(pch)->setGravity(itgr->getGravity()); if (!pch->read(isp) || this->getLocalPatchIndex(pchInd+1) < 1) { delete pch; From fdbd205ddd901dde0e7ca18d10dc2778e0981054 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Fri, 20 Jun 2025 16:24:36 +0200 Subject: [PATCH 4/5] Changed: Override writeGlvBC in SIMSupel to do nothing --- src/SIM/SIMoutput.h | 2 +- src/SIM/SIMsupel.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SIM/SIMoutput.h b/src/SIM/SIMoutput.h index a33fb7ddf..4693395a9 100644 --- a/src/SIM/SIMoutput.h +++ b/src/SIM/SIMoutput.h @@ -94,7 +94,7 @@ class SIMoutput : public SIMinput //! \brief Writes boundary conditions as scalar fields to the VTF-file. //! \param nBlock Running result block counter //! \param[in] iStep Load/time step identifier - bool writeGlvBC(int& nBlock, int iStep = 1) const; + virtual bool writeGlvBC(int& nBlock, int iStep = 1) const; //! \brief Writes global node numbers as scalar fields to the VTF-file. //! \param nBlock Running result block counter diff --git a/src/SIM/SIMsupel.h b/src/SIM/SIMsupel.h index 151f8acc5..b64193822 100644 --- a/src/SIM/SIMsupel.h +++ b/src/SIM/SIMsupel.h @@ -50,6 +50,9 @@ class SIMsupel : public SIMdummy //! \brief Returns the name of this simulator. virtual std::string getName() const { return "SIMsupel"; } + //! \brief Overridden to not write BC codes for superelement models. + virtual bool writeGlvBC(int&, int) const { return true; } + //! \brief Performs recovery of the internal DOFs for superelements. //! \param[in] glbSol Global solution vector bool recoverInternalDOFs(const Vector& glbSol); From e30e407ffb3b7158cd4c25f189cf6e6afd8e85c8 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Tue, 24 Jun 2025 16:53:10 +0200 Subject: [PATCH 5/5] Added: Install header file ASMsupel.h --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2c69e2fe..c523f0b68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,7 +165,7 @@ else() src/ASM/TimeDomain.h src/ASM/ASMs?D.h src/ASM/ASM?D.h src/ASM/ASM??DLag.h src/ASM/ASMLagBase.h src/ASM/ASMutils.h src/ASM/DomainDecomposition.h src/ASM/ItgPoint.h - src/ASM/ReactionsOnly.h + src/ASM/ReactionsOnly.h src/ASM/ASMsupel.h src/LinAlg/*.h src/SIM/*.h src/Utility/*.h 3rdparty/*.h ${CMAKE_BINARY_DIR}/IFEM.h)