From d0a89d1a40c3eaa354b1da75d46e66e963454596 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Tue, 15 Oct 2024 21:27:48 +0200 Subject: [PATCH 1/6] Add missing dummy implementation of fmi2GetDirectionalDerivative(). Handle missing definitions of FEDEM_SOLVER variable more gracefully. Cosmetic refactoring using Lambda functions for convenience, etc. --- src/FMU/fmu_template.C | 334 ++++++++++++++++++----------------------- 1 file changed, 144 insertions(+), 190 deletions(-) diff --git a/src/FMU/fmu_template.C b/src/FMU/fmu_template.C index 7daed569..5e998c8e 100644 --- a/src/FMU/fmu_template.C +++ b/src/FMU/fmu_template.C @@ -15,31 +15,27 @@ #undef UNICODE #include "fmi2Functions.h" - #include #include #include #include +#include #if defined _MSC_VER - #include - typedef HINSTANCE LibHandle; - std::string pathSeparator = "\\"; +#include +typedef HINSTANCE LibHandle; #elif defined __GNUC__ - #include - #include - typedef void* LibHandle; - std::string pathSeparator = "/"; +#include +typedef void* LibHandle; +#else +#error "Platform not supported, neither _MSC_VER nor __GNUC__ defined" #endif - //Macro for printing debug info to terminal #ifdef FMU_DEBUG -#define DEBUG_STDERR(x) { std::cerr << x << std::endl; } -#define DEBUG_STDOUT(x) { std::cout << x << std::endl; } +#define DEBUG_STDOUT(x) std::cout << x << std::endl; #else -#define DEBUG_STDERR(x) {} -#define DEBUG_STDOUT(x) {} +#define DEBUG_STDOUT(x) #endif typedef void (*DLPROC)(); @@ -55,24 +51,6 @@ typedef double (*DLPROC_EVALFUNC)(int,const char*,double,int*); typedef bool (*DLPROC_SAVETRANSFORMATIONSTATE)(double*,const int); typedef int (*DLPROC_RESTARTFROMSTATE)(const double*,const int,const int); - -DLPROC getFuncAddress(const LibHandle& lib, - const std::string& fName) -{ -#if defined _MSC_VER - DLPROC address = (DLPROC)GetProcAddress(lib,fName.c_str()); -#elif defined __GNUC__ - DLPROC address = (DLPROC)dlsym(lib,fName.c_str()); -#else -#error "Platform not supported, neither _MSC_VER nor __GNUC__ defined" -#endif - - if (!address) - DEBUG_STDERR(" *** Could not get function address for " + fName); - - return address; -} - DLPROC_INIT solverInit; DLPROC_DONE solverDone; DLPROC_GETSTATESIZE getStateSize; @@ -84,20 +62,8 @@ DLPROC_SAVETRANSFORMATIONSTATE saveTransformationState; DLPROC_RESTARTFROMSTATE restartFromState; -std::string parentPath(std::string path) -{ - size_t pos = 0; - while(path.find(pathSeparator,pos+1) != std::string::npos) - { - pos = path.find(pathSeparator,pos+1); - } - return path.substr(0,pos); -} +namespace { -#ifdef __cplusplus -extern "C" { -#endif - enum fmuStateCode { FMUSTART= 1u << 0, @@ -145,18 +111,22 @@ extern "C" { fmi2Boolean logging = false; const fmi2CallbackFunctions *functions; }; - - - void readConfig(componentInstance* comp, std::string path) + + void readConfig(componentInstance* comp, const std::string& path) { - DEBUG_STDOUT("Configpath: " + path); + DEBUG_STDOUT("configPath: " + path); + + // Lambda function allocating an integer array. + auto&& allocateInts = [comp](fmi2Integer nwi) + { + return (fmi2Integer*)comp->functions->allocateMemory(nwi,sizeof(fmi2Integer)); + }; + // TODO(RunarHR): readConfig: Make this more robust std::string line; std::ifstream confFile(path); - std::getline(confFile,line); - comp->initialState.modelIdentifier = line; - std::getline(confFile,line); - comp->initialState.modelGuid = line; + std::getline(confFile,comp->initialState.modelIdentifier); + std::getline(confFile,comp->initialState.modelGuid); std::getline(confFile,line); comp->initialState.numReals = atoi(line.c_str()); std::getline(confFile,line); @@ -167,12 +137,12 @@ extern "C" { comp->initialState.numParams = atoi(line.c_str()); std::getline(confFile,line); comp->initialState.numTransforms = atoi(line.c_str()); - comp->initialState.fedemInputIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numInputs, sizeof(fmi2Integer) ); - comp->initialState.fedemOutputIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numOutputs, sizeof(fmi2Integer) ); - comp->initialState.fedemTransformIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numTransforms, sizeof(fmi2Integer) ); - comp->state.fedemInputIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numInputs, sizeof(fmi2Integer) ); - comp->state.fedemOutputIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numOutputs, sizeof(fmi2Integer) ); - comp->state.fedemTransformIndices = (fmi2Integer*)comp->functions->allocateMemory( comp->initialState.numTransforms, sizeof(fmi2Integer) ); + comp->initialState.fedemInputIndices = allocateInts(comp->initialState.numInputs); + comp->initialState.fedemOutputIndices = allocateInts(comp->initialState.numOutputs); + comp->initialState.fedemTransformIndices = allocateInts(comp->initialState.numTransforms); + comp->state.fedemInputIndices = allocateInts(comp->initialState.numInputs); + comp->state.fedemOutputIndices = allocateInts(comp->initialState.numOutputs); + comp->state.fedemTransformIndices = allocateInts(comp->initialState.numTransforms); for(int i=0; i< comp->initialState.numInputs; i++) { @@ -189,16 +159,14 @@ extern "C" { std::getline(confFile,line); comp->initialState.fedemTransformIndices[i] = atoi(line.c_str()); } - - confFile.close(); } void copyModelState(modelState* destination, modelState* source) { //Copy arrays memcpy( destination->reals, source->reals, sizeof(fmi2Real)*source->numReals ); - memcpy( destination->solverState, source->solverState, sizeof(fmi2Real)*(source->solverStateSize )); - memcpy( destination->transformationState, source->transformationState, sizeof(fmi2Real)*(source->transformationStateSize )); + memcpy( destination->solverState, source->solverState, sizeof(fmi2Real)*source->solverStateSize ); + memcpy( destination->transformationState, source->transformationState, sizeof(fmi2Real)*source->transformationStateSize ); //Copy variables destination->modelIdentifier = source->modelIdentifier; @@ -217,102 +185,133 @@ extern "C" { memcpy( destination->fedemOutputIndices, source->fedemOutputIndices, sizeof(fmi2Integer)*source->numOutputs ); memcpy( destination->fedemTransformIndices, source->fedemTransformIndices, sizeof(fmi2Integer)*source->numTransforms ); } - + +} // closing brace for anonymous namespace + + +#ifdef __cplusplus +extern "C" { +#endif + fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String GUID, fmi2String fmuResourceLocation, const fmi2CallbackFunctions* functions, fmi2Boolean visible, fmi2Boolean loggingOn) { - componentInstance* comp = 0; - - comp = (componentInstance*)functions->allocateMemory(1,sizeof(componentInstance)); + DEBUG_STDOUT("Hello, FMU world!"); + + const char* solverPath = getenv("FEDEM_SOLVER"); + if (!solverPath) + { + std::cerr <<" *** Environment variable FEDEM_SOLVER not defined."<< std::endl; + return 0; + } + + DEBUG_STDOUT("solverPath: " + std::string(solverPath)); + + componentInstance* comp = (componentInstance*)functions->allocateMemory(1,sizeof(componentInstance)); comp->logging = loggingOn; - comp->functions = functions; comp->instanceName = instanceName; - - + + // Lambda function returning the parent path of given pathname. + auto&& parentPath = [](const std::string& path) + { + size_t pos = path.find_last_of("/\\"); + return path.substr(0, pos == std::string::npos ? 0 : pos); + }; + + // Lambda function appending a filename to a path name. + auto&& appendPath = [](const std::string& path, const std::string& fname) + { +#if defined _MSC_VER + return path + "\\" + fname; +#else + return path + "/" + fname; +#endif + }; + //Get resource folder path #if defined _MSC_VER char path[1024]; HMODULE hm = NULL; - - if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCSTR) &fmi2GetTypesPlatform, - &hm)) - { - int ret = GetLastError(); - if(comp->logging)comp->functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", - "fmi2Instantiate: Could not retrieve path of running module from GetModuleHandleEx."); - } + (LPCSTR)&fmi2GetTypesPlatform, &hm)) + if (comp->logging) + functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", + "fmi2Instantiate: Could not retrieve path of running module from GetModuleHandleEx."); GetModuleFileNameA(hm, path, sizeof(path)); #elif defined __GNUC__ Dl_info info; - if (dladdr((void*)fmi2Instantiate, &info)) - { - DEBUG_STDOUT("FMU-library location: " + std::string(info.dli_fname)); - } - else + if (!dladdr((void*)fmi2Instantiate, &info)) { - DEBUG_STDERR(" *** Could not find location of FMU-library"); + std::cerr <<" *** Could not find location of FMU-library."<< std::endl; return 0; } const char* path = info.dli_fname; #endif - + DEBUG_STDOUT("FMU-library location: " + std::string(path)); + std::string platformPath(parentPath(path)); DEBUG_STDOUT("platformPath: " + platformPath); - std::string binariesPath(parentPath(platformPath.c_str())); + std::string binariesPath(parentPath(platformPath)); DEBUG_STDOUT("binariesPath: " + binariesPath); - std::string rootPath(parentPath(binariesPath.c_str())); - std::string resourcePath = rootPath + pathSeparator + "resources"; - DEBUG_STDOUT("resourcePath: " + resourcePath); + std::string rootPath(parentPath(binariesPath)); + std::string resourcePath(appendPath(rootPath,"resources")); + std::string workingDir(appendPath(resourcePath,"model")); + DEBUG_STDOUT("workingDir: " + workingDir); - readConfig(comp, resourcePath + pathSeparator + "config.txt"); + readConfig(comp, appendPath(resourcePath,"config.txt")); - if(strcmp(comp->initialState.modelGuid.c_str(), GUID ) != 0) + if (strcmp(comp->initialState.modelGuid.c_str(),GUID) != 0) { - DEBUG_STDOUT("fmi2Instantiate: GUIDs does not match"); + std::cerr <<" *** GUIDs do not match: "<< comp->initialState.modelGuid <<" "<< GUID << std::endl; if(comp->logging)functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", "fmi2Instantiate: GUIDs does not match."); - DEBUG_STDOUT(comp->initialState.modelGuid.c_str()); functions->freeMemory(comp); return 0; } - std::string solverPath(getenv("FEDEM_SOLVER")); - DEBUG_STDOUT("solverPath: " + solverPath); - // NOTE(RunarHR): Initialization of solver should be done in EnterInitializationMode, but must be done here to get solverState size. #if defined _MSC_VER - std::string solverBinariesPath(parentPath(solverPath.c_str())); + std::string solverBinariesPath(parentPath(solverPath)); SetDllDirectory(solverBinariesPath.c_str()); - LibHandle h_solver = LoadLibrary(solverPath.c_str()); + LibHandle h_solver = LoadLibrary(solverPath); SetDllDirectory(NULL); #elif defined __GNUC__ - LibHandle h_solver = dlopen(solverPath.c_str(), RTLD_LAZY); + LibHandle h_solver = dlopen(solverPath,RTLD_LAZY); #endif if (!h_solver) { - DEBUG_STDERR(" *** Could not load solver library"); + std::cerr <<" *** Could not load solver library "<< solverPath << std::endl; return 0; } - solverInit = (DLPROC_INIT)getFuncAddress(h_solver,"solverInit"); - solverDone = (DLPROC_DONE)getFuncAddress(h_solver,"solverDone"); - getStateSize = (DLPROC_GETSTATESIZE)getFuncAddress(h_solver,"getStateSize"); - getTransformationStateSize = (DLPROC_GETSTATESIZE)getFuncAddress(h_solver,"getTransformationStateSize"); - setExtFunc = (DLPROC_SETEXTFUNC)getFuncAddress(h_solver,"setExtFunc"); - solveNext = (DLPROC_SOLVENEXT)getFuncAddress(h_solver,"solveNext"); - evalFunc = (DLPROC_EVALFUNC)getFuncAddress(h_solver,"evalFunc"); - saveTransformationState = (DLPROC_SAVETRANSFORMATIONSTATE)getFuncAddress(h_solver,"saveTransformationState"); - restartFromState = (DLPROC_RESTARTFROMSTATE)getFuncAddress(h_solver,"restartFromState"); - - std::string workingDir = resourcePath + pathSeparator + "model"; + // Lambda function of optaining a function pointer from the solver library. + auto&& getFuncAddress = [h_solver](const char* fName) + { +#if defined _MSC_VER + DLPROC address = (DLPROC)GetProcAddress(h_solver,fName); +#else + DLPROC address = (DLPROC)dlsym(h_solver,fName); +#endif + if (!address) + std::cerr <<" *** Could not get function address for "<< fName << std::endl; + return address; + }; - // Lambda function adding option files to argvStart based on existance + solverInit = (DLPROC_INIT)getFuncAddress("solverInit"); + solverDone = (DLPROC_DONE)getFuncAddress("solverDone"); + getStateSize = (DLPROC_GETSTATESIZE)getFuncAddress("getStateSize"); + getTransformationStateSize = (DLPROC_GETSTATESIZE)getFuncAddress("getTransformationStateSize"); + setExtFunc = (DLPROC_SETEXTFUNC)getFuncAddress("setExtFunc"); + solveNext = (DLPROC_SOLVENEXT)getFuncAddress("solveNext"); + evalFunc = (DLPROC_EVALFUNC)getFuncAddress("evalFunc"); + saveTransformationState = (DLPROC_SAVETRANSFORMATIONSTATE)getFuncAddress("saveTransformationState"); + restartFromState = (DLPROC_RESTARTFROMSTATE)getFuncAddress("restartFromState"); + + // Lambda function adding option files to argvStart based on existance. std::vector argvStart; - auto&& addOptionFile=[workingDir,&argvStart](const char* opt,const char* fileName) + auto&& addOptionFile=[workingDir,&argvStart,appendPath](const char* opt,const char* fileName) { - std::string filePath = workingDir + pathSeparator + fileName; - std::ifstream fs(filePath.c_str()); + std::ifstream fs(appendPath(workingDir,fileName).c_str()); if (fs.good()) { argvStart.push_back(const_cast(opt)); @@ -334,24 +333,27 @@ extern "C" { { if(comp->logging) functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", "Could not initialize solver."); - DEBUG_STDERR(" *** Solver failed to initialize" + std::to_string(status)); + std::cerr <<" *** Solver failed to initialize ("<< status <<")."<< std::endl; return 0; } - + + // Lambda function allocating a real array. + auto&& allocateReals = [functions](fmi2Integer nwr) + { + return (fmi2Real*)functions->allocateMemory(nwr,sizeof(fmi2Real)); + }; + comp->initialState.solverStateSize = getStateSize(); comp->initialState.transformationStateSize = getTransformationStateSize(); - - //Initial State - comp->initialState.reals = (fmi2Real*)functions->allocateMemory( comp->initialState.numReals, sizeof(fmi2Real) ); - comp->initialState.solverState = (fmi2Real*)comp->functions->allocateMemory( comp->initialState.solverStateSize,sizeof(fmi2Real) ); - comp->initialState.transformationState = (fmi2Real*)comp->functions->allocateMemory( comp->initialState.transformationStateSize,sizeof(fmi2Real) ); + + comp->initialState.reals = allocateReals(comp->initialState.numReals); + comp->initialState.solverState = allocateReals(comp->initialState.solverStateSize); + comp->initialState.transformationState = allocateReals(comp->initialState.transformationStateSize); comp->initialState.t = 0; - - //Working State - comp->state.reals = (fmi2Real*)functions->allocateMemory( comp->initialState.numReals, sizeof(fmi2Real) ); - comp->state.solverState = (fmi2Real*)comp->functions->allocateMemory( comp->initialState.solverStateSize,sizeof(fmi2Real) ); - comp->state.transformationState = (fmi2Real*)comp->functions->allocateMemory( comp->initialState.transformationStateSize,sizeof(fmi2Real) ); - comp->state.t = 0; + + comp->state.reals = allocateReals(comp->initialState.numReals); + comp->state.solverState = allocateReals(comp->initialState.solverStateSize); + comp->state.transformationState = allocateReals(comp->initialState.transformationStateSize); copyModelState( &(comp->state), &(comp->initialState) ); comp->stateCode = fmuStateCode::FMUINSTANTIATED; @@ -712,33 +714,7 @@ extern "C" { const char* fmi2GetVersion() { return fmi2Version; } - - - - - - - - - - - - - - - - - - - - - - - - - - - + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -747,43 +723,11 @@ extern "C" { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ////////////////////////////////////////////////////// // NOT IMPLEMENTED // ////////////////////////////////////////////////////// - + fmi2Status fmi2GetInteger(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Integer value[]) { // TODO(RunarHR): [Optional] Implement fmi2GetInteger @@ -940,7 +884,17 @@ extern "C" { comp->stateCode = fmuStateCode::FMUERROR; return fmi2Error; } - + + fmi2Status fmi2GetDirectionalDerivative(fmi2Component c, + const fmi2ValueReference vUnknown_ref[], size_t, + const fmi2ValueReference vKnown_ref[], size_t, + const fmi2Real dvKnown[], fmi2Real dvUnknown[]) + { + // TODO(RunarHR): [Optional] Implement fmi2GetDirectionalDerivative + static_cast(c)->stateCode = fmuStateCode::FMUERROR; + return fmi2Error; + } + #ifdef __cplusplus } // closing brace for extern "C" #endif From 1b09532954de76923be52b119e8ad2b430f42449 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Tue, 15 Oct 2024 22:58:53 +0200 Subject: [PATCH 2/6] Add stand-alone CMakeLists.txt for Fedem FMU --- FMU/CMakeLists.txt | 41 +++++++++++++++++++++++++++++++++++++++++ src/FMU/CMakeLists.txt | 5 ++--- 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 FMU/CMakeLists.txt diff --git a/FMU/CMakeLists.txt b/FMU/CMakeLists.txt new file mode 100644 index 00000000..c7b99c54 --- /dev/null +++ b/FMU/CMakeLists.txt @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2023 SAP SE +# +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of FEDEM - https://openfedem.org + +################################################################################ +# This is the top-level cmake project file for the Fedem FMU module. +################################################################################ + +cmake_minimum_required ( VERSION 2.8...3.5 ) +if ( POLICY CMP0076 ) + cmake_policy ( SET CMP0076 NEW ) # convert relative target source path names +endif ( POLICY CMP0076 ) + +# Project setup + +set ( APPLICATION_ID fedemFMU ) +set ( DOMAIN_ID FEDEM ) +set ( PACKAGE_ID SOLVERS ) + +project ( ${APPLICATION_ID} CXX ) +message ( STATUS "Generating build project for ${PROJECT_SOURCE_DIR}" ) +get_filename_component ( PARENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY ) + +find_path ( _MODULES FedemConfig.cmake + PATHS $ENV{CMAKE_MODULES} + "${PARENT_SOURCE_DIR}/fedem-foundation/cmake/Modules/" + ) +if ( _MODULES ) + message ( STATUS "NOTE : Using ${_MODULES}" ) + list ( APPEND CMAKE_MODULE_PATH ${_MODULES} ) +else ( _MODULES ) + message ( STATUS "ERROR : Missing path to FedemConfig.cmake" ) + message ( FATAL_ERROR "Set environment variable CMAKE_MODULES and try again" ) +endif ( _MODULES ) +unset ( _MODULES CACHE ) + +include ( FedemConfig ) + +add_subdirectory ( ../src/FMU "${CMAKE_CURRENT_BINARY_DIR}/src" ) diff --git a/src/FMU/CMakeLists.txt b/src/FMU/CMakeLists.txt index 8297ab5f..ee1a8466 100644 --- a/src/FMU/CMakeLists.txt +++ b/src/FMU/CMakeLists.txt @@ -12,15 +12,14 @@ set ( UNIT_ID ${DOMAIN_ID}_${PACKAGE_ID}_${LIB_ID} ) message ( STATUS "INFORMATION : Processing unit ${UNIT_ID}" ) string ( APPEND CMAKE_CXX_FLAGS_DEBUG " -DFMU_DEBUG" ) -if ( FTENV_WARNINGS AND LINUX AND NOT USE_INTEL_FORTRAN ) +if ( ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU ) string ( APPEND CMAKE_CXX_FLAGS " -Wno-unused-parameter" ) -endif ( FTENV_WARNINGS AND LINUX AND NOT USE_INTEL_FORTRAN ) +endif ( ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU ) # Build and install include_directories ( "${CMAKE_CURRENT_SOURCE_DIR}/FMI2/headers" ) add_library ( ${LIB_ID} SHARED fmu_template.C ) -add_dependencies ( all_solvers ${LIB_ID} ) set ( CLOUD_DIR "${CMAKE_INSTALL_PREFIX}/bin/Templates/cloudsim" ) install ( TARGETS ${LIB_ID} From 39ecbd24e9f4639256861f04193f01ea8c3d4157 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Wed, 16 Oct 2024 06:44:31 +0200 Subject: [PATCH 3/6] Add action to build and deploy Fedem FMU for Linux and Windows --- .github/workflows/build_doc.yml | 4 +- .github/workflows/build_fmu.yml | 110 ++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build_fmu.yml diff --git a/.github/workflows/build_doc.yml b/.github/workflows/build_doc.yml index b670d2a9..f7c47121 100644 --- a/.github/workflows/build_doc.yml +++ b/.github/workflows/build_doc.yml @@ -13,6 +13,8 @@ on: jobs: build-doc: + name: Build API documentation + runs-on: ubuntu-latest steps: @@ -30,7 +32,7 @@ jobs: - name: Checkout the latest release tag run: | - git checkout `git tag | tail -1` + git checkout `git tag | grep fedem- | tail -1` git submodule update - name: Configure for build diff --git a/.github/workflows/build_fmu.yml b/.github/workflows/build_fmu.yml new file mode 100644 index 00000000..23cef584 --- /dev/null +++ b/.github/workflows/build_fmu.yml @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: 2023 SAP SE +# +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of FEDEM - https://openfedem.org + +name: Build and release fedem FMU + +on: + push: + tags: + - fmu-* + + workflow_dispatch: + branches: + - main + +jobs: + build-linux: + name: Build FMU for Linux + + runs-on: ubuntu-latest + + steps: + - name: Silence some advice and hint + run: | + git config --global advice.detachedHead false + git config --global init.defaultBranch main + + - name: Check out source repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure the build + run: cmake -S ./FMU -B ./build -DCMAKE_BUILD_TYPE=Release + + - name: Build FMU library + run: cmake --build ./build --target fedem_fmu + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: fmu-linux + path: build/src/libfedem_fmu.so + + build-win: + name: Build FMU for Windows + + runs-on: windows-latest + + steps: + - name: Silence some advice and hint + run: | + git config --global advice.detachedHead false + git config --global init.defaultBranch main + + - name: Check out source repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure the build + run: cmake -S ./FMU -B ./build -G "Visual Studio 17 2022" + + - name: Build FMU library + run: cmake --build ./build --config Release --target fedem_fmu + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: fmu-win + path: build\src\Release\fedem_fmu.dll + + release-fmu: + name: Release FMU package + + needs: [ build-linux, build-win ] + + runs-on: ubuntu-latest + + steps: + - name: Silence some advice and hint + run: | + git config --global advice.detachedHead false + git config --global init.defaultBranch main + + - name: Check out source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.ACCESS_PAT }} + + - name: Get latest release tag + run: | + git checkout `git tag | grep fmu- tail -1` + echo "MY_TAG=`git tag | grep fmu- tail -1`" >> $GITHUB_ENV + echo "MY_VER=`cat cfg/VERSION`" >> $GITHUB_ENV + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Create release + uses: ncipollo/release-action@v1 + with: + tag: ${{ env.MY_TAG }} + name: "Fedem FMU ${{ env.MY_VER }}" + token: ${{ secrets.ACCESS_PAT }} + artifacts: "fmu-linux/libfedem_fmu.so,fmu-win/fedem_fmu.dll" + body: "FMU libraries for the FEDEM dynamics solver" From 9d8649b75a8844eaeac2ddccb0a0499a3341f65d Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Wed, 16 Oct 2024 20:58:14 +0200 Subject: [PATCH 4/6] Workaround creating empty source tar-balls for the Releases --- .gitattributes | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..51f5fed9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 SAP SE +# +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of FEDEM - https://openfedem.org + +# The following line will ensure the auto-generated tar-balls that +# github creates when makeing a new Release is empty. + +* export-ignore From 5593e69020785a943e5460d1b5107f0b3b55d13e Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Thu, 17 Oct 2024 06:52:47 +0200 Subject: [PATCH 5/6] Replace build_fedempy.yml by build_solvers.yml which also will build and deploy the solvers --- .github/workflows/build_fedempy.yml | 53 ------------------- .github/workflows/build_solvers.yml | 80 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/build_fedempy.yml create mode 100644 .github/workflows/build_solvers.yml diff --git a/.github/workflows/build_fedempy.yml b/.github/workflows/build_fedempy.yml deleted file mode 100644 index cc875ef7..00000000 --- a/.github/workflows/build_fedempy.yml +++ /dev/null @@ -1,53 +0,0 @@ -# SPDX-FileCopyrightText: 2023 SAP SE -# -# SPDX-License-Identifier: Apache-2.0 -# -# This file is part of FEDEM - https://openfedem.org - -name: Build and release fedempy - -on: - push: - tags: - - fedem-* - workflow_dispatch: - branches: - - main - -jobs: - publish-fedempy: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - token: ${{ secrets.ACCESS_PAT }} - - - name: Checkout the latest release tag - run: | - git checkout `git tag | tail -1` - echo "MY_TAG=`git tag | tail -1`" >> $GITHUB_ENV - echo "MY_VER=`cat cfg/VERSION`" >> $GITHUB_ENV - - - name: Build the fedempy module - run: | - if [ -e cfg/RELEASE ]; then - sed "1 s/.*$/fedempy package for Fedem &/" cfg/RELEASE | tr '\n' ' ' > body.md - else - echo -n "fedempy package for Fedem R8.0.4 " > body.md - fi - sed " 1 s/^.*\./(build /;s/.*$/& and later)/" cfg/VERSION >> body.md - cd PythonAPI - python -m setup sdist - - - name: Create release - uses: ncipollo/release-action@v1 - with: - tag: ${{ env.MY_TAG }} - name: "fedempy ${{ env.MY_VER }}" - token: ${{ secrets.ACCESS_PAT }} - artifacts: "PythonAPI/dist/fedempy-${{ env.MY_VER }}.tar.gz" - bodyFile: "body.md" - allowUpdates: true diff --git a/.github/workflows/build_solvers.yml b/.github/workflows/build_solvers.yml new file mode 100644 index 00000000..015bc621 --- /dev/null +++ b/.github/workflows/build_solvers.yml @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: 2023 SAP SE +# +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of FEDEM - https://openfedem.org + +name: Build and release fedem solvers + +on: + push: + tags: + - fedem-* + + workflow_dispatch: + branches: + - main + +jobs: + publish-solvers: + name: Publish solver packages + + runs-on: ubuntu-latest + + steps: + - name: Silence some advice and hint + run: | + git config --global advice.detachedHead false + git config --global init.defaultBranch main + + - name: Check out source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + token: ${{ secrets.ACCESS_PAT }} + + - name: Checkout the latest release tag + run: | + git checkout `git tag | grep fedem- | tail -1` + echo "MY_TAG=`git tag | grep fedem- | tail -1`" >> $GITHUB_ENV + echo "MY_VER=`cat cfg/VERSION`" >> $GITHUB_ENV + + - name: Configure main build + run: > + cmake -B ./build -DCMAKE_BUILD_TYPE=Release + -DBUILD_TESTS=OFF + -DBUILD_SOLVER_AS_DLL=ON + -DBUILD_CONTROL_AS_DLL=ON + -DUSE_CONCURRENT_RECOVERY=ON + -DUSE_SP_RECOVERY=ON + -DUSE_FFTPACK=ON + + - name: Build binaries + run: cmake --build ./build --target all_solvers + + - name: Prepare release package + run: | + mkdir bin + find build/src -name '*.so' -exec cp -p {} bin/ \; + find build/src -name 'fedem_*' -type f -exec cp -p {} bin/ \; + tar cfvz fedem-solvers-${{ env.MY_VER }}_linux64.tar.gz bin + sed "1 s/.*$/Solver package for Fedem &/" cfg/RELEASE | tr '\n' ' ' > body.md + sed "1 s/^.*\./(build /;s/.*$/&) for Linux 64-bit (Ubuntu)/" cfg/VERSION >> body.md + + - name: Build the fedempy package + run: | + sed "1 s/.*$/fedempy package for Fedem &/" cfg/RELEASE | tr '\n' ' ' >> body.md + sed "1 s/^.*\./(build /;s/.*$/& and later)/" cfg/VERSION >> body.md + cd PythonAPI + python -m setup sdist + + - name: Create release + uses: ncipollo/release-action@v1 + with: + tag: ${{ env.MY_TAG }} + name: "Fedem solvers ${{ env.MY_VER }}" + token: ${{ secrets.ACCESS_PAT }} + artifacts: "fedem-solvers-${{ env.MY_VER }}_linux64.tar.gz,PythonAPI/dist/fedempy-${{ env.MY_VER }}.tar.gz" + bodyFile: "body.md" + allowUpdates: true From bc25a071fd49a1e876b6710d7978d57e4e2e9025 Mon Sep 17 00:00:00 2001 From: Knut Morten Okstad Date: Fri, 18 Oct 2024 06:46:51 +0200 Subject: [PATCH 6/6] Reset build counter and update submodule reference. Update CHANGELOG for FEDEM R8.0.6. --- CHANGELOG.md | 20 ++++++++++++++++++++ PythonAPI/doc/source/conf.py | 2 +- cfg/VERSION | 2 +- fedem-foundation | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5386a4ef..3a749deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,25 @@ # FEDEM solvers Changelog +## [fedem-8.0.6] (2024-10-18) + +### :rocket: Added + +- Binaries for the solvers are automatically built for the Linux platform + whenever a new release tag is pushed, and attached as an artifact together with + the fedempy package on the [Releases](https://github.com/openfedem/fedem-solvers/releases) page. +- The FMU wrapper for the FEDEM solvers is built for both Windows and Linux platforms + and deployed as a separate release, whenever a tag named `fmu-*` is pushed. +- The auto-generated source code tar-balls associated with each realease are now empty, + since they will be incomplete anyway (missing submodule parts). + +### :bug: Fixed + +- https://github.com/openfedem/fedem-gui/issues/32 : + Wrong data format in the FMU shared object library. + Also handle missing definition of environment variable FEDEM_SOLVER in the FMU, + such that is does not crash but exits with a console error message. + ## [fedem-8.0.5] (2024-09-27) ### :rocket: Added @@ -73,3 +92,4 @@ [fedem-8.0.3]: https://github.com/openfedem/fedem-solvers/compare/fedem-8.0.1...fedem-8.0.3 [fedem-8.0.4]: https://github.com/openfedem/fedem-solvers/compare/fedem-8.0.3...fedem-8.0.4 [fedem-8.0.5]: https://github.com/openfedem/fedem-solvers/compare/fedem-8.0.4...fedem-8.0.5 +[fedem-8.0.6]: https://github.com/openfedem/fedem-solvers/compare/fedem-8.0.5...fedem-8.0.6 diff --git a/PythonAPI/doc/source/conf.py b/PythonAPI/doc/source/conf.py index 470ac897..7eec8a94 100644 --- a/PythonAPI/doc/source/conf.py +++ b/PythonAPI/doc/source/conf.py @@ -28,7 +28,7 @@ author = "Knut Morten Okstad" # The short X.Y version -version = "3.5" +version = "3.6" # The full version, including alpha/beta/rc tags release = "3.5.2" diff --git a/cfg/VERSION b/cfg/VERSION index 87ce4929..40c341bd 100644 --- a/cfg/VERSION +++ b/cfg/VERSION @@ -1 +1 @@ -3.5.2 +3.6.0 diff --git a/fedem-foundation b/fedem-foundation index 14b93881..f77f824d 160000 --- a/fedem-foundation +++ b/fedem-foundation @@ -1 +1 @@ -Subproject commit 14b938817afdb4604f6aea33ac7d9e3d69a52ff9 +Subproject commit f77f824d8a2f30de994b569c04f737b825c1d889