diff --git a/.clang-format b/.clang-format index 00adea7a..8284a618 100644 --- a/.clang-format +++ b/.clang-format @@ -3,11 +3,12 @@ Language: Cpp # BasedOnStyle: LLVM # # Proposed clang-format-11 style for OpenFOAM, trying to follow the OpenFOAM style guide: -# https://develop.openfoam.com/Development/openfoam/-/wikis/coding/style/style +# https://gitlab.com/openfoam/core/openfoam/-/wikis/coding/style/style # Configuration developed for the OpenFOAM-preCICE adapter code: # https://github.com/precice/openfoam-adapter # Contribute to the discussion at the respective OpenFOAM issue: -# https://develop.openfoam.com/Development/openfoam/-/issues/1634 +# https://develop.openfoam.com/Development/openfoam/-/issues/1634 (archived) +# https://gitlab.com/openfoam/core/openfoam/-/issues/1634 # # Keep `public:` at the first indentation level AccessModifierOffset: -4 diff --git a/.github/workflows/.gitignore b/.github/workflows/.gitignore new file mode 100644 index 00000000..72890765 --- /dev/null +++ b/.github/workflows/.gitignore @@ -0,0 +1,2 @@ +.secrets +.artifacts diff --git a/.github/workflows/build-custom.input b/.github/workflows/build-custom.input new file mode 100644 index 00000000..3b4fcc16 --- /dev/null +++ b/.github/workflows/build-custom.input @@ -0,0 +1,8 @@ +virtualEnvironment="ubuntu-22.04" +refAdapter="develop" +versionOpenFOAM="2606" +versionpreCICE="v3.4.1" +runTutorialHeatedPlate="false" +runTutorialQuickstart="false" +runTutorialPartitionedPipe="false" +branchTutorials="develop" diff --git a/.github/workflows/build-custom.yml b/.github/workflows/build-custom.yml index 7287a92c..e33f2819 100644 --- a/.github/workflows/build-custom.yml +++ b/.github/workflows/build-custom.yml @@ -4,13 +4,14 @@ on: inputs: virtualEnvironment: description: 'Virtual Environment' - default: 'ubuntu-18.04' + default: 'ubuntu-22.04' required: true type: choice options: + - ubuntu-26.04 + - ubuntu-24.04 - ubuntu-22.04 - ubuntu-20.04 - - ubuntu-18.04 refAdapter: description: 'Ref (branch/tag/commit) of the OpenFOAM adapter to build' default: 'develop' @@ -20,25 +21,30 @@ on: required: true type: choice options: - - OpenFOAMv2406 - - OpenFOAMv2312 - - OpenFOAMv2306 - - OpenFOAMv2212 - - OpenFOAMv2206 - - OpenFOAMv2112 - - OpenFOAMv2106 - - OpenFOAMv2012 - - OpenFOAMv2006 - - OpenFOAMv1912 - - OpenFOAM10 - - OpenFOAM9 - - OpenFOAM8 - - OpenFOAM7 - - OpenFOAM6 - - OpenFOAM5 + - 2606 + - 2512 + - 2506 + - 2412 + - 2406 + - 2312 + - 2306 + - 2212 + - 2206 + - 2112 + - 2106 + - 2012 + - 2006 + - 1912 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 versionpreCICE: description: 'Version of preCICE to build with' - default: 'v3.0.0' + default: 'v3.4.1' required: true runTutorialHeatedPlate: description: Run tutorial flow-over-heated-plate @@ -76,109 +82,20 @@ jobs: echo "Run tutorial partitioned-pipe: ${{ github.event.inputs.runTutorialPartitionedPipe }}" echo "Tutorials branch: ${{ github.event.inputs.branchTutorials }}" - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: ref: ${{ github.event.inputs.refAdapter }} - name: Install preCICE (no cache) uses: precice/setup-precice-action@main with: precice-version: ${{ github.event.inputs.versionpreCICE }} - - name: Install OpenFOAM (no cache) - id: installOpenFOAM - run: | - case "${{ github.event.inputs.versionOpenFOAM }}" in - OpenFOAMv2406) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2406-dev - echo "{openfoam_exec}={/usr/bin/openfoam2406}" >> $GITHUB_OUTPUT;; - OpenFOAMv2312) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2312-dev - echo "{openfoam_exec}={/usr/bin/openfoam2312}" >> $GITHUB_OUTPUT;; - OpenFOAMv2306) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2306-dev - echo "{openfoam_exec}={/usr/bin/openfoam2306}" >> $GITHUB_OUTPUT;; - OpenFOAMv2212) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2212-dev - echo "{openfoam_exec}={/usr/bin/openfoam2212}" >> $GITHUB_OUTPUT;; - OpenFOAMv2206) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2206-dev - echo "{openfoam_exec}={/usr/bin/openfoam2206}" >> $GITHUB_OUTPUT;; - OpenFOAMv2112) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2112-dev - echo "{openfoam_exec}={/usr/bin/openfoam2112}" >> $GITHUB_OUTPUT;; - OpenFOAMv2106) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2106-dev - echo "{openfoam_exec}={/usr/bin/openfoam2106}" >> $GITHUB_OUTPUT;; - OpenFOAMv2012) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2012-dev - echo "{openfoam_exec}={/usr/bin/openfoam2012}" >> $GITHUB_OUTPUT;; - OpenFOAMv2006) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam2006-dev - echo "{openfoam_exec}={/usr/bin/openfoam2006}" >> $GITHUB_OUTPUT;; - OpenFOAMv1912) - wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash - sudo apt-get install openfoam1912-dev - echo "{openfoam_exec}={/usr/bin/openfoam1912}" >> $GITHUB_OUTPUT;; - OpenFOAM10) - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam10 - echo "{openfoam_exec}={. /opt/openfoam10/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - OpenFOAM9) - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam9 - echo "{openfoam_exec}={. /opt/openfoam9/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - OpenFOAM8) - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam8 - echo "{openfoam_exec}={. /opt/openfoam8/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - OpenFOAM7) - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam7 - echo "{openfoam_exec}={. /opt/openfoam7/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - OpenFOAM6) - echo "OpenFOAM 6 is only available on Ubuntu 18.04 or older." - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam6 - echo "{openfoam_exec}={. /opt/openfoam6/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - OpenFOAM5) - echo "OpenFOAM 5 is only available on Ubuntu 18.04 or older." - sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -" - sudo add-apt-repository http://dl.openfoam.org/ubuntu - sudo apt-get update - sudo apt-get -y install openfoam5 - echo "{openfoam_exec}={. /opt/openfoam5/etc/bashrc &&}" >> $GITHUB_OUTPUT;; - *) - echo "I cannot find ${{ github.event.inputs.refAdapter }} in my known options." - exit 1;; - esac - - name: Sanity check - run: | - ls /usr/bin/openfoam* || echo "No /usr/bin/openfoam* directories." - ls /opt/openfoam* || echo "No /opt/openfoam* directories." - echo "GitHub Actions step output openfoam_exec:" - echo ${{steps.installOpenFOAM.outputs.openfoam_exec}} + - name: Set up OpenFOAM + uses: gerlero/setup-openfoam@v1 + with: + openfoam-version: ${{ github.event.inputs.versionOpenFOAM }} - name: Build OpenFOAM-preCICE adapter run: | - echo "OpenFOAM environment: ${{steps.installOpenFOAM.outputs.openfoam_exec}}" - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./Allwmake + ./Allwmake - name: Get tutorials run: | git clone https://github.com/precice/tutorials.git --branch ${{ github.event.inputs.branchTutorials }} --depth 1 @@ -187,10 +104,10 @@ jobs: if ${{ github.event.inputs.runTutorialHeatedPlate }} then cd tutorials/flow-over-heated-plate/fluid-openfoam - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./run.sh | tee fluid-openfoam.log 2>&1 & + ./run.sh | tee fluid-openfoam.log 2>&1 & PIDfluid=$! cd ../solid-openfoam - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./run.sh | tee solid-openfoam.log 2>&1 + ./run.sh | tee solid-openfoam.log 2>&1 wait $PIDfluid fi - name: Run tutorial quickstart @@ -198,19 +115,20 @@ jobs: if ${{ github.event.inputs.runTutorialQuickstart }} then cd tutorials/quickstart/fluid-openfoam - if [ "${{ github.event.inputs.versionOpenFOAM }}" == "OpenFOAM5" ] + if [ "${{ github.event.inputs.versionOpenFOAM }}" == 5 ] then echo "Manually changing from pimpleFoam to pimpleDyMFoam for compatibility with OpenFOAM 5." sed -i 's/pimpleFoam/pimpleDyMFoam/g' system/controlDict fi - if [ "${{ github.event.inputs.versionOpenFOAM }}" == "OpenFOAM9" ] + if [ "${{ github.event.inputs.versionOpenFOAM }}" == 9 ] then echo "Manually changing from solver to motionSolver in dynamicMeshDict for compatibility with OpenFOAM 9." sed -i 's/solver/motionSolver/g' constant/dynamicMeshDict fi - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./run.sh | tee fluid-openfoam.log 2>&1 & + ./run.sh | tee fluid-openfoam.log 2>&1 & PIDfluid=$! cd ../solid-cpp + apt-get update && apt-get -y install cmake cmake . && make && ./run.sh | tee solid-cpp.log 2>&1 wait $PIDfluid fi @@ -219,14 +137,14 @@ jobs: if ${{ github.event.inputs.runTutorialPartitionedPipe }} then cd tutorials/partitioned-pipe/fluid1-openfoam-pimplefoam - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./run.sh | tee fluid1-openfoam-pimplefoam.log 2>&1 & + ./run.sh | tee fluid1-openfoam-pimplefoam.log 2>&1 & PIDfluid=$! cd ../fluid2-openfoam-pimplefoam - ${{steps.installOpenFOAM.outputs.openfoam_exec}} ./run.sh | tee fluid2-openfoam-pimplefoam.log 2>&1 & + ./run.sh | tee fluid2-openfoam-pimplefoam.log 2>&1 & wait $PIDfluid fi - name: Archive logs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: logs path: | @@ -240,7 +158,7 @@ jobs: tutorials/partitioned-pipe/fluid1-openfoam-pimplefoam/fluid1-openfoam-pimplefoam.log tutorials/partitioned-pipe/fluid2-openfoam-pimplefoam/fluid2-openfoam-pimplefoam.log - name: Archive case files - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: case-files path: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5a79a0b..d5dd4719 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build with OpenFOAM v2404 +name: Build with OpenFOAM v2606 on: push: branches-ignore: @@ -20,28 +20,14 @@ on: - 'README.md' jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest container: precice/precice:nightly steps: - name: Check out repository - uses: actions/checkout@v4 - - name: Install action dependencies - run: | - apt-get update -qq - apt-get install -qq sudo wget - - name: Setup caches - uses: actions/cache@v4 - id: cache-id + uses: actions/checkout@v7 + - name: Set up OpenFOAM + uses: gerlero/setup-openfoam@v1 with: - path: ${{ runner.temp }}/cache-directory - key: ${{ runner.os }}-cache-dependencies-v1.0 - - name: Install OpenFOAM (considering cache) - uses: airvzxf/cache-anything-new-action@v1.0.1 - with: - script: 'install-openfoam.sh' - is_cached: ${{ steps.cache-id.outputs.cache-hit }} - cache: ${{ runner.temp }}/cache-directory - snapshot: '/' - exclude: '/boot /data /dev /mnt /proc /run /sys' + openfoam-version: 2606 - name: Build OpenFOAM-preCICE adapter - run: /usr/bin/openfoam2406 ./Allwmake + run: ./Allwmake diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml deleted file mode 100644 index c765840e..00000000 --- a/.github/workflows/check-format.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Check code formatting -on: [push, pull_request] -jobs: - formatting-check: - name: Check formatting (clang-format) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run clang-format style check for C/C++ programs. - uses: jidicula/clang-format-action@main - with: - clang-format-version: '11' - check-path: '.' diff --git a/.github/workflows/check-shell.yml b/.github/workflows/check-shell.yml deleted file mode 100644 index dd5474f8..00000000 --- a/.github/workflows/check-shell.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Lint scripts -on: [push, pull_request] -jobs: - check_shell: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v4 - - name: Lint shell scripts (shellcheck) - uses: ludeeus/action-shellcheck@master diff --git a/.github/workflows/install-openfoam.sh b/.github/workflows/install-openfoam.sh deleted file mode 100755 index 6a97a0e1..00000000 --- a/.github/workflows/install-openfoam.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -e - -# Install OpenFOAM v2406 -wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash -sudo apt-get update -y -sudo apt-get install -y openfoam2406-dev diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index f87390de..e6e0ee82 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout preCICE - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: - python-version: '3.10' + python-version: '3.x' check-latest: true - name: Install pre-commit run: pip install pre-commit diff --git a/.github/workflows/system-tests.yaml b/.github/workflows/system-tests.yaml index 90fa4709..bb9529ad 100644 --- a/.github/workflows/system-tests.yaml +++ b/.github/workflows/system-tests.yaml @@ -5,25 +5,9 @@ on: types: [labeled] jobs: - gather-refs: - if: ${{ github.event.label.name == 'trigger-system-tests' }} - runs-on: ubuntu-latest - outputs: - reftutorials: ${{ steps.reftutorials.outputs.shorthash }} - steps: - - id: reftutorials - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: tutorials - branch: develop - run-system-tests: if: ${{ github.event.label.name == 'trigger-system-tests' }} - needs: gather-refs - uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop + uses: precice/tutorials/.github/workflows/system-tests.yml@develop with: - suites: openfoam_adapter_pr - build_args: TUTORIALS_REF:${{ needs.gather-refs.outputs.reftutorials }},PRECICE_REF:v3.1.1,OPENFOAM_EXECUTABLE:openfoam2312,OPENFOAM_ADAPTER_REF:${{ github.event.pull_request.head.sha }} - systests_branch: develop - loglevel: "DEBUG" + suites: openfoam-adapter + build_args: OPENFOAM_EXECUTABLE:openfoam2512,OPENFOAM_ADAPTER_PR:${{ github.event.number }},OPENFOAM_ADAPTER_REF:${{ github.event.pull_request.head.sha }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f6d7347..2f84ce04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,14 +11,14 @@ repos: hooks: - id: clang-format - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.30.0 + rev: v0.48.0 hooks: - id: markdownlint files: "^docs/.*.md" - id: markdownlint-fix files: "^docs/.*.md" - repo: https://github.com/koalaman/shellcheck-precommit - rev: v0.10.0 + rev: v0.11.0 hooks: - id: shellcheck args: [ --external-sources, --exclude=SC1091 ] diff --git a/Adapter.C b/Adapter.C index d3a10962..40b0c1a8 100644 --- a/Adapter.C +++ b/Adapter.C @@ -3,6 +3,7 @@ #include "Utilities.H" #include "IOstreams.H" +#include using namespace Foam; @@ -10,417 +11,481 @@ preciceAdapter::Adapter::Adapter(const Time& runTime, const fvMesh& mesh) : runTime_(runTime), mesh_(mesh) { - adapterInfo("Loaded the OpenFOAM-preCICE adapter - v1.3.1.", "info"); + adapterInfo("Loaded the OpenFOAM-preCICE adapter - v1.4.0.", "info"); return; } -bool preciceAdapter::Adapter::configFileRead() +void preciceAdapter::Adapter::readFieldConfigs(const std::string& listName, Foam::ITstream& stream, std::vector& configs) { - - // We need a try-catch here, as if reading preciceDict fails, - // the respective exception will be reduced to a warning. - // See also comment in preciceAdapter::Adapter::configure(). - try + // Perform check on whether read/writeData is a list + if (stream.peek() == token::BEGIN_LIST) { - SETUP_TIMER(); - adapterInfo("Reading preciceDict...", "info"); - - // TODO: static is just a quick workaround to be able - // to find the dictionary also out of scope (e.g. in KappaEffective). - // We need a better solution. - static IOdictionary preciceDict( - IOobject( - "preciceDict", - runTime_.system(), - mesh_, - IOobject::MUST_READ_IF_MODIFIED, - IOobject::NO_WRITE)); - - // Read and display the preCICE configuration file name - preciceConfigFilename_ = preciceDict.get("preciceConfig"); - DEBUG(adapterInfo(" precice-config-file : " + preciceConfigFilename_)); - - // Read and display the participant name - participantName_ = preciceDict.get("participant"); - DEBUG(adapterInfo(" participant name : " + participantName_)); - - // Read and display the list of modules - DEBUG(adapterInfo(" modules requested : ")); - auto modules_ = preciceDict.get("modules"); - for (const auto& module : modules_) - { - DEBUG(adapterInfo(" - " + module + "\n")); + token _t; + stream >> _t; // First token is '(', which we throw away - // Set the modules switches - if (module == "CHT") + // Read stream until end of list + while (stream.peek() != token::END_LIST && !stream.eof()) + { + // Next token is always a word (the data name) + word dataName; + stream >> dataName; + + struct FieldConfig fieldConfig; + + // If next token is '{', we have a dictionary (new schema). + // We create a dictionary from the stream `dictionary dict(stream);` + // The dictionary must contain the 'name'. + // If 'solver_name' is not specified, it defaults to the same as 'name'. + // 'operation' defaults to 'value'. + // Note: Currently, the modules FF, CHT, and FSI do not use solver_name/operation. + if (stream.peek() == token::BEGIN_BLOCK) { - CHTenabled_ = true; + dictionary dict(stream); + fieldConfig.name = dict.get("name"); // The 'name' entry is mandatory. + fieldConfig.solver_name = dict.lookupOrDefault("solver_name", fieldConfig.name); + fieldConfig.operation = dict.lookupOrDefault("operation", "value"); + try + { + fieldConfig.flip_normal = dict.lookupOrDefault("flip-normal", false); + } + catch (const Foam::IOerror& e) + { + adapterInfo("Error parsing 'flip-normal' for field " + dataName + "\n" + e.message(), "error"); + } } - - if (module == "FSI") + // Else, we have a simple word entry (legacy schema/backwards compatibility). + else { - FSIenabled_ = true; + fieldConfig.name = dataName; + fieldConfig.solver_name = "Undefined (legacy mode)"; + fieldConfig.operation = "Undefined (legacy mode)"; + fieldConfig.flip_normal = false; } - if (module == "FF") - { - FFenabled_ = true; - } + configs.push_back(fieldConfig); + + DEBUG(adapterInfo(" - " + dataName)); + DEBUG(adapterInfo(" name: " + fieldConfig.name)); + DEBUG(adapterInfo(" solver_name: " + fieldConfig.solver_name)); + DEBUG(adapterInfo(" operation : " + fieldConfig.operation)); + DEBUG(adapterInfo(" flip-normal: " + std::string(fieldConfig.flip_normal ? "true" : "false"))); + } + stream >> _t; // Last token ')' + } + else + { + adapterInfo(listName + " must be a list", "error"); + } +} + +void preciceAdapter::Adapter::configFileRead() +{ + + SETUP_TIMER(); + adapterInfo("Reading preciceDict...", "info"); + + // TODO: static is just a quick workaround to be able + // to find the dictionary also out of scope (e.g. in KappaEffective). + // We need a better solution. + static IOdictionary preciceDict( + IOobject( + "preciceDict", + runTime_.system(), + mesh_, + IOobject::MUST_READ_IF_MODIFIED, + IOobject::NO_WRITE)); + + // Read and display the preCICE configuration file name + preciceConfigFilename_ = preciceDict.get("preciceConfig"); + DEBUG(adapterInfo(" precice-config-file : " + preciceConfigFilename_)); + + // Read and display the participant name + participantName_ = preciceDict.get("participant"); + DEBUG(adapterInfo(" participant name : " + participantName_)); + + // Read and display the list of modules + DEBUG(adapterInfo(" modules requested : ")); + auto modules_ = preciceDict.get("modules"); + for (const auto& module : modules_) + { + DEBUG(adapterInfo(" - " + module + "\n")); + + // Set the modules switches + if (module == "CHT") + { + CHTenabled_ = true; } - // Every interface is a subdictionary of "interfaces", - // each with an arbitrary name. Read all of them and create - // a list (here: pointer) of dictionaries. - const auto* interfaceDictPtr = preciceDict.findDict("interfaces"); - DEBUG(adapterInfo(" interfaces : ")); + if (module == "FSI") + { + FSIenabled_ = true; + } - // Check if we found any interfaces - // and get the details of each interface - if (!interfaceDictPtr) + if (module == "FF") { - adapterInfo(" Empty list of interfaces", "warning"); - return false; + FFenabled_ = true; } - else + + if (module == "generic") { - for (const entry& interfaceDictEntry : *interfaceDictPtr) + genericModuleEnabled_ = true; + } + } + + // Every interface is a subdictionary of "interfaces", + // each with an arbitrary name. Read all of them and create + // a list (here: pointer) of dictionaries. + const auto* interfaceDictPtr = preciceDict.findDict("interfaces"); + DEBUG(adapterInfo(" interfaces : ")); + + // Check if we found any interfaces + // and get the details of each interface + if (!interfaceDictPtr) + { + adapterInfo(" Empty list of interfaces", "error"); + return; + } + else + { + for (const entry& interfaceDictEntry : *interfaceDictPtr) + { + if (interfaceDictEntry.isDict()) { - if (interfaceDictEntry.isDict()) + const dictionary& interfaceDict = interfaceDictEntry.dict(); + struct InterfaceConfig interfaceConfig; + + interfaceConfig.meshName = interfaceDict.get("mesh"); + DEBUG(adapterInfo(" - mesh : " + interfaceConfig.meshName)); + + // By default, assume "faceCenters" as locationsType + interfaceConfig.locationsType = interfaceDict.lookupOrDefault("locations", "faceCenters"); + DEBUG(adapterInfo(" locations : " + interfaceConfig.locationsType)); + + // By default, assume that no mesh connectivity is required (i.e. no nearest-projection mapping) + interfaceConfig.meshConnectivity = interfaceDict.lookupOrDefault("connectivity", false); + // Mesh connectivity only makes sense in case of faceNodes, check and raise a warning otherwise + if (interfaceConfig.meshConnectivity && (interfaceConfig.locationsType == "faceCenters" || interfaceConfig.locationsType == "volumeCenters" || interfaceConfig.locationsType == "volumeCentres")) { - const dictionary& interfaceDict = interfaceDictEntry.dict(); - struct InterfaceConfig interfaceConfig; - - interfaceConfig.meshName = interfaceDict.get("mesh"); - DEBUG(adapterInfo(" - mesh : " + interfaceConfig.meshName)); - - // By default, assume "faceCenters" as locationsType - interfaceConfig.locationsType = interfaceDict.lookupOrDefault("locations", "faceCenters"); - DEBUG(adapterInfo(" locations : " + interfaceConfig.locationsType)); - - // By default, assume that no mesh connectivity is required (i.e. no nearest-projection mapping) - interfaceConfig.meshConnectivity = interfaceDict.lookupOrDefault("connectivity", false); - // Mesh connectivity only makes sense in case of faceNodes, check and raise a warning otherwise - if (interfaceConfig.meshConnectivity && (interfaceConfig.locationsType == "faceCenters" || interfaceConfig.locationsType == "volumeCenters" || interfaceConfig.locationsType == "volumeCentres")) - { - DEBUG(adapterInfo("Mesh connectivity is not supported for faceCenters or volumeCenters. \n" - "Please configure the desired interface with the locationsType faceNodes. \n" - "Have a look in the adapter documentation for detailed information.", - "warning")); - return false; - } - DEBUG(adapterInfo(" connectivity : " + std::to_string(interfaceConfig.meshConnectivity))); - - DEBUG(adapterInfo(" patches : ")); - auto patches = interfaceDict.get("patches"); - for (auto patch : patches) - { - interfaceConfig.patchNames.push_back(patch); - DEBUG(adapterInfo(" - " + patch)); - } - - DEBUG(adapterInfo(" cellSets : ")); - auto cellSets = interfaceDict.lookupOrDefault("cellSets", wordList()); - - for (auto cellSet : cellSets) - { - interfaceConfig.cellSetNames.push_back(cellSet); - DEBUG(adapterInfo(" - " + cellSet)); - } - - if (!interfaceConfig.cellSetNames.empty() && !(interfaceConfig.locationsType == "volumeCenters" || interfaceConfig.locationsType == "volumeCentres")) - { - adapterInfo("Cell sets are not supported for locationType != volumeCenters. \n" - "Please configure the desired interface with the locationsType volumeCenters. \n" - "Have a look in the adapter documentation for detailed information.", - "warning"); - return false; - } + DEBUG(adapterInfo("Mesh connectivity is not supported for faceCenters or volumeCenters. \n" + "Please configure the desired interface with the locationsType faceNodes. \n" + "Have a look in the adapter documentation for detailed information.", + "error")); + return; + } + DEBUG(adapterInfo(" connectivity : " + std::to_string(interfaceConfig.meshConnectivity))); + + DEBUG(adapterInfo(" patches : ")); + auto patches = interfaceDict.get("patches"); + for (auto patch : patches) + { + interfaceConfig.patchNames.push_back(patch); + DEBUG(adapterInfo(" - " + patch)); + } + DEBUG(adapterInfo(" cellSets : ")); + auto cellSets = interfaceDict.lookupOrDefault("cellSets", wordList()); + + for (auto cellSet : cellSets) + { + interfaceConfig.cellSetNames.push_back(cellSet); + DEBUG(adapterInfo(" - " + cellSet)); + } + + if (!interfaceConfig.cellSetNames.empty() && !(interfaceConfig.locationsType == "volumeCenters" || interfaceConfig.locationsType == "volumeCentres")) + { + adapterInfo("Cell sets are not supported for locationType != volumeCenters. \n" + "Please configure the desired interface with the locationsType volumeCenters. \n" + "Have a look in the adapter documentation for detailed information.", + "error"); + return; + } + + if (interfaceDict.found("writeData")) + { DEBUG(adapterInfo(" writeData : ")); - auto writeData = interfaceDict.get("writeData"); - for (auto writeDatum : writeData) - { - interfaceConfig.writeData.push_back(writeDatum); - DEBUG(adapterInfo(" - " + writeDatum)); - } + ITstream writeDataStream = interfaceDict.lookup("writeData"); + readFieldConfigs("writeData", writeDataStream, interfaceConfig.writeData); + } + if (interfaceDict.found("readData")) + { DEBUG(adapterInfo(" readData : ")); - auto readData = interfaceDict.get("readData"); - for (auto readDatum : readData) - { - interfaceConfig.readData.push_back(readDatum); - DEBUG(adapterInfo(" - " + readDatum)); - } - interfacesConfig_.push_back(interfaceConfig); + ITstream readDataStream = interfaceDict.lookup("readData"); + readFieldConfigs("readData", readDataStream, interfaceConfig.readData); } + + interfacesConfig_.push_back(interfaceConfig); } } + } - // NOTE: set the switch for your new module here + // NOTE: set the switch for your new module here - // If the CHT module is enabled, create it, read the - // CHT-specific options and configure it. - if (CHTenabled_) + if (genericModuleEnabled_) + { + Generic_ = new Generic::GenericInterface(mesh_); + if (!Generic_->configure(preciceDict)) { - CHT_ = new CHT::ConjugateHeatTransfer(mesh_); - if (!CHT_->configure(preciceDict)) - { - return false; - } + return; } + } - // If the FSI module is enabled, create it, read the - // FSI-specific options and configure it. - if (FSIenabled_) + // If the CHT module is enabled, create it, read the + // CHT-specific options and configure it. + if (CHTenabled_) + { + CHT_ = new CHT::ConjugateHeatTransfer(mesh_); + if (!CHT_->configure(preciceDict)) { - // Check for unsupported FSI with meshConnectivity - for (uint i = 0; i < interfacesConfig_.size(); i++) - { - if (interfacesConfig_.at(i).meshConnectivity == true) - { - adapterInfo( - "You have requested mesh connectivity (most probably for nearest-projection mapping) " - "and you have enabled the FSI module. " - "Mapping with connectivity information is not implemented for FSI, only for CHT-related fields. " - "warning"); - return false; - } - } - - FSI_ = new FSI::FluidStructureInteraction(mesh_, runTime_); - if (!FSI_->configure(preciceDict)) - { - return false; - } + adapterInfo("There was an error while configuring the CHT module", + "error"); + return; } + } - if (FFenabled_) + // If the FSI module is enabled, create it, read the + // FSI-specific options and configure it. + if (FSIenabled_) + { + FSI_ = new FSI::FluidStructureInteraction(mesh_, runTime_); + if (!FSI_->configure(preciceDict)) { - FF_ = new FF::FluidFluid(mesh_); - if (!FF_->configure(preciceDict)) - { - return false; - } + adapterInfo("There was an error while configuring the FSI module", + "error"); + return; } + } - // NOTE: Create your module and read any options specific to it here - - if (!CHTenabled_ && !FSIenabled_ && !FFenabled_) // NOTE: Add your new switch here + if (FFenabled_) + { + FF_ = new FF::FluidFluid(mesh_); + if (!FF_->configure(preciceDict)) { - adapterInfo("No module is enabled.", "error-deferred"); - return false; + adapterInfo("There was an error while configuring the FF module", + "error"); + return; } + } - // TODO: Loading modules should be implemented in more general way, - // in order to avoid code duplication. See issue #16 on GitHub. + // NOTE: Create your module and read any options specific to it here - ACCUMULATE_TIMER(timeInConfigRead_); - } - catch (const Foam::error& e) + if (!CHTenabled_ && !FSIenabled_ && !FFenabled_ && !genericModuleEnabled_) // NOTE: Add your new switch here { - adapterInfo(e.message(), "error-deferred"); - return false; + adapterInfo("No module is enabled.", "error"); + return; } - return true; + // TODO: Loading modules should be implemented in more general way, + // in order to avoid code duplication. See issue #16 on GitHub. + + ACCUMULATE_TIMER(timeInConfigRead_); + + return; } void preciceAdapter::Adapter::configure() +try { // Read the adapter's configuration file - if (!configFileRead()) - { - // This method is called from the functionObject's read() method, - // which is called by the Foam::functionObjectList::read() method. - // All the exceptions triggered in this method are caught as - // warnings and the simulation continues simply without the - // functionObject. However, we want the simulation to exit with an - // error in case something is wrong. We store the information that - // there was an error and it will be handled by the first call to - // the functionObject's execute(), which can throw errors normally. - errorsInConfigure = true; + configFileRead(); - return; + // Check the timestep type (fixed vs adjustable) + DEBUG(adapterInfo("Checking the timestep type (fixed vs adjustable)...")); + adjustableTimestep_ = runTime_.controlDict().lookupOrDefault("adjustTimeStep", false); + + if (adjustableTimestep_) + { + DEBUG(adapterInfo(" Timestep type: adjustable.")); + } + else + { + DEBUG(adapterInfo(" Timestep type: fixed.")); } - try + // Construct preCICE + SETUP_TIMER(); + DEBUG(adapterInfo("Creating the preCICE solver interface...")); + DEBUG(adapterInfo(" Number of processes: " + std::to_string(Pstream::nProcs()))); + DEBUG(adapterInfo(" MPI rank: " + std::to_string(Pstream::myProcNo()))); + precice_ = new precice::Participant(participantName_, preciceConfigFilename_, Pstream::myProcNo(), Pstream::nProcs()); + DEBUG(adapterInfo(" preCICE solver interface was created.")); + + ACCUMULATE_TIMER(timeInPreciceConstruct_); + + // Create interfaces + REUSE_TIMER(); + DEBUG(adapterInfo("Creating interfaces...")); + for (uint i = 0; i < interfacesConfig_.size(); i++) { - // Check the timestep type (fixed vs adjustable) - DEBUG(adapterInfo("Checking the timestep type (fixed vs adjustable)...")); - adjustableTimestep_ = runTime_.controlDict().lookupOrDefault("adjustTimeStep", false); + std::string namePointDisplacement = FSIenabled_ ? FSI_->getPointDisplacementFieldName() : "default"; + std::string nameCellDisplacement = FSIenabled_ ? FSI_->getCellDisplacementFieldName() : "default"; + bool restartFromDeformed = FSIenabled_ ? FSI_->isRestartingFromDeformed() : false; - if (adjustableTimestep_) - { - DEBUG(adapterInfo(" Timestep type: adjustable.")); - } - else - { - DEBUG(adapterInfo(" Timestep type: fixed.")); - } + Interface* interface = new Interface(*precice_, mesh_, interfacesConfig_.at(i).meshName, interfacesConfig_.at(i).locationsType, interfacesConfig_.at(i).patchNames, interfacesConfig_.at(i).cellSetNames, interfacesConfig_.at(i).meshConnectivity, restartFromDeformed, namePointDisplacement, nameCellDisplacement); + interfaces_.push_back(interface); + DEBUG(adapterInfo("Interface created on mesh " + interfacesConfig_.at(i).meshName)); - // Construct preCICE - SETUP_TIMER(); - DEBUG(adapterInfo("Creating the preCICE solver interface...")); - DEBUG(adapterInfo(" Number of processes: " + std::to_string(Pstream::nProcs()))); - DEBUG(adapterInfo(" MPI rank: " + std::to_string(Pstream::myProcNo()))); - precice_ = new precice::Participant(participantName_, preciceConfigFilename_, Pstream::myProcNo(), Pstream::nProcs()); - DEBUG(adapterInfo(" preCICE solver interface was created.")); - - ACCUMULATE_TIMER(timeInPreciceConstruct_); - - // Create interfaces - REUSE_TIMER(); - DEBUG(adapterInfo("Creating interfaces...")); - for (uint i = 0; i < interfacesConfig_.size(); i++) + DEBUG(adapterInfo("Adding coupling data writers...")); + for (uint j = 0; j < interfacesConfig_.at(i).writeData.size(); j++) { - std::string namePointDisplacement = FSIenabled_ ? FSI_->getPointDisplacementFieldName() : "default"; - std::string nameCellDisplacement = FSIenabled_ ? FSI_->getCellDisplacementFieldName() : "default"; - bool restartFromDeformed = FSIenabled_ ? FSI_->isRestartingFromDeformed() : false; + const FieldConfig& fieldConfig = interfacesConfig_.at(i).writeData.at(j); + std::string dataName = fieldConfig.name; - Interface* interface = new Interface(*precice_, mesh_, interfacesConfig_.at(i).meshName, interfacesConfig_.at(i).locationsType, interfacesConfig_.at(i).patchNames, interfacesConfig_.at(i).cellSetNames, interfacesConfig_.at(i).meshConnectivity, restartFromDeformed, namePointDisplacement, nameCellDisplacement); - interfaces_.push_back(interface); - DEBUG(adapterInfo("Interface created on mesh " + interfacesConfig_.at(i).meshName)); + unsigned int inModules = 0; - DEBUG(adapterInfo("Adding coupling data writers...")); - for (uint j = 0; j < interfacesConfig_.at(i).writeData.size(); j++) + // Add CHT-related coupling data writers + if (CHTenabled_ && CHT_->addWriters(fieldConfig, interface)) { - std::string dataName = interfacesConfig_.at(i).writeData.at(j); - - unsigned int inModules = 0; + inModules++; + } - // Add CHT-related coupling data writers - if (CHTenabled_ && CHT_->addWriters(dataName, interface)) - { - inModules++; - } + // Add FSI-related coupling data writers + if (FSIenabled_ && FSI_->addWriters(fieldConfig, interface)) + { + inModules++; + } - // Add FSI-related coupling data writers - if (FSIenabled_ && FSI_->addWriters(dataName, interface)) - { - inModules++; - } + // Add FF-related coupling data writers + if (FFenabled_ && FF_->addWriters(fieldConfig, interface)) + { + inModules++; + } - // Add FF-related coupling data writers - if (FFenabled_ && FF_->addWriters(dataName, interface)) + // Add generic module coupling data writers + // Only add Generic interface if not found in other modules + if (inModules == 0) + { + if (genericModuleEnabled_ && Generic_->addWriters(fieldConfig, interface)) { inModules++; - } + }; + } - if (inModules == 0) - { - adapterInfo("I don't know how to write \"" + dataName - + "\". Maybe this is a typo or maybe you need to enable some adapter module?", - "error-deferred"); - } - else if (inModules > 1) - { - adapterInfo("It looks like more than one modules can write \"" + dataName - + "\" and I don't know how to choose. Try disabling one of the modules.", - "error-deferred"); - } + if (inModules == 0) + { + adapterInfo("I don't know how to write \"" + dataName + + "\". Maybe this is a typo or maybe you need to enable some adapter module?", + "error"); + } + else if (inModules > 1) + { + adapterInfo("It looks like more than one modules can write \"" + dataName + + "\" and I don't know how to choose. Try disabling one of the modules.", + "error"); + } - // NOTE: Add any coupling data writers for your module here. - } // end add coupling data writers + // NOTE: Add any coupling data writers for your module here. + } // end add coupling data writers - DEBUG(adapterInfo("Adding coupling data readers...")); - for (uint j = 0; j < interfacesConfig_.at(i).readData.size(); j++) - { - std::string dataName = interfacesConfig_.at(i).readData.at(j); + DEBUG(adapterInfo("Adding coupling data readers...")); + for (uint j = 0; j < interfacesConfig_.at(i).readData.size(); j++) + { + const FieldConfig& fieldConfig = interfacesConfig_.at(i).readData.at(j); + std::string dataName = fieldConfig.name; - unsigned int inModules = 0; + unsigned int inModules = 0; - // Add CHT-related coupling data readers - if (CHTenabled_ && CHT_->addReaders(dataName, interface)) inModules++; + // Add CHT-related coupling data readers + if (CHTenabled_ && CHT_->addReaders(fieldConfig, interface)) + { + inModules++; + } - // Add FSI-related coupling data readers - if (FSIenabled_ && FSI_->addReaders(dataName, interface)) inModules++; + // Add FSI-related coupling data readers + if (FSIenabled_ && FSI_->addReaders(fieldConfig, interface)) + { + inModules++; + } - // Add FF-related coupling data readers - if (FFenabled_ && FF_->addReaders(dataName, interface)) inModules++; + // Add FF-related coupling data readers + if (FFenabled_ && FF_->addReaders(fieldConfig, interface)) + { + inModules++; + } - if (inModules == 0) - { - adapterInfo("I don't know how to read \"" + dataName - + "\". Maybe this is a typo or maybe you need to enable some adapter module?", - "error-deferred"); - } - else if (inModules > 1) + // Add generic module coupling data readers + // Only add Generic interface if not found in other modules + if (inModules == 0) + { + if (genericModuleEnabled_ && Generic_->addReaders(fieldConfig, interface)) { - adapterInfo("It looks like more than one modules can read \"" + dataName - + "\" and I don't know how to choose. Try disabling one of the modules.", - "error-deferred"); + inModules++; } + } - // NOTE: Add any coupling data readers for your module here. - } // end add coupling data readers - - // Create the interface's data buffer - interface->createBuffer(); - } - ACCUMULATE_TIMER(timeInMeshSetup_); + if (inModules == 0) + { + adapterInfo("I don't know how to read \"" + dataName + + "\". Maybe this is a typo or maybe you need to enable some adapter module?", + "error"); + } + else if (inModules > 1) + { + adapterInfo("It looks like more than one modules can read \"" + dataName + + "\" and I don't know how to choose. Try disabling one of the modules.", + "error"); + } - // Initialize preCICE and exchange the first coupling data - initialize(); + // NOTE: Add any coupling data readers for your module here. + } // end add coupling data readers - // If checkpointing is required, specify the checkpointed fields - // and write the first checkpoint - if (requiresWritingCheckpoint()) - { - checkpointing_ = true; + // Create the interface's data buffer + interface->createBuffer(); + } + ACCUMULATE_TIMER(timeInMeshSetup_); - // Setup the checkpointing (find and add fields to checkpoint) - setupCheckpointing(); + // Initialize preCICE and exchange the first coupling data + initialize(); - // Write checkpoint (for the first iteration) - writeCheckpoint(); - } + // If checkpointing is required, specify the checkpointed fields + // and write the first checkpoint + if (requiresWritingCheckpoint()) + { + checkpointing_ = true; - // Adjust the timestep for the first iteration, if it is fixed - if (!adjustableTimestep_) - { - adjustSolverTimeStepAndReadData(); - } + // Setup the checkpointing (find and add fields to checkpoint) + setupCheckpointing(); - // If the solver tries to end before the coupling is complete, - // e.g. because the solver's endTime was smaller or (in implicit - // coupling) equal with the max-time specified in preCICE, - // problems may occur near the end of the simulation, - // as the function object may be called only once near the end. - // See the implementation of Foam::Time::run() for more details. - // To prevent this, we set the solver's endTime to "infinity" - // and let only preCICE control the end of the simulation. - // This has the side-effect of not triggering the end() method - // in any function object normally. Therefore, we trigger it - // when preCICE dictates to stop the coupling. - adapterInfo( - "Setting the solver's endTime to infinity to prevent early exits. " - "Only preCICE will control the simulation's endTime. " - "Any functionObject's end() method will be triggered by the adapter. " - "You may disable this behavior in the adapter's configuration.", - "info"); - const_cast(runTime_).setEndTime(GREAT); + // Write checkpoint (for the first iteration) + writeCheckpoint(); } - catch (const Foam::error& e) + + // Adjust the timestep for the first iteration, if it is fixed + if (!adjustableTimestep_) { - adapterInfo(e.message(), "error-deferred"); - errorsInConfigure = true; + adjustSolverTimeStepAndReadData(); } + // If the solver tries to end before the coupling is complete, + // e.g. because the solver's endTime was smaller or (in implicit + // coupling) equal with the max-time specified in preCICE, + // problems may occur near the end of the simulation, + // as the function object may be called only once near the end. + // See the implementation of Foam::Time::run() for more details. + // To prevent this, we set the solver's endTime to "infinity" + // and let only preCICE control the end of the simulation. + // This has the side-effect of not triggering the end() method + // in any function object normally. Therefore, we trigger it + // when preCICE dictates to stop the coupling. + adapterInfo( + "Setting the solver's endTime to infinity to prevent early exits. " + "Only preCICE will control the simulation's endTime. " + "Any functionObject's end() method will be triggered by the adapter. " + "You may disable this behavior in the adapter's configuration.", + "info"); + const_cast(runTime_).setEndTime(GREAT); + return; } +catch (const PreciceError& e) +{ + std::exit(EXIT_FAILURE); +} void preciceAdapter::Adapter::execute() +try { - if (errorsInConfigure) - { - // Handle any errors during configure(). - // See the comments in configure() for details. - adapterInfo( - "There was a problem while configuring the adapter. " - "See the log for details.", - "error"); - } // The solver has already solved the equations for this timestep. // Now call the adapter's methods to perform the coupling. @@ -437,6 +502,7 @@ void preciceAdapter::Adapter::execute() // Read checkpoint if required if (requiresReadingCheckpoint()) { + pruneCheckpointedFields(); readCheckpoint(); } @@ -499,14 +565,23 @@ void preciceAdapter::Adapter::execute() return; } +catch (const PreciceError& e) +{ + std::exit(EXIT_FAILURE); +} void preciceAdapter::Adapter::adjustTimeStep() +try { adjustSolverTimeStepAndReadData(); return; } +catch (const PreciceError& e) +{ + std::exit(EXIT_FAILURE); +} void preciceAdapter::Adapter::readCouplingData(double relativeReadTime) { @@ -544,9 +619,11 @@ void preciceAdapter::Adapter::initialize() SETUP_TIMER(); if (precice_->requiresInitialData()) + { + DEBUG(adapterInfo("Initializing preCICE data...")); writeCouplingData(); + } - DEBUG(adapterInfo("Initializing preCICE data...")); precice_->initialize(); preciceInitialized_ = true; ACCUMULATE_TIMER(timeInInitialize_); @@ -558,7 +635,7 @@ void preciceAdapter::Adapter::initialize() void preciceAdapter::Adapter::finalize() { - if (NULL != precice_ && preciceInitialized_ && !isCouplingOngoing()) + if (nullptr != precice_ && preciceInitialized_ && !isCouplingOngoing()) { DEBUG(adapterInfo("Finalizing the preCICE solver interface...")); @@ -652,13 +729,11 @@ void preciceAdapter::Adapter::adjustSolverTimeStepAndReadData() double tolerance = 1e-14; if (precice_->getMaxTimeStepSize() - timestepSolverDetermined > tolerance) { - // Add a bool 'subCycling = true' which is checked in the storeMeshPoints() function. adapterInfo( "The solver's timestep is smaller than the " "coupling timestep. Subcycling...", "info"); timestepSolver_ = timestepSolverDetermined; - // TODO subcycling is enabled. For FSI the oldVolumes must be written, which is normally not done. if (FSIenabled_) { adapterInfo( @@ -669,7 +744,7 @@ void preciceAdapter::Adapter::adjustSolverTimeStepAndReadData() else if (timestepSolverDetermined - precice_->getMaxTimeStepSize() > tolerance) { // In the last time-step, we adjust to dt = 0, but we don't need to trigger the warning here - if (precice_->isCouplingOngoing()) + if (isCouplingOngoing()) { adapterInfo( "The solver's timestep cannot be larger than the coupling timestep." @@ -696,7 +771,6 @@ void preciceAdapter::Adapter::adjustSolverTimeStepAndReadData() // Read the received coupling data from the buffer // Fits to an implicit Euler readCouplingData(runTime_.deltaT().value()); - return; } @@ -708,7 +782,7 @@ bool preciceAdapter::Adapter::isCouplingOngoing() // the solver would try to access this method again, // giving a segmentation fault if precice_ // was not available. - if (NULL != precice_) + if (nullptr != precice_) { isCouplingOngoing = precice_->isCouplingOngoing(); } @@ -752,33 +826,31 @@ void preciceAdapter::Adapter::reloadCheckpointTime() void preciceAdapter::Adapter::storeMeshPoints() { - DEBUG(adapterInfo("Storing mesh points...")); - // TODO: In foam-extend, we would need "allPoints()". Check if this gives the same data. - meshPoints_ = mesh_.points(); - oldMeshPoints_ = mesh_.oldPoints(); - - /* - // TODO This is only required for subcycling. It should not be called when not subcycling!! - // Add a bool 'subcycling' which can be evaluated every timestep. - if ( !oldVolsStored && mesh_.foundObject("V00") ) // For Ddt schemes which use one previous timestep + if (!meshPoints_) { - setupMeshVolCheckpointing(); - oldVolsStored = true; + DEBUG(adapterInfo("Storing mesh points...")); + // Add points and oldPoints + meshPoints_ = new Foam::pointField(mesh_.points()); + meshOldPoints_ = new Foam::pointField(mesh_.oldPoints()); } - // Update any volume fields from the buffer to the checkpointed values (if already exists.) - */ - DEBUG(adapterInfo("Stored mesh points.")); if (mesh_.moving()) { - if (!meshCheckPointed) + if (!meshCheckPointed_) { // Set up the checkpoint for the mesh flux: meshPhi setupMeshCheckpointing(); - meshCheckPointed = true; + meshCheckPointed_ = true; } writeMeshCheckpoint(); - writeVolCheckpoint(); // Does not write anything unless subcycling. + + // For Ddt schemes which use up to two previous timesteps V0, V00 + if (volumeCheckpointState_ != VolumeCheckpointState::ALL_AVAILABLE) + { + setupMeshVolCheckpointing(); + } + + writeMeshVolCheckpoint(); } } @@ -790,100 +862,50 @@ void preciceAdapter::Adapter::reloadMeshPoints() return; } - // In Foam::polyMesh::movePoints. - // TODO: The function movePoints overwrites the pointer to the old mesh. - // Therefore, if you revert the mesh, the oldpointer will be set to the points, which are the new values. - DEBUG(adapterInfo("Moving mesh points to their previous locations...")); + // Reload mesh points + const_cast(mesh_).movePoints(*meshPoints_); - // TODO - // Switch oldpoints on for pure physics. (is this required?). Switch off for better mesh deformation capabilities? - // const_cast(mesh_.points()) = oldMeshPoints_; - const_cast(mesh_).movePoints(meshPoints_); + // polyMesh.movePoints will only update oldPoints + // if (curMotionTimeIndex_ != time().timeIndex()) + const_cast(mesh_.oldPoints()) = *meshOldPoints_; - DEBUG(adapterInfo("Moved mesh points to their previous locations.")); + readMeshCheckpoint(); - // TODO The if statement can be removed in this case, but it is still included for clarity - if (meshCheckPointed) - { - readMeshCheckpoint(); - } + DEBUG(adapterInfo("Moved mesh points to their previous locations.")); - /* // TODO This part should only be used when sybcycling. See the description in 'storeMeshPoints()' - // The if statement can be removed in this case, but it is still included for clarity - if ( oldVolsStored ) - { - readVolCheckpoint(); - } - */ + readMeshVolCheckpoint(); } void preciceAdapter::Adapter::setupMeshCheckpointing() { - // The other mesh Fields: - // C - // Cf - // Sf - // magSf - // delta - // are updated by the function fvMesh::movePoints. Only the meshPhi needs checkpointing. DEBUG(adapterInfo("Creating a list of the mesh checkpointed fields...")); + // Add meshPhi (Face motion flux) + addMeshCheckpointField(const_cast(mesh_.phi())); - // Add meshPhi to the checkpointed fields - addMeshCheckpointField( - const_cast( - mesh_.phi())); -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Added " + mesh_.phi().name() + " in the list of checkpointed fields."); -#endif + DEBUG(adapterInfo("Added " + mesh_.phi().name() + " to the list of checkpointed fields.")); } void preciceAdapter::Adapter::setupMeshVolCheckpointing() { - DEBUG(adapterInfo("Creating a list of the mesh volume checkpointed fields...")); - // Add the V0 and the V00 to the list of checkpointed fields. - // For V0 - addVolCheckpointField( - const_cast( - mesh_.V0())); -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Added " + mesh_.V0().name() + " in the list of checkpointed fields."); -#endif - // For V00 - addVolCheckpointField( - const_cast( - mesh_.V00())); -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Added " + mesh_.V00().name() + " in the list of checkpointed fields."); -#endif - - // Also add the buffer fields. - // TODO For V0 - /* addVolCheckpointFieldBuffer - ( - const_cast - ( - mesh_.V0() - ) - ); */ -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Added " + mesh_.V0().name() + " in the list of buffer checkpointed fields."); -#endif - // TODO For V00 - /* addVolCheckpointFieldBuffer - ( - const_cast - ( - mesh_.V00() - ) - );*/ -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Added " + mesh_.V00().name() + " in the list of buffer checkpointed fields."); -#endif + // Add the mesh volumes for the current and up to two previous time steps (V, V0, V00), if available + if (volumeCheckpointState_ == VolumeCheckpointState::UNINITIALIZED) + { + // When V is available, also V0 is available (see fvMesh::movePoints, storeOldVol(V())) + addMeshVolCheckpointField(const_cast(mesh_.V())); + DEBUG(adapterInfo("Checkpoint mesh V")); + + addMeshVolCheckpointField(const_cast(mesh_.V0())); + DEBUG(adapterInfo("Checkpoint mesh V0")); + + volumeCheckpointState_ = VolumeCheckpointState::V_AND_V0_AVAILABLE; + } + else if (volumeCheckpointState_ == VolumeCheckpointState::V_AND_V0_AVAILABLE) + { + addMeshVolCheckpointField(const_cast(mesh_.V00())); + DEBUG(adapterInfo("Checkpoint mesh V00")); + + volumeCheckpointState_ = VolumeCheckpointState::ALL_AVAILABLE; + } } @@ -895,12 +917,12 @@ void preciceAdapter::Adapter::setupCheckpointing() DEBUG(adapterInfo("Adding in checkpointed fields...")); #undef doLocalCode -#define doLocalCode(GeomField) \ - /* Checkpoint registered GeomField objects */ \ - for (const word& obj : mesh_.sortedNames()) \ - { \ - addCheckpointField(mesh_.thisDb().getObjectPtr(obj)); \ - DEBUG(adapterInfo("Checkpoint " + obj + " : " #GeomField)); \ +#define doLocalCode(GeomFieldType) \ + /* Checkpoint registered GeomFieldType objects */ \ + for (const word& obj : mesh_.sortedNames()) \ + { \ + addCheckpointField(mesh_.thisDb().getObjectPtr(obj)); \ + DEBUG(adapterInfo("Checkpoint " + obj + " : " #GeomFieldType)); \ } doLocalCode(volScalarField); @@ -923,40 +945,76 @@ void preciceAdapter::Adapter::setupCheckpointing() ACCUMULATE_TIMER(timeInCheckpointingSetup_); } +void preciceAdapter::Adapter::pruneCheckpointedFields() +{ + // Check if checkpointed fields exist in OpenFOAM registry + // If not, remove them from the checkpointed fields vector -// All mesh checkpointed fields + word fieldName; + uint index; + std::vector regFields; + std::vector toRemoveIndices; -void preciceAdapter::Adapter::addMeshCheckpointField(surfaceScalarField& field) -{ - { - meshSurfaceScalarFields_.push_back(&field); - meshSurfaceScalarFieldCopies_.push_back(new surfaceScalarField(field)); - } -} +#undef doLocalCode +#define doLocalCode(GeomFieldType, GeomField_, GeomFieldCopies_) \ + regFields.clear(); \ + toRemoveIndices.clear(); \ + index = 0; \ + /* Iterate through fields in OpenFOAM registry */ \ + for (const word& fieldName : mesh_.sortedNames()) \ + { \ + regFields.push_back(fieldName); \ + } \ + /* Iterate through checkpointed fields */ \ + for (GeomFieldType * fieldObj : GeomFieldCopies_) \ + { \ + fieldName = fieldObj->name(); \ + if (std::find(regFields.begin(), regFields.end(), fieldName) == regFields.end()) \ + { \ + toRemoveIndices.push_back(index); \ + } \ + index += 1; \ + } \ + if (!toRemoveIndices.empty()) \ + { \ + /* Iterate in reverse to avoid index shifting */ \ + for (auto it = toRemoveIndices.rbegin(); it != toRemoveIndices.rend(); ++it) \ + { \ + index = *it; \ + DEBUG(adapterInfo("Removed " #GeomFieldType " : " + GeomFieldCopies_.at(index)->name() + " from the checkpointed fields list.")); \ + GeomField_.erase(GeomField_.begin() + index); \ + delete GeomFieldCopies_.at(index); \ + GeomFieldCopies_.erase(GeomFieldCopies_.begin() + index); \ + } \ + } + + doLocalCode(volScalarField, volScalarFields_, volScalarFieldCopies_); + doLocalCode(volVectorField, volVectorFields_, volVectorFieldCopies_); + doLocalCode(volTensorField, volTensorFields_, volTensorFieldCopies_); + doLocalCode(volSymmTensorField, volSymmTensorFields_, volSymmTensorFieldCopies_); + + doLocalCode(surfaceScalarField, surfaceScalarFields_, surfaceScalarFieldCopies_); + doLocalCode(surfaceVectorField, surfaceVectorFields_, surfaceVectorFieldCopies_); + doLocalCode(surfaceTensorField, surfaceTensorFields_, surfaceTensorFieldCopies_); + + doLocalCode(pointScalarField, pointScalarFields_, pointScalarFieldCopies_); + doLocalCode(pointVectorField, pointVectorFields_, pointVectorFieldCopies_); + doLocalCode(pointTensorField, pointTensorFields_, pointTensorFieldCopies_); -void preciceAdapter::Adapter::addMeshCheckpointField(surfaceVectorField& field) -{ - { - meshSurfaceVectorFields_.push_back(&field); - meshSurfaceVectorFieldCopies_.push_back(new surfaceVectorField(field)); - } +#undef doLocalCode } -void preciceAdapter::Adapter::addMeshCheckpointField(volVectorField& field) +// All mesh checkpointed fields + +void preciceAdapter::Adapter::addMeshCheckpointField(surfaceScalarField& field) { - { - meshVolVectorFields_.push_back(&field); - meshVolVectorFieldCopies_.push_back(new volVectorField(field)); - } + meshSurfaceScalarFields_.push_back(&field); + meshSurfaceScalarFieldCopies_.push_back(new surfaceScalarField(field)); } -// TODO Internal field for the V0 (volume old) and V00 (volume old-old) fields -void preciceAdapter::Adapter::addVolCheckpointField(volScalarField::Internal& field) +void preciceAdapter::Adapter::addMeshVolCheckpointField(volScalarField::Internal& field) { - { - volScalarInternalFields_.push_back(&field); - volScalarInternalFieldCopies_.push_back(new volScalarField::Internal(field)); - } + meshVolFieldCopies_.push_back(new volScalarField::Internal(field)); } @@ -1245,10 +1303,7 @@ void preciceAdapter::Adapter::readCheckpoint() // NOTE: Add here other field types to read, if needed. -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Checkpoint was read. Time = " + std::to_string(runTime_.value())); -#endif + DEBUG(adapterInfo("Checkpoint was read. Time = " + std::to_string(runTime_.value()))); ACCUMULATE_TIMER(timeInCheckpointingRead_); @@ -1332,10 +1387,7 @@ void preciceAdapter::Adapter::writeCheckpoint() } // NOTE: Add here other types to write, if needed. -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Checkpoint for time t = " + std::to_string(runTime_.value()) + " was stored."); -#endif + DEBUG(adapterInfo("Checkpoint for time t = " + std::to_string(runTime_.value()) + " was stored.")); ACCUMULATE_TIMER(timeInCheckpointingWrite_); @@ -1346,11 +1398,9 @@ void preciceAdapter::Adapter::readMeshCheckpoint() { DEBUG(adapterInfo("Reading a mesh checkpoint...")); - // TODO only the meshPhi field is here, which is a surfaceScalarField. The other fields can be removed. - // Reload all the fields of type mesh surfaceScalarField + // Only the meshPhi field is here, which is a surfaceScalarField. for (uint i = 0; i < meshSurfaceScalarFields_.size(); i++) { - // Load the volume field *(meshSurfaceScalarFields_.at(i)) == *(meshSurfaceScalarFieldCopies_.at(i)); int nOldTimes(meshSurfaceScalarFields_.at(i)->nOldTimes()); @@ -1364,44 +1414,7 @@ void preciceAdapter::Adapter::readMeshCheckpoint() } } - // Reload all the fields of type mesh surfaceVectorField - for (uint i = 0; i < meshSurfaceVectorFields_.size(); i++) - { - // Load the volume field - *(meshSurfaceVectorFields_.at(i)) == *(meshSurfaceVectorFieldCopies_.at(i)); - - int nOldTimes(meshSurfaceVectorFields_.at(i)->nOldTimes()); - if (nOldTimes >= 1) - { - meshSurfaceVectorFields_.at(i)->oldTime() == meshSurfaceVectorFieldCopies_.at(i)->oldTime(); - } - if (nOldTimes == 2) - { - meshSurfaceVectorFields_.at(i)->oldTime().oldTime() == meshSurfaceVectorFieldCopies_.at(i)->oldTime().oldTime(); - } - } - - // Reload all the fields of type mesh volVectorField - for (uint i = 0; i < meshVolVectorFields_.size(); i++) - { - // Load the volume field - *(meshVolVectorFields_.at(i)) == *(meshVolVectorFieldCopies_.at(i)); - - int nOldTimes(meshVolVectorFields_.at(i)->nOldTimes()); - if (nOldTimes >= 1) - { - meshVolVectorFields_.at(i)->oldTime() == meshVolVectorFieldCopies_.at(i)->oldTime(); - } - if (nOldTimes == 2) - { - meshVolVectorFields_.at(i)->oldTime().oldTime() == meshVolVectorFieldCopies_.at(i)->oldTime().oldTime(); - } - } - -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Mesh checkpoint was read. Time = " + std::to_string(runTime_.value())); -#endif + DEBUG(adapterInfo("Mesh checkpoint was read. Time = " + std::to_string(runTime_.value()))); return; } @@ -1410,92 +1423,92 @@ void preciceAdapter::Adapter::writeMeshCheckpoint() { DEBUG(adapterInfo("Writing a mesh checkpoint...")); - // Store all the fields of type mesh surfaceScalar + // Store all the fields of type mesh surfaceScalar (phi) for (uint i = 0; i < meshSurfaceScalarFields_.size(); i++) { *(meshSurfaceScalarFieldCopies_.at(i)) == *(meshSurfaceScalarFields_.at(i)); } - // Store all the fields of type mesh surfaceVector - for (uint i = 0; i < meshSurfaceVectorFields_.size(); i++) - { - *(meshSurfaceVectorFieldCopies_.at(i)) == *(meshSurfaceVectorFields_.at(i)); - } + DEBUG(adapterInfo("Storing mesh points...")); - // Store all the fields of type mesh volVector - for (uint i = 0; i < meshVolVectorFields_.size(); i++) - { - *(meshVolVectorFieldCopies_.at(i)) == *(meshVolVectorFields_.at(i)); - } + // Store mesh points + // swap pointers + *(meshOldPoints_) = *(meshPoints_); + *(meshPoints_) = mesh_.points(); -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Mesh checkpoint for time t = " + std::to_string(runTime_.value()) + " was stored."); -#endif + DEBUG(adapterInfo("Mesh checkpoint for time t = " + std::to_string(runTime_.value()) + " was stored.")); return; } -// TODO for the volumes of the mesh, check this part for subcycling. -void preciceAdapter::Adapter::readVolCheckpoint() +void preciceAdapter::Adapter::readMeshVolCheckpoint() { - DEBUG(adapterInfo("Reading the mesh volumes checkpoint...")); + // Reload V, V0, V00 + if (volumeCheckpointState_ == VolumeCheckpointState::V_AND_V0_AVAILABLE || volumeCheckpointState_ == VolumeCheckpointState::ALL_AVAILABLE) + { + // We do not need to reload V, as it is re-calculated in every time-step + + const_cast(mesh_.V0()) = *(meshVolFieldCopies_.at(0)); + DEBUG(adapterInfo("Read mesh volume " + meshVolFieldCopies_.at(0)->name())); - // Reload all the fields of type mesh volVectorField::Internal - for (uint i = 0; i < volScalarInternalFields_.size(); i++) + DEBUG(adapterInfo("Mesh volumes checkpoint for time t = " + std::to_string(runTime_.value()) + " were read.")); + } + if (volumeCheckpointState_ == VolumeCheckpointState::ALL_AVAILABLE) { - // Load the volume field - *(volScalarInternalFields_.at(i)) = *(volScalarInternalFieldCopies_.at(i)); - // There are no old times for the internal fields. + const_cast(mesh_.V00()) = *(meshVolFieldCopies_.at(1)); + DEBUG(adapterInfo("Read mesh volume " + meshVolFieldCopies_.at(1)->name())); } -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Mesh volumes were read. Time = " + std::to_string(runTime_.value())); -#endif - return; } -void preciceAdapter::Adapter::writeVolCheckpoint() +void preciceAdapter::Adapter::writeMeshVolCheckpoint() { - DEBUG(adapterInfo("Writing a mesh volumes checkpoint...")); + // Store V, V0, V00 + if (volumeCheckpointState_ == VolumeCheckpointState::V_AND_V0_AVAILABLE || volumeCheckpointState_ == VolumeCheckpointState::ALL_AVAILABLE) + { + *(meshVolFieldCopies_.at(0)) = const_cast(mesh_.V()); + DEBUG(adapterInfo("Write mesh volume " + meshVolFieldCopies_.at(0)->name())); + + *(meshVolFieldCopies_.at(1)) = const_cast(mesh_.V0()); + DEBUG(adapterInfo("Write mesh volume " + meshVolFieldCopies_.at(1)->name())); - // Store all the fields of type mesh volScalarField::Internal - for (uint i = 0; i < volScalarInternalFields_.size(); i++) + DEBUG(adapterInfo("Mesh volumes checkpoint for time t = " + std::to_string(runTime_.value()) + " were stored.")); + } + if (volumeCheckpointState_ == VolumeCheckpointState::ALL_AVAILABLE) { - *(volScalarInternalFieldCopies_.at(i)) = *(volScalarInternalFields_.at(i)); + *(meshVolFieldCopies_.at(2)) = const_cast(mesh_.V00()); + DEBUG(adapterInfo("Write mesh volume " + meshVolFieldCopies_.at(2)->name())); } -#ifdef ADAPTER_DEBUG_MODE - adapterInfo( - "Mesh volumes checkpoint for time t = " + std::to_string(runTime_.value()) + " was stored."); -#endif - return; } - void preciceAdapter::Adapter::end() +try { // Throw a warning if the simulation exited before the coupling was complete - if (NULL != precice_ && isCouplingOngoing()) + if (nullptr != precice_ && isCouplingOngoing()) { adapterInfo("The solver exited before the coupling was complete.", "warning"); } return; } +catch (const PreciceError& e) +{ + std::exit(EXIT_FAILURE); +} void preciceAdapter::Adapter::teardown() { // If the solver interface was not deleted before, delete it now. // Normally it should be deleted when isCouplingOngoing() becomes false. - if (NULL != precice_) + if (nullptr != precice_) { DEBUG(adapterInfo("Destroying the preCICE solver interface...")); delete precice_; - precice_ = NULL; + precice_ = nullptr; } // Delete the preCICE solver interfaces @@ -1560,27 +1573,14 @@ void preciceAdapter::Adapter::teardown() } meshSurfaceScalarFieldCopies_.clear(); - // meshSurfaceVector - for (uint i = 0; i < meshSurfaceVectorFieldCopies_.size(); i++) - { - delete meshSurfaceVectorFieldCopies_.at(i); - } - meshSurfaceVectorFieldCopies_.clear(); - - // meshVolVector - for (uint i = 0; i < meshVolVectorFieldCopies_.size(); i++) + for (uint i = 0; i < meshVolFieldCopies_.size(); i++) { - delete meshVolVectorFieldCopies_.at(i); + delete meshVolFieldCopies_.at(i); } - meshVolVectorFieldCopies_.clear(); + meshVolFieldCopies_.clear(); - // TODO for the internal volume - // volScalarInternal - for (uint i = 0; i < volScalarInternalFieldCopies_.size(); i++) - { - delete volScalarInternalFieldCopies_.at(i); - } - volScalarInternalFieldCopies_.clear(); + delete meshPoints_; + delete meshOldPoints_; // volTensorField for (uint i = 0; i < volTensorFieldCopies_.size(); i++) @@ -1616,27 +1616,35 @@ void preciceAdapter::Adapter::teardown() } // Delete the CHT module - if (NULL != CHT_) + if (nullptr != CHT_) { DEBUG(adapterInfo("Destroying the CHT module...")); delete CHT_; - CHT_ = NULL; + CHT_ = nullptr; } // Delete the FSI module - if (NULL != FSI_) + if (nullptr != FSI_) { DEBUG(adapterInfo("Destroying the FSI module...")); delete FSI_; - FSI_ = NULL; + FSI_ = nullptr; } // Delete the FF module - if (NULL != FF_) + if (nullptr != FF_) { DEBUG(adapterInfo("Destroying the FF module...")); delete FF_; - FF_ = NULL; + FF_ = nullptr; + } + + // Delete the Generic module + if (nullptr != Generic_) + { + DEBUG(adapterInfo("Destroying the Generic module...")); + delete Generic_; + Generic_ = nullptr; } // NOTE: Delete your new module here @@ -1645,6 +1653,7 @@ void preciceAdapter::Adapter::teardown() } preciceAdapter::Adapter::~Adapter() +try { teardown(); @@ -1670,3 +1679,7 @@ preciceAdapter::Adapter::~Adapter() return; } +catch (const PreciceError& e) +{ + std::exit(EXIT_FAILURE); +} diff --git a/Adapter.H b/Adapter.H index 2e9cd606..5ab6b4e6 100644 --- a/Adapter.H +++ b/Adapter.H @@ -12,6 +12,8 @@ // Fluid-Fluid module #include "FF/FF.H" +#include "module-generic/Generic.H" + // NOTE: If you want to couple a new variable, include your module's header here. // You also need to include it in the Make/files file. // In case you use additional OpenFOAM symbols, you may also need to specify @@ -24,6 +26,12 @@ // preCICE Solver Interface #include +#if PRECICE_VERSION_GREATER_EQUAL(3, 2, 0) +using PreciceError = ::precice::Error; +#else +using PreciceError = std::runtime_error; +#endif + namespace preciceAdapter { @@ -42,8 +50,8 @@ private: bool meshConnectivity; std::vector patchNames; std::vector cellSetNames; - std::vector writeData; - std::vector readData; + std::vector writeData; + std::vector readData; }; //- Configuration interfaces @@ -73,9 +81,6 @@ private: // Configuration parameters used in the Adapter - //- Remember if there were errors in the read() method - bool errorsInConfigure = false; - //- preCICE configuration file name Foam::word preciceConfigFilename_; @@ -85,6 +90,9 @@ private: //- Switch for checkpointing bool checkpointing_ = false; + //- Moving mesh checkpointing + bool meshCheckPointed_ = false; + //- Type of timestep (fixed, adjustable) bool adjustableTimestep_; @@ -102,6 +110,9 @@ private: // NOTE: Add a switch for your new module here + //- Switch to enable General module + bool genericModuleEnabled_ = false; + //- Interfaces std::vector interfaces_; @@ -112,13 +123,16 @@ private: bool preciceInitialized_ = false; //- Conjugate Heat Transfer module object - CHT::ConjugateHeatTransfer* CHT_ = NULL; + CHT::ConjugateHeatTransfer* CHT_ = nullptr; //- Fluid-Structure Interaction module object - FSI::FluidStructureInteraction* FSI_ = NULL; + FSI::FluidStructureInteraction* FSI_ = nullptr; //- Fluid-Fluid module object - FF::FluidFluid* FF_ = NULL; + FF::FluidFluid* FF_ = nullptr; + + //- Generic module object + Generic::GenericInterface* Generic_ = nullptr; // NOTE: Add here a pointer for your new module object @@ -138,45 +152,32 @@ private: //- Checkpointed time (index) Foam::label couplingIterationTimeIndex_; - //- Checkpointed mesh points - Foam::pointField meshPoints_; - Foam::pointField oldMeshPoints_; - bool meshCheckPointed = false; - // TODO: Currently unused, see storeMeshPoints(). - //- Checkpointed mesh volume - // bool oldVolsStored = false; - // Foam::volScalarField::Internal * oldVols_; - // Foam::volScalarField::Internal * oldOldVols_; - // int curTimeIndex_ = 0; + enum class VolumeCheckpointState : uint8_t + { + UNINITIALIZED = 0, + V_AND_V0_AVAILABLE = 1, + ALL_AVAILABLE = 2 + }; + + //- Volume checkpointing state + VolumeCheckpointState volumeCheckpointState_ = VolumeCheckpointState::UNINITIALIZED; // Vectors of pointers to the checkpointed mesh fields and their copies - //- Checkpointed surfaceScalarField mesh fields + //- Checkpointed mesh points, oldPoints + Foam::pointField* meshPoints_ = nullptr; + Foam::pointField* meshOldPoints_ = nullptr; + + //- Checkpointed volScalarField mesh fields (V, V0, V00) + std::vector meshVolFieldCopies_; + + //- Checkpointed surfaceScalarField mesh fields (phi) std::vector meshSurfaceScalarFields_; //- Checkpointed surfaceScalarField mesh fields (copies) std::vector meshSurfaceScalarFieldCopies_; - //- Checkpointed surfaceVectorField mesh fields - std::vector meshSurfaceVectorFields_; - - //- Checkpointed surfaceVectorField mesh fields (copies) - std::vector meshSurfaceVectorFieldCopies_; - - //- Checkpointed volVectorField mesh fields - std::vector meshVolVectorFields_; - - //- Checkpointed volVectorField mesh fields (copies) - std::vector meshVolVectorFieldCopies_; - - // TODO checkpoint for the V0 (Old volume) and V00 (Old-Old volume) fields. - //- Checkpointed volScalarField mesh fields - std::vector volScalarInternalFields_; - - //- Checkpointed volScalarField mesh fields (copies) - std::vector volScalarInternalFieldCopies_; - // Vectors of pointers to the checkpointed fields and their copies //- Checkpointed volScalarField fields @@ -245,7 +246,10 @@ private: // Configuration //- Read the adapter's configuration file - bool configFileRead(); + void configFileRead(); + + //- Helper to read a list of field configurations, which can contain either words, dictionaries, or both + void readFieldConfigs(const std::string& listName, Foam::ITstream& stream, std::vector& configs); //- Check the adapter's configuration file bool configFileCheck(const std::string adapterConfigFileName); @@ -288,12 +292,16 @@ private: //- Configure the mesh checkpointing void setupMeshCheckpointing(); - //- Configure the mesh checkpointing + //- Configure the mesh volume checkpointing void setupMeshVolCheckpointing(); //- Configure the checkpointing void setupCheckpointing(); + //- Remove checkpointed fields which are not used by OpenFOAM anymore + void pruneCheckpointedFields(); + + //- Make a copy of the runTime object void storeCheckpointTime(); @@ -307,21 +315,12 @@ private: void reloadMeshPoints(); // Add mesh checkpoint fields, depending on the type - //- Add a surfaceScalarField mesh field - void addMeshCheckpointField(surfaceScalarField& field); - - //- Add a surfaceVectorField mesh field - void addMeshCheckpointField(surfaceVectorField& field); - - //- Add a volVectorField mesh field - void addMeshCheckpointField(volVectorField& field); - // TODO V0 and V00 checkpointed field. - //- Add the V0 and V00 checkpoint fields - void addVolCheckpointField(volScalarField::Internal& field); - // void addVolCheckpointFieldBuffer(volScalarField::Internal & field); + //- Add a volScalarField::Internal mesh field (V, V0, V00) + void addMeshVolCheckpointField(volScalarField::Internal& field); - // Add checkpoint fields, depending on the type + //- Add a surfaceScalarField mesh field (phi) + void addMeshCheckpointField(Foam::surfaceScalarField& field); //- Add a volScalarField to checkpoint void addCheckpointField(volScalarField* field); @@ -368,12 +367,11 @@ private: //- Write the checkpoint - store the fields and time void writeCheckpoint(); - // TODO Probably these can be included to the mesh checkpoints. //- Read the volume checkpoint - restore the mesh volume fields - void readVolCheckpoint(); + void readMeshVolCheckpoint(); //- Write the volume checkpoint to a buffer - restore the mesh volume fields - void writeVolCheckpoint(); + void writeMeshVolCheckpoint(); //- Destroy the preCICE interface and delete the allocated // memory in a proper way. Called by the destructor. diff --git a/Allwmake b/Allwmake index 2480fe64..ec7df980 100755 --- a/Allwmake +++ b/Allwmake @@ -6,7 +6,7 @@ set -e -u # Optional: Preprocessor flags # "-DADAPTER_DEBUG_MODE" enables debug messages # "-DADAPTER_ENABLE_TIMINGS" enables time measurements -ADAPTER_PREP_FLAGS="${ADAPTER_PREP_FLAGS:-}" +ADAPTER_CFLAGS="${PRECICE_OPENFOAM_CFLAGS:-}" # Build command and options # In order to compile with multiple threads, set the environment variable WM_NCOMPPROCS, @@ -17,7 +17,7 @@ adapter_build_command(){ } # Where should the adapter be built? Default: "${FOAM_USER_LIBBIN}" -ADAPTER_TARGET_DIR="${FOAM_USER_LIBBIN:-}" +ADAPTER_TARGET_DIR="${PRECICE_OPENFOAM_TARGET_DIR:-$FOAM_USER_LIBBIN}" # More information for compatibility with OpenFOAM DOC_COMPATIBILITY="https://precice.org/adapter-openfoam-support.html" @@ -38,7 +38,7 @@ warning() { log "Building the OpenFOAM-preCICE adapter..." # Export the environment variables -export ADAPTER_PREP_FLAGS +export ADAPTER_CFLAGS export ADAPTER_TARGET_DIR # Check if pkg-config is available and get the config for preCICE @@ -90,7 +90,7 @@ fi log "" log "The adapter will be built into ${ADAPTER_TARGET_DIR}" -log "Additional preprocessor/compiler options: ${ADAPTER_PREP_FLAGS}" +log "Additional preprocessor/compiler options: ${ADAPTER_CFLAGS}" # Run wmake (build the adapter) and check the exit code log "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 90bb8b66..83d5970f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,43 @@ Read more details in the issue [#52: Releases and versioning](https://github.com +## [v1.4.0] 2026-09-14 + +### Added + +- Added an experimental `Generic` module for volume or surface coupling of arbitrary scalar and vector field types [#346](https://github.com/precice/openfoam-adapter/pull/346). + The module supports `value`, `gradient`, `surface-normal-gradient` ([#398](https://github.com/precice/openfoam-adapter/pull/398)), and `mixed` ([#400](https://github.com/precice/openfoam-adapter/pull/400)) operations and performs these without any knowledge on the physical meaning of the field. +- Added the drag force, implicit momentum coefficient, explicit momentum, and the full pressure gradient in the FF module, as coupling variables for CFD-DEM simulations [#380](https://github.com/precice/openfoam-adapter/pull/380). +- Added support for adapter configuration files that don't specify a `readData` or `writeData` entry at an interface, enabling uni-directional coupling [#348](https://github.com/precice/openfoam-adapter/pull/348). +- Added support for case-insensitive field names (e.g., both `DISPLACEMENTS` and `Displacements` are now accepted), enabling coupling to solvers that require all-uppercase names [#390](https://github.com/precice/openfoam-adapter/pull/390). +- Added exception handling for preCICE API calls [#366](https://github.com/precice/openfoam-adapter/pull/366). +- Added documentation on: + - subcycling limitations [#388](https://github.com/precice/openfoam-adapter/pull/388). + - relevant differences between OpenFOAM versions, to make porting between versions easier [#351](https://github.com/precice/openfoam-adapter/pull/351). + - running the CI locally, including hints on which OpenFOAM versions are available on which platforms [#350](https://github.com/precice/openfoam-adapter/pull/350). + - the adapter being available on the OpenFOAM Package Index (OPI) [#375](https://github.com/precice/openfoam-adapter/pull/375). + +### Fixed + +- Fixed a checkpointing issue leading to a `different dimensions` error by pruning checkpointed fields that do not (anymore) appear in the registry of objects at that timestep before reading the checkpoint [#344](https://github.com/precice/openfoam-adapter/pull/344). +- Fixed a compatibility issue with OpenFOAM v2606 affecting the `coupledVelocity` and `coupledPressure` boundary conditions [#410](https://github.com/precice/openfoam-adapter/pull/410), [#411](https://github.com/precice/openfoam-adapter/pull/411). +- Fixed an issue preventing compiling the adapter with the Intel OneAPI compiler [#358](https://github.com/precice/openfoam-adapter/pull/358). +- Fixed the wording in the error message thrown when reading stresses (was previously referring to forces) [#379](https://github.com/precice/openfoam-adapter/pull/379). + +### Changed + +- Simplified the error handling process in configuration. Configure simulations with the `errors strict` option for the adapter function object to force OpenFOAM to exit a simulation if the adapter cannot be loaded [#364](https://github.com/precice/openfoam-adapter/pull/364). +- Improved the mesh checkpointing by explicitly checkpointing mesh cell volumes (`V`, `V0`, `V00`), now supporting higher-order time derivative schemes [#369](https://github.com/precice/openfoam-adapter/pull/369), [#387](https://github.com/precice/openfoam-adapter/pull/387). +- Renamed `ADAPTER_PREP_FLAGS` to `ADAPTER_CFLAGS` in `Allwmake`, now setting it from `PRECICE_OPENFOAM_CFLAGS`, if defined in the environment. Similarly, now setting `ADAPTER_TARGET_DIR` from `PRECICE_OPENFOAM_TARGET_DIR`, if defined [#352](https://github.com/precice/openfoam-adapter/pull/352). +- Updated `preciceDict` parsing for upcoming schema changes [#354](https://github.com/precice/openfoam-adapter/pull/354). + The entries `readData` and `writeData` are now mixed lists of words and/or dictionaries. + These dictionaries allow specifying additional options, such as `solver_name`, `operation`, and `flip-normal` for each field. + Backwards compatibility with the previous `wordList` parsing is maintained. +- Updated `isLocationTypeSupported` across all modules to remove unreachable conditions and to better reflect the implementation. FSI now allows mesh connectivity when writing Displacement [#393](https://github.com/precice/openfoam-adapter/pull/393). +- Upgraded the default OpenFOAM version to v2606 [#411](https://github.com/precice/openfoam-adapter/pull/411). +- Migrate CI workflows to use the [setup-openfoam](https://github.com/marketplace/actions/setup-openfoam) action to install OpenFOAM [#338](https://github.com/precice/openfoam-adapter/pull/338). +- Improved the documentation regarding the supported fields, including their locations and support for mesh connectivity [#392](https://github.com/precice/openfoam-adapter/pull/392). + ## [v1.3.1] 2024-07-27 ### Fixed diff --git a/CHT/CHT.C b/CHT/CHT.C index e51342dd..ee189ac3 100644 --- a/CHT/CHT.C +++ b/CHT/CHT.C @@ -118,44 +118,46 @@ std::string preciceAdapter::CHT::ConjugateHeatTransfer::determineSolverType() return solverType; } -bool preciceAdapter::CHT::ConjugateHeatTransfer::addWriters(std::string dataName, Interface* interface) +bool preciceAdapter::CHT::ConjugateHeatTransfer::addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("Sink-Temperature") == 0) + if (matchingStrings(dataName, "Sink-Temperature")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new SinkTemperature(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Sink Temperature.")); } - else if (dataName.find("Temperature") == 0) + else if (matchingStrings(dataName, "Temperature")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Temperature(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Temperature.")); } - else if (dataName.find("Heat-Flux") == 0) + else if (matchingStrings(dataName, "Heat-Flux")) { if (solverType_.compare("compressible") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatFlux_Compressible(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Heat Flux for compressible solvers.")); } else if (solverType_.compare("incompressible") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatFlux_Incompressible(mesh_, nameT_, nameRho_, nameCp_, namePr_, nameAlphat_)); DEBUG(adapterInfo("Added writer: Heat Flux for incompressible solvers. ")); } else if (solverType_.compare("basic") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatFlux_Basic(mesh_, nameT_, nameKappa_)); DEBUG(adapterInfo("Added writer: Heat Flux for basic solvers. ")); } @@ -165,26 +167,26 @@ bool preciceAdapter::CHT::ConjugateHeatTransfer::addWriters(std::string dataName "error"); } } - else if (dataName.find("Heat-Transfer-Coefficient") == 0) + else if (matchingStrings(dataName, "Heat-Transfer-Coefficient")) { if (solverType_.compare("compressible") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatTransferCoefficient_Compressible(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Heat Transfer Coefficient for compressible solvers.")); } else if (solverType_.compare("incompressible") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatTransferCoefficient_Incompressible(mesh_, nameT_, nameRho_, nameCp_, namePr_, nameAlphat_)); DEBUG(adapterInfo("Added writer: Heat Transfer Coefficient for incompressible solvers. ")); } else if (solverType_.compare("basic") == 0) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new HeatTransferCoefficient_Basic(mesh_, nameT_, nameKappa_)); DEBUG(adapterInfo("Added writer: Heat Transfer Coefficient for basic solvers. ")); } @@ -208,44 +210,46 @@ bool preciceAdapter::CHT::ConjugateHeatTransfer::addWriters(std::string dataName return found; } -bool preciceAdapter::CHT::ConjugateHeatTransfer::addReaders(std::string dataName, Interface* interface) +bool preciceAdapter::CHT::ConjugateHeatTransfer::addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("Sink-Temperature") == 0) + if (matchingStrings(dataName, "Sink-Temperature")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new SinkTemperature(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Sink Temperature.")); } - else if (dataName.find("Temperature") == 0) + else if (matchingStrings(dataName, "Temperature")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Temperature(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Temperature.")); } - else if (dataName.find("Heat-Flux") == 0) + else if (matchingStrings(dataName, "Heat-Flux")) { if (solverType_.compare("compressible") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatFlux_Compressible(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Heat Flux for compressible solvers.")); } else if (solverType_.compare("incompressible") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatFlux_Incompressible(mesh_, nameT_, nameRho_, nameCp_, namePr_, nameAlphat_)); DEBUG(adapterInfo("Added reader: Heat Flux for incompressible solvers. ")); } else if (solverType_.compare("basic") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatFlux_Basic(mesh_, nameT_, nameKappa_)); DEBUG(adapterInfo("Added reader: Heat Flux for basic solvers. ")); } @@ -255,26 +259,26 @@ bool preciceAdapter::CHT::ConjugateHeatTransfer::addReaders(std::string dataName "error"); } } - else if (dataName.find("Heat-Transfer-Coefficient") == 0) + else if (matchingStrings(dataName, "Heat-Transfer-Coefficient")) { if (solverType_.compare("compressible") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatTransferCoefficient_Compressible(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Heat Transfer Coefficient for compressible solvers.")); } else if (solverType_.compare("incompressible") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatTransferCoefficient_Incompressible(mesh_, nameT_, nameRho_, nameCp_, namePr_, nameAlphat_)); DEBUG(adapterInfo("Added reader: Heat Transfer Coefficient for incompressible solvers. ")); } else if (solverType_.compare("basic") == 0) { interface->addCouplingDataReader( - dataName, + fieldConfig, new HeatTransferCoefficient_Basic(mesh_, nameT_, nameKappa_)); DEBUG(adapterInfo("Added reader: Heat Transfer Coefficient for basic solvers. ")); } diff --git a/CHT/CHT.H b/CHT/CHT.H index bbda2fca..d56faaa3 100644 --- a/CHT/CHT.H +++ b/CHT/CHT.H @@ -60,10 +60,10 @@ public: bool configure(const IOdictionary& adapterConfig); //- Add coupling data writers - bool addWriters(std::string dataName, Interface* interface); + bool addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); //- Add coupling data readers - bool addReaders(std::string dataName, Interface* interface); + bool addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); }; } diff --git a/CHT/HeatFlux.C b/CHT/HeatFlux.C index 7c63e290..cd7adced 100644 --- a/CHT/HeatFlux.C +++ b/CHT/HeatFlux.C @@ -105,14 +105,9 @@ void preciceAdapter::CHT::HeatFlux::read(double* buffer, const unsigned int dim) bool preciceAdapter::CHT::HeatFlux::isLocationTypeSupported(const bool meshConnectivity) const { - // For cases with mesh connectivity, we support: - // - face nodes, only for writing - // - face centers, only for reading - // However, since we do not distinguish between reading and writing in the code, we - // always return true and offload the handling to the user. if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); // we currently do not support meshConnectivity for volumeCenters + return (this->locationType_ == LocationType::faceNodes); } else { diff --git a/CHT/HeatTransferCoefficient.C b/CHT/HeatTransferCoefficient.C index 68778280..4797ae67 100644 --- a/CHT/HeatTransferCoefficient.C +++ b/CHT/HeatTransferCoefficient.C @@ -118,14 +118,9 @@ void preciceAdapter::CHT::HeatTransferCoefficient::read(double* buffer, const un bool preciceAdapter::CHT::HeatTransferCoefficient::isLocationTypeSupported(const bool meshConnectivity) const { - // For cases with mesh connectivity, we support: - // - face nodes, only for writing - // - face centers, only for reading - // However, since we do not distinguish between reading and writing in the code, we - // always return true and offload the handling to the user. if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); // we currently do not support meshConnectivity for volumeCenters + return (this->locationType_ == LocationType::faceNodes); } else { diff --git a/CHT/KappaEffective.C b/CHT/KappaEffective.C index 4ec5963a..3143b649 100644 --- a/CHT/KappaEffective.C +++ b/CHT/KappaEffective.C @@ -126,12 +126,7 @@ void preciceAdapter::CHT::KappaEff_Incompressible::extract(uint patchID, bool me } else { - WarningInFunction - << "The object alphat does not exist. " - << "An incompressible solver should create it explicitly " - << "(e.g. in the createFields.H)." - << "Assuming only the laminar part of the thermal diffusivity." - << nl; + adapterInfo("The object alphat does not exist. An incompressible solver should create it explicitly (e.g. in the createFields.H). Assuming only the laminar part of the thermal diffusivity.", "warning"); alphaEff = nu / Pr_.value(); } diff --git a/CHT/SinkTemperature.C b/CHT/SinkTemperature.C index 9ed18244..2b006dbe 100644 --- a/CHT/SinkTemperature.C +++ b/CHT/SinkTemperature.C @@ -95,14 +95,9 @@ void preciceAdapter::CHT::SinkTemperature::read(double* buffer, const unsigned i bool preciceAdapter::CHT::SinkTemperature::isLocationTypeSupported(const bool meshConnectivity) const { - // For cases with mesh connectivity, we support: - // - face nodes, only for writing - // - face centers, only for reading - // However, since we do not distinguish between reading and writing in the code, we - // always return true and offload the handling to the user. if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); // we currently do not support meshConnectivity for volumeCenters + return (this->locationType_ == LocationType::faceNodes); } else { diff --git a/CHT/SinkTemperature.H b/CHT/SinkTemperature.H index ab15bce7..c22e5fc9 100644 --- a/CHT/SinkTemperature.H +++ b/CHT/SinkTemperature.H @@ -26,10 +26,10 @@ public: const std::string nameT); //- Write the sink temperature values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the sink temperature values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; bool isLocationTypeSupported(const bool meshConnectivity) const final; diff --git a/CHT/Temperature.C b/CHT/Temperature.C index 3459e840..9b63ec7a 100644 --- a/CHT/Temperature.C +++ b/CHT/Temperature.C @@ -128,14 +128,9 @@ void preciceAdapter::CHT::Temperature::read(double* buffer, const unsigned int d bool preciceAdapter::CHT::Temperature::isLocationTypeSupported(const bool meshConnectivity) const { - // For cases with mesh connectivity, we support: - // - face nodes, only for writing - // - face centers, only for reading - // However, since we do not distinguish between reading and writing in the code, we - // always return true and offload the handling to the user. if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); // we currently do not support meshConnectivity for volumeCenters + return (this->locationType_ == LocationType::faceNodes); } else { diff --git a/CHT/Temperature.H b/CHT/Temperature.H index a374d64d..de1982e0 100644 --- a/CHT/Temperature.H +++ b/CHT/Temperature.H @@ -27,10 +27,10 @@ public: const std::string nameT); //- Write the temperature values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the temperature values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; bool isLocationTypeSupported(const bool meshConnectivity) const final; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b09fa65b..c11da3cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,26 @@ There are also a few additional workflows that can be triggered manually: Members of the repository can trigger these workflows in the "Actions" tab. +### Running the CI locally + +You can also run GitHub Actions locally with [act](https://github.com/nektos/act). + +List the workflows with `act --list`. To simulate a push event, run `act push`. Some workflows might work on GitHub Actions but not on act, in which case you could try running specific workflows with, for example, `act -j 'build'`. + +To trigger the custom build workflow: + +1. Switch to the directory `.github/workflows/` +2. Edit the inputs in the `build-custom.input` file. +3. [Generate a GitHub access token](https://github.com/settings/personal-access-tokens) and add it to a `.secrets` file with content `GITHUB_TOKEN=`. + This, as well as setting an `--artifact-server-path` to a local directory are needed for the `upload-artifact` action, even though build artifacts are only stored locally (see updates in a [related issue](https://github.com/nektos/act/issues/329)). +4. Start the build using: + + ```shell + act -W build-custom.yml --input-file build-custom.input --secret-file .secrets --artifact-server-path $PWD/.artifacts + ``` + +Find valid combinations of Ubuntu and OpenFOAM versions in the [OpenFOAM support page](https://precice.org/adapter-openfoam-support.html). + ## System tests For non-trivial pull requests, we also need to execute [system regression tests](https://precice.org/dev-docs-system-tests.html), diff --git a/CouplingDataUser.C b/CouplingDataUser.C index f90ef9a0..257c57b0 100644 --- a/CouplingDataUser.C +++ b/CouplingDataUser.C @@ -24,6 +24,22 @@ const std::string& preciceAdapter::CouplingDataUser::dataName() return dataName_; } +void preciceAdapter::CouplingDataUser::setFlipNormal(bool flipNormal) +{ + flipNormal_ = flipNormal; +} + +void preciceAdapter::CouplingDataUser::applyFlipNormal(precice::span dataBuffer) +{ + if (flipNormal_) + { + for (double& val : dataBuffer) + { + val *= -1.0; + } + } +} + void preciceAdapter::CouplingDataUser::setPatchIDs(std::vector patchIDs) { patchIDs_ = patchIDs; @@ -51,11 +67,19 @@ void preciceAdapter::CouplingDataUser::checkDataLocation(const bool meshConnecti else if (locationType_ == LocationType::volumeCenters) location = "volumeCenters"; - adapterInfo("\"locations = " + location + "\" is not supported for the data \"" - + getDataName() + "\". Please select a different " - "location type, a different data set or provide " - "additional connectivity information.", - "error"); + if (meshConnectivity) + { + adapterInfo("The data \"" + getDataName() + "\"" + + " does not currently support mesh connectivity (e.g., nearest-projection mapping) for location type " + + "\"" + location + "\".", + "error"); + } + else + { + adapterInfo("The data \"" + getDataName() + "\" does not support location type \"" + + location + "\". Please select a different location type.", + "error"); + } } } diff --git a/CouplingDataUser.H b/CouplingDataUser.H index 8faab32a..ed9c9261 100644 --- a/CouplingDataUser.H +++ b/CouplingDataUser.H @@ -2,6 +2,7 @@ #define COUPLINGDATAUSER_H #include "Utilities.H" +#include #include #include @@ -42,6 +43,9 @@ protected: //- location type of the interface LocationType locationType_ = LocationType::none; + //- flip normal option + bool flipNormal_ = false; + public: //- Constructor CouplingDataUser(); @@ -58,6 +62,12 @@ public: //- Get the data name const std::string& dataName(); + //- Set flip normal option + void setFlipNormal(bool flipNormal); + + //- Apply flip normal to the data buffer (multiply by -1) + void applyFlipNormal(precice::span dataBuffer); + //- Set the patch IDs that form the interface void setPatchIDs(std::vector patchIDs); diff --git a/FF/Alpha.C b/FF/Alpha.C index f11d9c2e..8f85bcf9 100644 --- a/FF/Alpha.C +++ b/FF/Alpha.C @@ -102,7 +102,7 @@ bool preciceAdapter::FF::Alpha::isLocationTypeSupported(const bool meshConnectiv { if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters); + return false; } else { diff --git a/FF/Alpha.H b/FF/Alpha.H index 7e5feb99..faa0c9bf 100644 --- a/FF/Alpha.H +++ b/FF/Alpha.H @@ -26,15 +26,15 @@ public: const std::string nameAlpha); //- Write the Alpha values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the Alpha values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; - bool isLocationTypeSupported(const bool meshConnectivity) const override; + bool isLocationTypeSupported(const bool meshConnectivity) const final; //- Get the name of the current data field - std::string getDataName() const override; + std::string getDataName() const final; }; } diff --git a/FF/AlphaGradient.C b/FF/AlphaGradient.C index 00260801..faf63a0a 100644 --- a/FF/AlphaGradient.C +++ b/FF/AlphaGradient.C @@ -64,7 +64,14 @@ void preciceAdapter::FF::AlphaGradient::read(double* buffer, const unsigned int bool preciceAdapter::FF::AlphaGradient::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters); + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters); + } } std::string preciceAdapter::FF::AlphaGradient::getDataName() const diff --git a/FF/AlphaGradient.H b/FF/AlphaGradient.H index b538f009..04905b7d 100644 --- a/FF/AlphaGradient.H +++ b/FF/AlphaGradient.H @@ -25,15 +25,15 @@ public: const std::string nameAlpha); //- Write the Alpha gradient values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the Alpha gradient values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; - bool isLocationTypeSupported(const bool meshConnectivity) const override; + bool isLocationTypeSupported(const bool meshConnectivity) const final; //- Get the name of the current data field - std::string getDataName() const override; + std::string getDataName() const final; }; } diff --git a/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.C b/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.C index 320a6ca2..8d57b60b 100644 --- a/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.C +++ b/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.C @@ -50,26 +50,11 @@ Foam::coupledPressureFvPatchField::coupledPressureFvPatchField( { if (notNull(iF) && mapper.hasUnmapped()) { - WarningInFunction - << "On field " << iF.name() << " patch " << p.name() - << " patchField " << this->type() - << " : mapper does not map all values." << nl - << " To avoid this warning fully specify the mapping in derived" - << " patch fields." << endl; + adapterInfo("On field " + iF.name() + " patch " + p.name() + " patchField " + this->type() + " : mapper does not map all values. To avoid this warning, fully specify the mapping in derived patch fields.", "warning"); } } -Foam::coupledPressureFvPatchField::coupledPressureFvPatchField( - const coupledPressureFvPatchField& ptf) -: fixedFluxExtrapolatedPressureFvPatchScalarField(ptf), - refValue_(ptf.refValue_), - refGrad_(ptf.refGrad_), - valueFraction_(ptf.valueFraction_) -{ -} - - Foam::coupledPressureFvPatchField::coupledPressureFvPatchField( const coupledPressureFvPatchField& ptf, const DimensionedField& iF) @@ -99,7 +84,7 @@ void Foam::coupledPressureFvPatchField::updateCoeffs() const scalarField& phip = phi->boundaryField()[this->patch().index()]; const Foam::volVectorField* U = &db().lookupObject(uName_); const vectorField& Up = U->boundaryField()[this->patch().index()]; - const vectorField n = this->patch().nf(); + const vectorField n = this->patch().nf().cref(); int t0 = this->patch().boundaryMesh().mesh().time().startTimeIndex(); int t = this->patch().boundaryMesh().mesh().time().timeIndex(); diff --git a/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.H b/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.H index da9f875a..a931307a 100644 --- a/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.H +++ b/FF/BoundaryConditions/coupledPressure/coupledPressureFvPatchField.H @@ -93,14 +93,7 @@ public: //- Construct as copy coupledPressureFvPatchField( - const coupledPressureFvPatchField&); - - //- Construct and return a clone - virtual tmp clone() const - { - return tmp( - new coupledPressureFvPatchField(*this)); - } + const coupledPressureFvPatchField&) = delete; //- Construct as copy setting internal field reference coupledPressureFvPatchField( diff --git a/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.C b/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.C index 1546477c..ce4ebe78 100644 --- a/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.C +++ b/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.C @@ -56,16 +56,6 @@ Foam::coupledVelocityFvPatchField::coupledVelocityFvPatchField( } -Foam::coupledVelocityFvPatchField::coupledVelocityFvPatchField( - const coupledVelocityFvPatchField& ptf) -: fvPatchField(ptf), - refValue_(ptf.refValue_), - refGrad_(ptf.refGrad_), - valueFraction_(ptf.valueFraction_) -{ -} - - Foam::coupledVelocityFvPatchField::coupledVelocityFvPatchField( const coupledVelocityFvPatchField& ptf, const DimensionedField& iF) @@ -95,7 +85,7 @@ void Foam::coupledVelocityFvPatchField::updateCoeffs() } const Foam::surfaceScalarField* phi = &db().lookupObject(phiName_); const scalarField& phip = phi->boundaryField()[this->patch().index()]; - const vectorField n = this->patch().nf(); + const vectorField n = this->patch().nf().cref(); int t0 = this->patch().boundaryMesh().mesh().time().startTimeIndex(); int t = this->patch().boundaryMesh().mesh().time().timeIndex(); diff --git a/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.H b/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.H index dadc1f2f..5ca7da0d 100644 --- a/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.H +++ b/FF/BoundaryConditions/coupledVelocity/coupledVelocityFvPatchField.H @@ -90,14 +90,7 @@ public: //- Construct as copy coupledVelocityFvPatchField( - const coupledVelocityFvPatchField&); - - //- Construct and return a clone - virtual tmp clone() const - { - return tmp( - new coupledVelocityFvPatchField(*this)); - } + const coupledVelocityFvPatchField&) = delete; //- Construct as copy setting internal field reference coupledVelocityFvPatchField( diff --git a/FF/DragForce.C b/FF/DragForce.C new file mode 100644 index 00000000..f59d3334 --- /dev/null +++ b/FF/DragForce.C @@ -0,0 +1,203 @@ +#include "DragForce.H" +#include "coupledVelocityFvPatchField.H" + +using namespace Foam; + +preciceAdapter::FF::DragForce::DragForce( + const Foam::fvMesh& mesh, + const std::string nameFd) +{ + if (mesh.foundObject(nameFd)) + { + adapterInfo("Loaded existing particle force object " + nameFd, "debug"); + F_d_ = const_cast( + &mesh.lookupObject(nameFd)); + } + else + { + adapterInfo("Creating a new drag force object " + nameFd, "debug"); + F_d_ = new volVectorField( + IOobject( + nameFd, + mesh.time().timeName(), + mesh, + IOobject::MUST_READ, + IOobject::AUTO_WRITE), + mesh); + } + dataType_ = vector; +} + +std::size_t preciceAdapter::FF::DragForce::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : F_d_->internalField()) + { + // x-dimension + buffer[bufferIndex++] = cell.x(); + + // y-dimension + buffer[bufferIndex++] = cell.y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = cell.z(); + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(F_d_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + buffer[bufferIndex++] = F_d_->internalField()[currentCell].x(); + + // y-dimension + buffer[bufferIndex++] = F_d_->internalField()[currentCell].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = F_d_->internalField()[currentCell].z(); + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + vectorField UPatch = F_d_->boundaryField()[patchID]; + + // For every cell of the patch + forAll(F_d_->boundaryFieldRef()[patchID], i) + { + // Copy the velocity into the buffer + // x-dimension + buffer[bufferIndex++] = + UPatch[i].x(); + + // y-dimension + buffer[bufferIndex++] = + UPatch[i].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = + UPatch[i].z(); + } + } + } + return bufferIndex; +} + +void preciceAdapter::FF::DragForce::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (auto& cell : F_d_->ref()) + { + // x-dimension + cell.x() = buffer[bufferIndex++]; + + // y-dimension + cell.y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + cell.z() = buffer[bufferIndex++]; + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(F_d_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + F_d_->ref()[currentCell].x() = buffer[bufferIndex++]; + + // y-dimension + F_d_->ref()[currentCell].y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + F_d_->ref()[currentCell].z() = buffer[bufferIndex++]; + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the velocity value boundary patch + vectorField* valuePatchPtr = &F_d_->boundaryFieldRef()[patchID]; + vectorField& valuePatch = *valuePatchPtr; + + // For every cell of the patch + forAll(F_d_->boundaryFieldRef()[patchID], i) + { + // Set the velocity as the buffer value + // x-dimension + valuePatch[i].x() = + buffer[bufferIndex++]; + + // y-dimension + valuePatch[i].y() = + buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + valuePatch[i].z() = + buffer[bufferIndex++]; + } + } + } +} + +bool preciceAdapter::FF::DragForce::isLocationTypeSupported(const bool meshConnectivity) const +{ + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); + } +} + +std::string preciceAdapter::FF::DragForce::getDataName() const +{ + return "DragForce"; +} diff --git a/FF/DragForce.H b/FF/DragForce.H new file mode 100644 index 00000000..84b6a0a2 --- /dev/null +++ b/FF/DragForce.H @@ -0,0 +1,51 @@ +#ifndef FF_DRAG_FORCE_H +#define FF_DRAG_FORCE_H + +#include "CouplingDataUser.H" + +#include "fvCFD.H" +#include "cellSet.H" + +namespace preciceAdapter +{ +namespace FF +{ + +//- Class that writes and reads velocity +class DragForce : public CouplingDataUser +{ + +private: + //- DragForce field + Foam::volVectorField* F_d_; + +public: + //- Constructor + DragForce( + const Foam::fvMesh& mesh, + const std::string nameFd); + + //- Write the velocity values into the buffer + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; + + //- Read the velocity values from the buffer + void read(double* buffer, const unsigned int dim) final; + + bool isLocationTypeSupported(const bool meshConnectivity) const final; + + //- Get the name of the current data field + std::string getDataName() const final; +}; + +} +} + +// NOTE: In order to couple another variable, you need to add another +// subclass of the CouplingDataUser. Take this file and DragForce.C +// as an example and look for more notes in the Adapter.C and Adapter.H. +// Essentially, the new class needs to implement a constructor (which also +// sets the dataType_) and implement the write() and read() methods. +// Then, you need to create objects of this class in the Adapter.C, +// whenever requested from the configuration file (see notes). + +#endif diff --git a/FF/ExplicitMomentum.C b/FF/ExplicitMomentum.C new file mode 100644 index 00000000..34fd4f01 --- /dev/null +++ b/FF/ExplicitMomentum.C @@ -0,0 +1,203 @@ +#include "ExplicitMomentum.H" +#include "coupledVelocityFvPatchField.H" + +using namespace Foam; + +preciceAdapter::FF::ExplicitMomentum::ExplicitMomentum( + const Foam::fvMesh& mesh, + const std::string nameExplicitMomentum) +{ + if (mesh.foundObject(nameExplicitMomentum)) + { + adapterInfo("Loaded existing explicit momentum object " + nameExplicitMomentum, "debug"); + ExplicitMomentum_ = const_cast( + &mesh.lookupObject(nameExplicitMomentum)); + } + else + { + adapterInfo("Creating a new explicit momentum object " + nameExplicitMomentum, "debug"); + ExplicitMomentum_ = new volVectorField( + IOobject( + nameExplicitMomentum, + mesh.time().timeName(), + mesh, + IOobject::MUST_READ, + IOobject::AUTO_WRITE), + mesh); + } + dataType_ = vector; +} + +std::size_t preciceAdapter::FF::ExplicitMomentum::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : ExplicitMomentum_->internalField()) + { + // x-dimension + buffer[bufferIndex++] = cell.x(); + + // y-dimension + buffer[bufferIndex++] = cell.y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = cell.z(); + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(ExplicitMomentum_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + buffer[bufferIndex++] = ExplicitMomentum_->internalField()[currentCell].x(); + + // y-dimension + buffer[bufferIndex++] = ExplicitMomentum_->internalField()[currentCell].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = ExplicitMomentum_->internalField()[currentCell].z(); + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + vectorField ExplicitMomentumPatch = ExplicitMomentum_->boundaryField()[patchID]; + + // For every cell of the patch + forAll(ExplicitMomentum_->boundaryFieldRef()[patchID], i) + { + // Copy the velocity into the buffer + // x-dimension + buffer[bufferIndex++] = + ExplicitMomentumPatch[i].x(); + + // y-dimension + buffer[bufferIndex++] = + ExplicitMomentumPatch[i].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = + ExplicitMomentumPatch[i].z(); + } + } + } + return bufferIndex; +} + +void preciceAdapter::FF::ExplicitMomentum::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (auto& cell : ExplicitMomentum_->ref()) + { + // x-dimension + cell.x() = buffer[bufferIndex++]; + + // y-dimension + cell.y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + cell.z() = buffer[bufferIndex++]; + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(ExplicitMomentum_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + ExplicitMomentum_->ref()[currentCell].x() = buffer[bufferIndex++]; + + // y-dimension + ExplicitMomentum_->ref()[currentCell].y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + ExplicitMomentum_->ref()[currentCell].z() = buffer[bufferIndex++]; + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the velocity value boundary patch + vectorField* valuePatchPtr = &ExplicitMomentum_->boundaryFieldRef()[patchID]; + vectorField& valuePatch = *valuePatchPtr; + + // For every cell of the patch + forAll(ExplicitMomentum_->boundaryFieldRef()[patchID], i) + { + // Set the velocity as the buffer value + // x-dimension + valuePatch[i].x() = + buffer[bufferIndex++]; + + // y-dimension + valuePatch[i].y() = + buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + valuePatch[i].z() = + buffer[bufferIndex++]; + } + } + } +} + +bool preciceAdapter::FF::ExplicitMomentum::isLocationTypeSupported(const bool meshConnectivity) const +{ + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); + } +} + +std::string preciceAdapter::FF::ExplicitMomentum::getDataName() const +{ + return "ExplicitMomentum"; +} diff --git a/FF/ExplicitMomentum.H b/FF/ExplicitMomentum.H new file mode 100644 index 00000000..a2524bed --- /dev/null +++ b/FF/ExplicitMomentum.H @@ -0,0 +1,51 @@ +#ifndef FF_EXPLICIT_MOMENTUM_H +#define FF_EXPLICIT_MOMENTUM_H + +#include "CouplingDataUser.H" + +#include "fvCFD.H" +#include "cellSet.H" + +namespace preciceAdapter +{ +namespace FF +{ + +//- Class that writes and reads velocity +class ExplicitMomentum : public CouplingDataUser +{ + +private: + //- ExplicitMomentum field + Foam::volVectorField* ExplicitMomentum_; + +public: + //- Constructor + ExplicitMomentum( + const Foam::fvMesh& mesh, + const std::string nameExplicitMomentum); + + //- Write the velocity values into the buffer + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; + + //- Read the velocity values from the buffer + void read(double* buffer, const unsigned int dim) final; + + bool isLocationTypeSupported(const bool meshConnectivity) const final; + + //- Get the name of the current data field + std::string getDataName() const final; +}; + +} +} + +// NOTE: In order to couple another variable, you need to add another +// subclass of the CouplingDataUser. Take this file and ExplicitMomentum.C +// as an example and look for more notes in the Adapter.C and Adapter.H. +// Essentially, the new class needs to implement a constructor (which also +// sets the dataType_) and implement the write() and read() methods. +// Then, you need to create objects of this class in the Adapter.C, +// whenever requested from the configuration file (see notes). + +#endif diff --git a/FF/FF.C b/FF/FF.C index 2c2144ee..cea8613b 100644 --- a/FF/FF.C +++ b/FF/FF.C @@ -71,6 +71,18 @@ bool preciceAdapter::FF::FluidFluid::readConfig(const IOdictionary& adapterConfi namePhi_ = FFdict.lookupOrDefault("namePhi", "phi"); DEBUG(adapterInfo(" face flux field name : " + namePhi_)); + // Read the name of the face flux field (if different) + nameDragForce_ = FFdict.lookupOrDefault("nameDragForce", "Fd"); + adapterInfo(" drag force name : " + nameDragForce_); + + // Read the name of the explicit momentum source term (if different) + nameExplicitMomentum_ = FFdict.lookupOrDefault("nameExplicitMomentum", "ExplicitMomentum"); + adapterInfo(" explicit momentum name : " + nameExplicitMomentum_); + + // Read the name of the implicit momentum source term (if different) + nameImplicitMomentum_ = FFdict.lookupOrDefault("nameImplicitMomentum", "ImplicitMomentum"); + adapterInfo(" implicit momentum name : " + nameImplicitMomentum_); + // Check whether to enable flux correction for velocity fluxCorrection_ = FFdict.lookupOrDefault("fluxCorrection", false); DEBUG(adapterInfo(" flux correction of velocity is set to : " + std::to_string(fluxCorrection_))); @@ -113,70 +125,79 @@ std::string preciceAdapter::FF::FluidFluid::determineSolverType() return solverType; } -bool preciceAdapter::FF::FluidFluid::addWriters(std::string dataName, Interface* interface) +bool preciceAdapter::FF::FluidFluid::addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("VelocityGradient") == 0) + if (matchingStrings(dataName, "VelocityGradient")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new VelocityGradient(mesh_, nameU_)); DEBUG(adapterInfo("Added writer: Velocity Gradient.")); } - else if (dataName.find("Velocity") == 0) + else if (matchingStrings(dataName, "Velocity")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Velocity(mesh_, nameU_, namePhi_, fluxCorrection_)); DEBUG(adapterInfo("Added writer: Velocity.")); } - else if (dataName.find("PressureGradient") == 0) + else if (matchingStrings(dataName, "PressureGradientFull")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, + new PressureGradientFull(mesh_, nameP_)); + DEBUG(adapterInfo("Added writer: Full Pressure Gradient.")); + } + else if (matchingStrings(dataName, "PressureGradient")) + { + interface->addCouplingDataWriter( + fieldConfig, new PressureGradient(mesh_, nameP_)); DEBUG(adapterInfo("Added writer: Pressure Gradient.")); } - else if (dataName.find("Pressure") == 0) + else if (matchingStrings(dataName, "Pressure")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Pressure(mesh_, nameP_)); DEBUG(adapterInfo("Added writer: Pressure.")); } - else if (dataName.find("FlowTemperatureGradient") == 0) + else if (matchingStrings(dataName, "FlowTemperatureGradient")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new TemperatureGradient(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Flow Temperature Gradient.")); } - else if (dataName.find("FlowTemperature") == 0) + else if (matchingStrings(dataName, "FlowTemperature")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Temperature(mesh_, nameT_)); DEBUG(adapterInfo("Added writer: Flow Temperature.")); } - else if (dataName.find("AlphaGradient") == 0) + else if (matchingStrings(dataName, "AlphaGradient")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new AlphaGradient(mesh_, nameAlpha_)); DEBUG(adapterInfo("Added writer: Alpha Gradient.")); } - else if (dataName.find("Alpha") == 0) + else if (matchingStrings(dataName, "Alpha")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Alpha(mesh_, nameAlpha_)); DEBUG(adapterInfo("Added writer: Alpha.")); } - else if (dataName.find("Phi") == 0) + else if (matchingStrings(dataName, "Phi")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Phi(mesh_, namePhi_)); DEBUG(adapterInfo("Added writer: Phi.")); } @@ -194,73 +215,96 @@ bool preciceAdapter::FF::FluidFluid::addWriters(std::string dataName, Interface* return found; } -bool preciceAdapter::FF::FluidFluid::addReaders(std::string dataName, Interface* interface) +bool preciceAdapter::FF::FluidFluid::addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("VelocityGradient") == 0) + if (matchingStrings(dataName, "VelocityGradient")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new VelocityGradient(mesh_, nameU_)); DEBUG(adapterInfo("Added reader: VelocityGradient.")); } - else if (dataName.find("Velocity") == 0) + else if (matchingStrings(dataName, "Velocity")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Velocity(mesh_, nameU_, namePhi_)); DEBUG(adapterInfo("Added reader: Velocity.")); } - else if (dataName.find("PressureGradient") == 0) + else if (matchingStrings(dataName, "PressureGradient")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new PressureGradient(mesh_, nameP_)); DEBUG(adapterInfo("Added reader: Pressure Gradient.")); } - else if (dataName.find("Pressure") == 0) + else if (matchingStrings(dataName, "Pressure")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Pressure(mesh_, nameP_)); DEBUG(adapterInfo("Added reader: Pressure.")); } - else if (dataName.find("FlowTemperatureGradient") == 0) + else if (matchingStrings(dataName, "FlowTemperatureGradient")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new TemperatureGradient(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Flow Temperature Gradient.")); } - else if (dataName.find("FlowTemperature") == 0) + else if (matchingStrings(dataName, "FlowTemperature")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Temperature(mesh_, nameT_)); DEBUG(adapterInfo("Added reader: Flow Temperature.")); } - else if (dataName.find("AlphaGradient") == 0) + else if (matchingStrings(dataName, "AlphaGradient")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new AlphaGradient(mesh_, nameAlpha_)); DEBUG(adapterInfo("Added reader: Alpha Gradient.")); } - else if (dataName.find("Alpha") == 0) + else if (matchingStrings(dataName, "Alpha")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Alpha(mesh_, nameAlpha_)); DEBUG(adapterInfo("Added reader: Alpha.")); } - else if (dataName.find("Phi") == 0) + else if (matchingStrings(dataName, "Phi")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Phi(mesh_, namePhi_)); DEBUG(adapterInfo("Added reader: Phi.")); } + else if (matchingStrings(dataName, "DragForce")) + { + interface->addCouplingDataReader( + fieldConfig, + new DragForce(mesh_, nameDragForce_)); + DEBUG(adapterInfo("Added reader: DragForce.")); + } + else if (matchingStrings(dataName, "ExplicitMomentum")) + { + interface->addCouplingDataReader( + fieldConfig, + new ExplicitMomentum(mesh_, nameExplicitMomentum_)); + DEBUG(adapterInfo("Added reader: ExplicitMomentum.")); + } + else if (matchingStrings(dataName, "ImplicitMomentum")) + { + interface->addCouplingDataReader( + fieldConfig, + new ImplicitMomentum(mesh_, nameImplicitMomentum_)); + DEBUG(adapterInfo("Added reader: ImplicitMomentum.")); + } else { found = false; diff --git a/FF/FF.H b/FF/FF.H index 77629442..d57d77e0 100644 --- a/FF/FF.H +++ b/FF/FF.H @@ -4,10 +4,14 @@ #include "Interface.H" #include "FF/Velocity.H" +#include "FF/DragForce.H" +#include "FF/ExplicitMomentum.H" +#include "FF/ImplicitMomentum.H" #include "FF/Pressure.H" #include "FF/Temperature.H" #include "FF/Alpha.H" #include "FF/PressureGradient.H" +#include "FF/PressureGradientFull.H" #include "FF/VelocityGradient.H" #include "FF/TemperatureGradient.H" #include "FF/AlphaGradient.H" @@ -47,6 +51,15 @@ protected: //- Name of the face flux field std::string namePhi_ = "phi"; + //- Name of the particle drag force + std::string nameDragForce_ = "DragForce"; + + //- Name of the explicit momentum source term + std::string nameExplicitMomentum_ = "ExplicitMomentum"; + + //- Name of the implicit momentum source term + std::string nameImplicitMomentum_ = "ImplicitMomentum"; + //- Flux correction of velocity bool fluxCorrection_ = false; @@ -64,10 +77,10 @@ public: bool configure(const IOdictionary& adapterConfig); //- Add coupling data writers - bool addWriters(std::string dataName, Interface* interface); + bool addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); //- Add coupling data readers - bool addReaders(std::string dataName, Interface* interface); + bool addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); }; } diff --git a/FF/ImplicitMomentum.C b/FF/ImplicitMomentum.C new file mode 100644 index 00000000..59a4366b --- /dev/null +++ b/FF/ImplicitMomentum.C @@ -0,0 +1,139 @@ +#include "ImplicitMomentum.H" +#include "coupledVelocityFvPatchField.H" + +using namespace Foam; + +preciceAdapter::FF::ImplicitMomentum::ImplicitMomentum( + const Foam::fvMesh& mesh, + const std::string nameImplicitMomentum) +{ + if (mesh.foundObject(nameImplicitMomentum)) + { + adapterInfo("Loaded existing implicit momentum object " + nameImplicitMomentum, "debug"); + ImplicitMomentum_ = const_cast( + &mesh.lookupObject(nameImplicitMomentum)); + } + else + { + adapterInfo("Creating a new implicit momentum object " + nameImplicitMomentum, "debug"); + ImplicitMomentum_ = new volScalarField( + IOobject( + nameImplicitMomentum, + mesh.time().timeName(), + mesh, + IOobject::MUST_READ, + IOobject::AUTO_WRITE), + mesh); + } + dataType_ = scalar; +} + +std::size_t preciceAdapter::FF::ImplicitMomentum::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : ImplicitMomentum_->internalField()) + { + buffer[bufferIndex++] = cell; + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(ImplicitMomentum_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + buffer[bufferIndex++] = ImplicitMomentum_->internalField()[currentCell]; + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + scalarField ImplicitMomentumPatch = ImplicitMomentum_->boundaryField()[patchID]; + + // For every cell of the patch + forAll(ImplicitMomentum_->boundaryFieldRef()[patchID], i) + { + // Copy the velocity into the buffer + buffer[bufferIndex++] = + ImplicitMomentumPatch[i]; + } + } + return bufferIndex; +} + +void preciceAdapter::FF::ImplicitMomentum::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (auto& cell : ImplicitMomentum_->ref()) + { + cell = buffer[bufferIndex++]; + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(ImplicitMomentum_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + ImplicitMomentum_->ref()[currentCell] = buffer[bufferIndex++]; + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the velocity value boundary patch + scalarField* valuePatchPtr = &ImplicitMomentum_->boundaryFieldRef()[patchID]; + scalarField& valuePatch = *valuePatchPtr; + + // For every cell of the patch + forAll(ImplicitMomentum_->boundaryFieldRef()[patchID], i) + { + // Set the velocity as the buffer value + valuePatch[i] = + buffer[bufferIndex++]; + } + } +} + +bool preciceAdapter::FF::ImplicitMomentum::isLocationTypeSupported(const bool meshConnectivity) const +{ + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); + } +} + +std::string preciceAdapter::FF::ImplicitMomentum::getDataName() const +{ + return "ImplicitMomentum"; +} diff --git a/FF/ImplicitMomentum.H b/FF/ImplicitMomentum.H new file mode 100644 index 00000000..42cf143a --- /dev/null +++ b/FF/ImplicitMomentum.H @@ -0,0 +1,51 @@ +#ifndef FF_IMPLICIT_MOMENTUM_H +#define FF_IMPLICIT_MOMENTUM_H + +#include "CouplingDataUser.H" + +#include "fvCFD.H" +#include "cellSet.H" + +namespace preciceAdapter +{ +namespace FF +{ + +//- Class that writes and reads velocity +class ImplicitMomentum : public CouplingDataUser +{ + +private: + //- ImplicitMomentum field + Foam::volScalarField* ImplicitMomentum_; + +public: + //- Constructor + ImplicitMomentum( + const Foam::fvMesh& mesh, + const std::string nameImplicitMomentum); + + //- Write the velocity values into the buffer + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; + + //- Read the velocity values from the buffer + void read(double* buffer, const unsigned int dim) final; + + bool isLocationTypeSupported(const bool meshConnectivity) const final; + + //- Get the name of the current data field + std::string getDataName() const final; +}; + +} +} + +// NOTE: In order to couple another variable, you need to add another +// subclass of the CouplingDataUser. Take this file and ExplDragCoeff.C +// as an example and look for more notes in the Adapter.C and Adapter.H. +// Essentially, the new class needs to implement a constructor (which also +// sets the dataType_) and implement the write() and read() methods. +// Then, you need to create objects of this class in the Adapter.C, +// whenever requested from the configuration file (see notes). + +#endif diff --git a/FF/ModuleFF.C b/FF/ModuleFF.C index 21d191ea..5adb6ad8 100644 --- a/FF/ModuleFF.C +++ b/FF/ModuleFF.C @@ -1,9 +1,13 @@ // Central inlude file to reduce the number of build targets and build time #include "FF.C" #include "Velocity.C" +#include "DragForce.C" +#include "ExplicitMomentum.C" +#include "ImplicitMomentum.C" #include "VelocityGradient.C" #include "Pressure.C" #include "PressureGradient.C" +#include "PressureGradientFull.C" #include "Temperature.C" #include "TemperatureGradient.C" #include "Alpha.C" diff --git a/FF/Phi.C b/FF/Phi.C index d2001395..75ab0a74 100644 --- a/FF/Phi.C +++ b/FF/Phi.C @@ -50,7 +50,14 @@ void preciceAdapter::FF::Phi::read(double* buffer, const unsigned int dim) bool preciceAdapter::FF::Phi::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters); + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters); + } } std::string preciceAdapter::FF::Phi::getDataName() const diff --git a/FF/Phi.H b/FF/Phi.H index 0b42c6ad..a89f6515 100644 --- a/FF/Phi.H +++ b/FF/Phi.H @@ -25,15 +25,15 @@ public: const std::string namePhi); //- Write the Phi values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the Phi values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; - bool isLocationTypeSupported(const bool meshConnectivity) const override; + bool isLocationTypeSupported(const bool meshConnectivity) const final; //- Get the name of the current data field - std::string getDataName() const override; + std::string getDataName() const final; }; } diff --git a/FF/Pressure.C b/FF/Pressure.C index 5e8b94db..3a8e7bc8 100644 --- a/FF/Pressure.C +++ b/FF/Pressure.C @@ -116,7 +116,7 @@ bool preciceAdapter::FF::Pressure::isLocationTypeSupported(const bool meshConnec { if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters); + return false; } else { diff --git a/FF/Pressure.H b/FF/Pressure.H index ecd06a02..67f82b8f 100644 --- a/FF/Pressure.H +++ b/FF/Pressure.H @@ -26,10 +26,10 @@ public: const std::string nameP); //- Write the pressure values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the pressure values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; bool isLocationTypeSupported(const bool meshConnectivity) const final; diff --git a/FF/PressureGradient.H b/FF/PressureGradient.H index 47db220e..27b8b03f 100644 --- a/FF/PressureGradient.H +++ b/FF/PressureGradient.H @@ -25,10 +25,10 @@ public: const std::string nameP); //- Write the pressure gradient values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) override; //- Read the pressure gradient values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) override; bool isLocationTypeSupported(const bool meshConnectivity) const final; diff --git a/FF/PressureGradientFull.C b/FF/PressureGradientFull.C new file mode 100644 index 00000000..2588fbb8 --- /dev/null +++ b/FF/PressureGradientFull.C @@ -0,0 +1,132 @@ +#include "PressureGradientFull.H" + +using namespace Foam; + +preciceAdapter::FF::PressureGradientFull::PressureGradientFull( + const Foam::fvMesh& mesh, + const std::string nameP) +: p_( + const_cast( + &mesh.lookupObject(nameP))), + gradP_(IOobject( + "gradP", + mesh.time().timeName(), + mesh, + IOobject::NO_READ, + IOobject::NO_WRITE), + fvc::grad(*p_)) +{ + dataType_ = vector; +} + +std::size_t preciceAdapter::FF::PressureGradientFull::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + gradP_ = fvc::grad(*p_); + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : gradP_.internalField()) + { + // x-dimension + buffer[bufferIndex++] = cell.x(); + + // y-dimension + buffer[bufferIndex++] = cell.y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = cell.z(); + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(p_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + buffer[bufferIndex++] = gradP_.internalField()[currentCell].x(); + + // y-dimension + buffer[bufferIndex++] = gradP_.internalField()[currentCell].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = gradP_.internalField()[currentCell].z(); + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // For every cell of the patch + forAll(gradP_.boundaryFieldRef()[patchID], i) + { + // Copy the velocity into the buffer + // x-dimension + buffer[bufferIndex++] = + gradP_.boundaryFieldRef()[patchID][i].x(); + + // y-dimension + buffer[bufferIndex++] = + gradP_.boundaryFieldRef()[patchID][i].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = + gradP_.boundaryFieldRef()[patchID][i].z(); + } + } + } + return bufferIndex; +} + +void preciceAdapter::FF::PressureGradientFull::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the pressure gradient boundary patch + scalarField& gradientPatch = + refCast( + p_->boundaryFieldRef()[patchID]) + .gradient(); + + // For every cell of the patch + forAll(gradientPatch, i) + { + // Set the pressure gradient as the buffer value + gradientPatch[i] = + buffer[bufferIndex++]; + } + } +} + +bool preciceAdapter::FF::PressureGradientFull::isLocationTypeSupported(const bool meshConnectivity) const +{ + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); +} + +std::string preciceAdapter::FF::PressureGradientFull::getDataName() const +{ + return "PressureGradientFull"; +} diff --git a/FF/PressureGradientFull.H b/FF/PressureGradientFull.H new file mode 100644 index 00000000..38279a1e --- /dev/null +++ b/FF/PressureGradientFull.H @@ -0,0 +1,53 @@ +#ifndef FF_FULL_PRESSURE_GRADIENT_H +#define FF_FULL_PRESSURE_GRADIENT_H + +#include "CouplingDataUser.H" + +#include "fvCFD.H" + +namespace preciceAdapter +{ +namespace FF +{ + +//- Class that writes and reads pressure gradient +class PressureGradientFull : public CouplingDataUser +{ + +private: + //- Pressure field + Foam::volScalarField* p_; + + // Gradient field + Foam::volVectorField gradP_; + +public: + //- Constructor + PressureGradientFull( + const Foam::fvMesh& mesh, + const std::string nameP); + + //- Write the pressure gradient values into the buffer + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) override; + + //- Read the pressure gradient values from the buffer + void read(double* buffer, const unsigned int dim) override; + + bool isLocationTypeSupported(const bool meshConnectivity) const final; + + //- Get the name of the current data field + std::string getDataName() const final; +}; + +} +} + +// NOTE: In order to couple another variable, you need to add another +// subclass of the CouplingDataUser. Take this file and PressureGradientFull.C +// as an example and look for more notes in the Adapter.C and Adapter.H. +// Essentially, the new class needs to implement a constructor (which also +// sets the dataType_) and implement the write() and read() methods. +// Then, you need to create objects of this class in the Adapter.C, +// whenever requested from the configuration file (see notes). + +#endif diff --git a/FF/Temperature.H b/FF/Temperature.H index da6daad9..3e6ef322 100644 --- a/FF/Temperature.H +++ b/FF/Temperature.H @@ -25,10 +25,10 @@ public: const std::string nameT); //- Write the temperature values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) override; //- Read the temperature values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) override; bool isLocationTypeSupported(const bool meshConnectivity) const override; diff --git a/FF/TemperatureGradient.H b/FF/TemperatureGradient.H index c7259a7b..58c2e4d0 100644 --- a/FF/TemperatureGradient.H +++ b/FF/TemperatureGradient.H @@ -25,15 +25,15 @@ public: const std::string nameT); //- Write the Temperature gradient values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the Temperature gradient values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; - bool isLocationTypeSupported(const bool meshConnectivity) const override; + bool isLocationTypeSupported(const bool meshConnectivity) const final; //- Get the name of the current data field - std::string getDataName() const override; + std::string getDataName() const final; }; } diff --git a/FF/Velocity.C b/FF/Velocity.C index f9ecfe61..cebaed72 100644 --- a/FF/Velocity.C +++ b/FF/Velocity.C @@ -92,7 +92,7 @@ std::size_t preciceAdapter::FF::Velocity::write(double* buffer, bool meshConnect if (fluxCorrection_) { scalarField phip = phi_->boundaryFieldRef()[patchID]; - vectorField n = U_->boundaryField()[patchID].patch().nf(); + const vectorField n = U_->boundaryField()[patchID].patch().nf().cref(); const scalarField& magS = U_->boundaryFieldRef()[patchID].patch().magSf(); UPatch = UPatch - n * (n & U_->boundaryField()[patchID]) + n * phip / magS; } @@ -209,7 +209,7 @@ bool preciceAdapter::FF::Velocity::isLocationTypeSupported(const bool meshConnec { if (meshConnectivity) { - return (this->locationType_ == LocationType::faceCenters); + return false; } else { diff --git a/FF/Velocity.H b/FF/Velocity.H index c6706821..a610ea56 100644 --- a/FF/Velocity.H +++ b/FF/Velocity.H @@ -32,10 +32,10 @@ public: bool fluxCorrection = false); //- Write the velocity values into the buffer - std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim) final; //- Read the velocity values from the buffer - void read(double* buffer, const unsigned int dim); + void read(double* buffer, const unsigned int dim) final; bool isLocationTypeSupported(const bool meshConnectivity) const final; diff --git a/FSI/Displacement.C b/FSI/Displacement.C index 4a76e26f..fd13d605 100644 --- a/FSI/Displacement.C +++ b/FSI/Displacement.C @@ -142,7 +142,15 @@ void preciceAdapter::FSI::Displacement::read(double* buffer, const unsigned int bool preciceAdapter::FSI::Displacement::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); + // Solid solver can allow connectivity for writing displacement + if (meshConnectivity) + { + return (this->locationType_ == LocationType::faceNodes); + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); + } } std::string preciceAdapter::FSI::Displacement::getDataName() const diff --git a/FSI/DisplacementDelta.C b/FSI/DisplacementDelta.C index a4bdd730..99c6ca58 100644 --- a/FSI/DisplacementDelta.C +++ b/FSI/DisplacementDelta.C @@ -41,9 +41,7 @@ std::size_t preciceAdapter::FSI::DisplacementDelta::write(double* buffer, bool m * the outer for the locations and the inner for the dimensions. * See the preCICE writeBlockVectorData() implementation. */ - FatalErrorInFunction - << "Writing displacementDeltas is not supported." - << exit(FatalError); + adapterInfo("Writing displacementDelta is not supported.", "error"); return 0; } @@ -98,7 +96,15 @@ void preciceAdapter::FSI::DisplacementDelta::read(double* buffer, const unsigned bool preciceAdapter::FSI::DisplacementDelta::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); + // Solid solver *could* allow connectivity for writing displacement + if (meshConnectivity) + { + return (this->locationType_ == LocationType::faceNodes); + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::faceNodes); + } } std::string preciceAdapter::FSI::DisplacementDelta::getDataName() const diff --git a/FSI/DisplacementDelta.H b/FSI/DisplacementDelta.H index 65066b77..b817618c 100644 --- a/FSI/DisplacementDelta.H +++ b/FSI/DisplacementDelta.H @@ -44,7 +44,7 @@ public: bool isLocationTypeSupported(const bool meshConnectivity) const final; //- Get the name of the current data field - std::string getDataName() const; + std::string getDataName() const final; //- We need to initialize the cell-based vector and the interpolation object // in case we want to use the faceCenter location for the coupling diff --git a/FSI/FSI.C b/FSI/FSI.C index 0681896f..728445a6 100644 --- a/FSI/FSI.C +++ b/FSI/FSI.C @@ -130,36 +130,38 @@ std::string preciceAdapter::FSI::FluidStructureInteraction::determineSolverType( } -bool preciceAdapter::FSI::FluidStructureInteraction::addWriters(std::string dataName, Interface* interface) +bool preciceAdapter::FSI::FluidStructureInteraction::addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("Force") == 0) + if (matchingStrings(dataName, "Force")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Force(mesh_, solverType_, nameForce_) /* TODO: Add any other arguments here */ ); DEBUG(adapterInfo("Added writer: Force.")); } - else if (dataName.find("DisplacementDelta") == 0) + else if (matchingStrings(dataName, "DisplacementDelta")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new DisplacementDelta(mesh_, namePointDisplacement_, nameCellDisplacement_)); DEBUG(adapterInfo("Added writer: DisplacementDelta.")); } - else if (dataName.find("Displacement") == 0) + else if (matchingStrings(dataName, "Displacement")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Displacement(mesh_, namePointDisplacement_, nameCellDisplacement_)); DEBUG(adapterInfo("Added writer: Displacement.")); } - else if (dataName.find("Stress") == 0) + else if (matchingStrings(dataName, "Stress")) { interface->addCouplingDataWriter( - dataName, + fieldConfig, new Stress(mesh_, solverType_) /* TODO: Add any other arguments here */ ); DEBUG(adapterInfo("Added writer: Stress.")); @@ -178,36 +180,38 @@ bool preciceAdapter::FSI::FluidStructureInteraction::addWriters(std::string data return found; } -bool preciceAdapter::FSI::FluidStructureInteraction::addReaders(std::string dataName, Interface* interface) +bool preciceAdapter::FSI::FluidStructureInteraction::addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) { + std::string dataName = fieldConfig.name; + bool found = true; // Set to false later, if needed. - if (dataName.find("Force") == 0) + if (matchingStrings(dataName, "Force")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Force(mesh_, solverType_, nameForce_) /* TODO: Add any other arguments here */ ); DEBUG(adapterInfo("Added reader: Force.")); } - else if (dataName.find("DisplacementDelta") == 0) + else if (matchingStrings(dataName, "DisplacementDelta")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new DisplacementDelta(mesh_, namePointDisplacement_, nameCellDisplacement_)); DEBUG(adapterInfo("Added reader: DisplacementDelta.")); } - else if (dataName.find("Displacement") == 0) + else if (matchingStrings(dataName, "Displacement")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Displacement(mesh_, namePointDisplacement_, nameCellDisplacement_)); DEBUG(adapterInfo("Added reader: Displacement.")); } - else if (dataName.find("Stress") == 0) + else if (matchingStrings(dataName, "Stress")) { interface->addCouplingDataReader( - dataName, + fieldConfig, new Stress(mesh_, solverType_) /* TODO: Add any other arguments here */ ); DEBUG(adapterInfo("Added reader: Stress.")); diff --git a/FSI/FSI.H b/FSI/FSI.H index 52d948fa..8e588792 100644 --- a/FSI/FSI.H +++ b/FSI/FSI.H @@ -62,10 +62,10 @@ public: bool configure(const IOdictionary& adapterConfig); //- Add coupling data writers - bool addWriters(std::string dataName, Interface* interface); + bool addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); //- Add coupling data readers - bool addReaders(std::string dataName, Interface* interface); + bool addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); //- Return the nameCellDisplacement_ field name std::string getCellDisplacementFieldName(); diff --git a/FSI/Force.C b/FSI/Force.C index d8cbad39..9bcb56d6 100644 --- a/FSI/Force.C +++ b/FSI/Force.C @@ -79,7 +79,14 @@ void preciceAdapter::FSI::Force::read(double* buffer, const unsigned int dim) bool preciceAdapter::FSI::Force::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters); + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters); + } } std::string preciceAdapter::FSI::Force::getDataName() const diff --git a/FSI/ForceBase.C b/FSI/ForceBase.C index 48297cdf..198eecaa 100644 --- a/FSI/ForceBase.C +++ b/FSI/ForceBase.C @@ -14,10 +14,7 @@ preciceAdapter::FSI::ForceBase::ForceBase( && solverType_.compare("compressible") != 0 && solverType_.compare("solid") != 0) { - FatalErrorInFunction - << "Force based calculations only support " - << "compressible, incompressible, or solid solver types." - << exit(FatalError); + adapterInfo("Force based calculations only support compressible, incompressible, or solid solver types.", "error"); } dataType_ = vector; @@ -82,9 +79,7 @@ Foam::tmp preciceAdapter::FSI::ForceBase::rho() const } else { - FatalErrorInFunction - << "Did not find the correct rho." - << exit(FatalError); + adapterInfo("Something went wrong while looking for a rho object for the force calculation.", "error"); return volScalarField::null(); } @@ -122,9 +117,7 @@ Foam::tmp preciceAdapter::FSI::ForceBase::mu() const } else { - FatalErrorInFunction - << "Did not find the correct mu." - << exit(FatalError); + adapterInfo("Something went wrong while looking for a mu object for the force calculation.", "error"); return volScalarField::null(); } @@ -169,10 +162,7 @@ std::size_t preciceAdapter::FSI::ForceBase::writeToBuffer(double* buffer, } else { - FatalErrorInFunction - << "Forces calculation does only support " - << "compressible or incompressible solver type." - << exit(FatalError); + adapterInfo("Calculating forces is only supported for solverType compressible or incompressible.", "error"); } // Viscous forces @@ -193,12 +183,12 @@ std::size_t preciceAdapter::FSI::ForceBase::writeToBuffer(double* buffer, void preciceAdapter::FSI::ForceBase::readFromBuffer(double* buffer) const { - /* TODO: Implement + /* TODO: Implement (issue https://github.com/precice/openfoam-adapter/issues/240) * We need two nested for-loops for each patch, * the outer for the locations and the inner for the dimensions. - * See the preCICE readBlockVectorData() implementation. + * Since the Force class already implements a read method, + * this only corresponds to stresses. + * In general, consider refactoring the ForceBase, Force, and Stress classes. */ - FatalErrorInFunction - << "Reading forces is not supported." - << exit(FatalError); + adapterInfo("Reading stresses is not supported.", "error"); } diff --git a/FSI/Stress.C b/FSI/Stress.C index 1194a249..ada422bb 100644 --- a/FSI/Stress.C +++ b/FSI/Stress.C @@ -33,7 +33,14 @@ void preciceAdapter::FSI::Stress::read(double* buffer, const unsigned int dim) bool preciceAdapter::FSI::Stress::isLocationTypeSupported(const bool meshConnectivity) const { - return (this->locationType_ == LocationType::faceCenters); + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters); + } } std::string preciceAdapter::FSI::Stress::getDataName() const diff --git a/Interface.C b/Interface.C index 55d59c53..b8b51891 100644 --- a/Interface.C +++ b/Interface.C @@ -1,3 +1,6 @@ +#include +#include + #include "Interface.H" #include "Utilities.H" #include "faceTriangulation.H" @@ -64,11 +67,10 @@ preciceAdapter::Interface::Interface( // Throw an error if the patch was not found if (patchID == -1) { - FatalErrorInFunction - << "ERROR: Patch '" - << patchNames.at(j) - << "' does not exist." - << exit(FatalError); + adapterInfo("Patch \"" + + patchNames.at(j) + "\" does not exist and therefore cannot be used as a coupling interface for mesh \"" + + meshName + "\". Check the system/preciceDict.", + "error"); } // Add the patch in the list @@ -432,11 +434,14 @@ void preciceAdapter::Interface::configureMesh(const fvMesh& mesh, const std::str void preciceAdapter::Interface::addCouplingDataWriter( - std::string dataName, + const FieldConfig& fieldConfig, CouplingDataUser* couplingDataWriter) { // Set the data name (from preCICE) - couplingDataWriter->setDataName(dataName); + couplingDataWriter->setDataName(fieldConfig.name); + + // Set the flip normal option + couplingDataWriter->setFlipNormal(fieldConfig.flip_normal); // Set the patchIDs of the patches that form the interface couplingDataWriter->setPatchIDs(patchIDs_); @@ -455,15 +460,39 @@ void preciceAdapter::Interface::addCouplingDataWriter( // Add the CouplingDataUser to the list of writers couplingDataWriters_.push_back(couplingDataWriter); + + // Check whether config mismatch scalar or vector data + unsigned int preciceDataDim = precice_.getDataDimensions(meshName_, fieldConfig.name); + bool isVectorDataConfig = (dim_ == preciceDataDim); // 2d or 3d vectors + bool isVectorDataAdapter = couplingDataWriter->hasVectorData(); + + // Taking gradient of a scalar field results in a vector field + bool isScalarConfiguredGradient = (!isVectorDataConfig) && (fieldConfig.operation == "gradient"); + + if (isVectorDataConfig != isVectorDataAdapter) + { + adapterInfo("Data dimension mismatch for field \"" + fieldConfig.name + "\". " + + "The field is defined as " + (isVectorDataConfig ? "vector" : "scalar") + + " data in the preCICE configuration, but the data is " + + (isVectorDataAdapter ? "vector" : "scalar") + + " in the adapter. Please check your preCICE configuration." + + (isScalarConfiguredGradient ? " If you are trying to couple the gradient of a scalar field, \ + make sure the data is defined vector in the preCICE config." + : ""), + "error"); + } } void preciceAdapter::Interface::addCouplingDataReader( - std::string dataName, + const FieldConfig& fieldConfig, preciceAdapter::CouplingDataUser* couplingDataReader) { // Set the patchIDs of the patches that form the interface - couplingDataReader->setDataName(dataName); + couplingDataReader->setDataName(fieldConfig.name); + + // Set the flip normal option + couplingDataReader->setFlipNormal(fieldConfig.flip_normal); // Add the CouplingDataUser to the list of readers couplingDataReader->setPatchIDs(patchIDs_); @@ -482,6 +511,21 @@ void preciceAdapter::Interface::addCouplingDataReader( // Add the CouplingDataUser to the list of readers couplingDataReaders_.push_back(couplingDataReader); + + // Check whether config mismatch scalar or vector data + unsigned int preciceDataDim = precice_.getDataDimensions(meshName_, fieldConfig.name); + bool isVectorDataConfig = (dim_ == preciceDataDim); // 2d or 3d vectors + bool isVectorDataAdapter = couplingDataReader->hasVectorData(); + + if (isVectorDataConfig != isVectorDataAdapter) + { + adapterInfo("Data dimension mismatch for field \"" + fieldConfig.name + "\". " + + "The field is defined as " + (isVectorDataConfig ? "vector" : "scalar") + + " data in the preCICE configuration, but the data is " + + (isVectorDataAdapter ? "vector" : "scalar") + + " in the adapter. Please check your preCICE configuration.", + "error"); + } } void preciceAdapter::Interface::createBuffer() @@ -521,10 +565,6 @@ void preciceAdapter::Interface::createBuffer() // Create the data buffer // An interface has only one data buffer, which is shared between several // CouplingDataUsers. - // TODO: Check (write tests) if this works properly when we have multiple - // scalar and vector coupling data users in an interface. With the current - // preCICE implementation, it should work as, when writing scalars, - // it should only use the first 1/3 elements of the buffer. dataBuffer_.resize(dataBufferSize); } @@ -543,12 +583,16 @@ void preciceAdapter::Interface::readCouplingData(double relativeReadTime) // We could add a sanity check here // nReadData == vertexIDs_.size() * (1 + (dim_ - 1) * static_cast(couplingDataReader->hasVectorData())); + precice::span dataSpanRead {dataBuffer_.data(), nReadData}; precice_.readData( meshName_, couplingDataReader->dataName(), vertexIDs_, relativeReadTime, - {dataBuffer_.data(), nReadData}); + dataSpanRead); + + // Apply flip normal if required + couplingDataReader->applyFlipNormal(dataSpanRead); // Read the received data from the buffer couplingDataReader->read(dataBuffer_.data(), dim_); @@ -557,10 +601,6 @@ void preciceAdapter::Interface::readCouplingData(double relativeReadTime) void preciceAdapter::Interface::writeCouplingData() { - // TODO: wrap around isWriteDataRequired - // Does the participant need to write data or is it subcycling? - // if (precice_.isWriteDataRequired(computedTimestepLength)) - // { // Make every coupling data writer write for (uint i = 0; i < couplingDataWriters_.size(); i++) { @@ -571,14 +611,18 @@ void preciceAdapter::Interface::writeCouplingData() // Write the data into the adapter's buffer auto nWrittenData = couplingDataWriter->write(dataBuffer_.data(), meshConnectivity_, dim_); + precice::span dataSpanWritten {dataBuffer_.data(), nWrittenData}; + + // Apply flip normal if required + couplingDataWriter->applyFlipNormal(dataSpanWritten); + // Make preCICE write vector or scalar data precice_.writeData( meshName_, couplingDataWriter->dataName(), vertexIDs_, - {dataBuffer_.data(), nWrittenData}); + dataSpanWritten); } - // } } preciceAdapter::Interface::~Interface() diff --git a/Interface.H b/Interface.H index 885ec9b0..6ff52a69 100644 --- a/Interface.H +++ b/Interface.H @@ -12,6 +12,15 @@ namespace preciceAdapter { +//- field options +struct FieldConfig +{ + std::string name; + std::string solver_name; + std::string operation; + bool flip_normal; +}; + class Interface { protected: @@ -82,13 +91,13 @@ public: //- Add a CouplingDataUser to read data from the interface void addCouplingDataReader( - std::string dataName, + const FieldConfig& fieldConfig, CouplingDataUser* couplingDataReader); //- Add a CouplingDataUser to write data on the interface void addCouplingDataWriter( - std::string dataName, + const FieldConfig& fieldConfig, CouplingDataUser* couplingDataWriter); //- Allocate an appropriate buffer for scalar or vector data. diff --git a/Make/files b/Make/files index 6fae86ae..37a8051c 100644 --- a/Make/files +++ b/Make/files @@ -7,6 +7,7 @@ CouplingDataUser.C CHT/ModuleCHT.C FSI/ModuleFSI.C FF/ModuleFF.C +module-generic/ModuleGeneric.C Adapter.C diff --git a/Make/options b/Make/options index 5104d7f6..af0f6292 100644 --- a/Make/options +++ b/Make/options @@ -14,7 +14,7 @@ EXE_INC = \ -I$(LIB_SRC)/triSurface/lnInclude \ $(ADAPTER_PKG_CONFIG_CFLAGS) \ -I../ \ - $(ADAPTER_PREP_FLAGS) + $(ADAPTER_CFLAGS) LIB_LIBS = \ -lfiniteVolume \ diff --git a/README.md b/README.md index caa2ba96..3ae810ed 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # OpenFOAM-preCICE adapter -[![Ask questions in the forum](https://img.shields.io/badge/community-discourse_forum-orange?link=https%3A%2F%2Fprecice.discourse.group%2F)](https://precice.discourse.group/) -[![Build with OpenFOAM v2404](https://github.com/precice/openfoam-adapter/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/precice/openfoam-adapter/actions/workflows/build.yml) +[![Ask questions in the forum](https://img.shields.io/badge/community-forum-orange?link=https%3A%2F%2Fprecice.discourse.group%2F)](https://precice.discourse.group/) +[![Release](https://img.shields.io/github/release/precice/openfoam-adapter.svg)](https://github.com/precice/openfoam-adapter/releases/latest) +[![SoftwareHeritage](https://archive.softwareheritage.org/badge/origin/https://github.com/precice/openfoam-adapter/)](https://archive.softwareheritage.org/browse/origin/?origin_url=https://github.com/precice/openfoam-adapter) +[![Cite](https://img.shields.io/badge/cite-literature_guide-d45815)](https://precice.org/fundamentals-literature-guide.html) [![Changelog](https://img.shields.io/badge/Keep%20a%20Changelog--555.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmMTVkMzAiIHZpZXdCb3g9IjAgMCAxODcgMTg1Ij48cGF0aCBkPSJNNjIgN2MtMTUgMy0yOCAxMC0zNyAyMmExMjIgMTIyIDAgMDAtMTggOTEgNzQgNzQgMCAwMDE2IDM4YzYgOSAxNCAxNSAyNCAxOGE4OSA4OSAwIDAwMjQgNCA0NSA0NSAwIDAwNiAwbDMtMSAxMy0xYTE1OCAxNTggMCAwMDU1LTE3IDYzIDYzIDAgMDAzNS01MiAzNCAzNCAwIDAwLTEtNWMtMy0xOC05LTMzLTE5LTQ3LTEyLTE3LTI0LTI4LTM4LTM3QTg1IDg1IDAgMDA2MiA3em0zMCA4YzIwIDQgMzggMTQgNTMgMzEgMTcgMTggMjYgMzcgMjkgNTh2MTJjLTMgMTctMTMgMzAtMjggMzhhMTU1IDE1NSAwIDAxLTUzIDE2bC0xMyAyaC0xYTUxIDUxIDAgMDEtMTItMWwtMTctMmMtMTMtNC0yMy0xMi0yOS0yNy01LTEyLTgtMjQtOC0zOWExMzMgMTMzIDAgMDE4LTUwYzUtMTMgMTEtMjYgMjYtMzMgMTQtNyAyOS05IDQ1LTV6TTQwIDQ1YTk0IDk0IDAgMDAtMTcgNTQgNzUgNzUgMCAwMDYgMzJjOCAxOSAyMiAzMSA0MiAzMiAyMSAyIDQxLTIgNjAtMTRhNjAgNjAgMCAwMDIxLTE5IDUzIDUzIDAgMDA5LTI5YzAtMTYtOC0zMy0yMy01MWE0NyA0NyAwIDAwLTUtNWMtMjMtMjAtNDUtMjYtNjctMTgtMTIgNC0yMCA5LTI2IDE4em0xMDggNzZhNTAgNTAgMCAwMS0yMSAyMmMtMTcgOS0zMiAxMy00OCAxMy0xMSAwLTIxLTMtMzAtOS01LTMtOS05LTEzLTE2YTgxIDgxIDAgMDEtNi0zMiA5NCA5NCAwIDAxOC0zNSA5MCA5MCAwIDAxNi0xMmwxLTJjNS05IDEzLTEzIDIzLTE2IDE2LTUgMzItMyA1MCA5IDEzIDggMjMgMjAgMzAgMzYgNyAxNSA3IDI5IDAgNDJ6bS00My03M2MtMTctOC0zMy02LTQ2IDUtMTAgOC0xNiAyMC0xOSAzN2E1NCA1NCAwIDAwNSAzNGM3IDE1IDIwIDIzIDM3IDIyIDIyLTEgMzgtOSA0OC0yNGE0MSA0MSAwIDAwOC0yNCA0MyA0MyAwIDAwLTEtMTJjLTYtMTgtMTYtMzEtMzItMzh6bS0yMyA5MWgtMWMtNyAwLTE0LTItMjEtN2EyNyAyNyAwIDAxLTEwLTEzIDU3IDU3IDAgMDEtNC0yMCA2MyA2MyAwIDAxNi0yNWM1LTEyIDEyLTE5IDI0LTIxIDktMyAxOC0yIDI3IDIgMTQgNiAyMyAxOCAyNyAzM3MtMiAzMS0xNiA0MGMtMTEgOC0yMSAxMS0zMiAxMXptMS0zNHYxNGgtOFY2OGg4djI4bDEwLTEwaDExbC0xNCAxNSAxNyAxOEg5NnoiLz48L3N2Zz4K)](https://github.com/precice/openfoam-adapter/blob/develop/CHANGELOG.md) [![License](https://img.shields.io/github/license/precice/openfoam-adapter.svg)](https://github.com/precice/openfoam-adapter/blob/master/LICENSE) +[![Build with OpenFOAM v2606](https://github.com/precice/openfoam-adapter/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/precice/openfoam-adapter/actions/workflows/build.yml) +[![OpenFOAM Package Index](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fexasim-project%2Fopi%2Frefs%2Fheads%2Fmain%2Fpkg%2Fprecice-adapter%2Fmetadata.json&query=%24.%5Bname%2Ctype%2Cversion%5D&label=opi)](https://github.com/exasim-project/opi) ## Start here diff --git a/Utilities.C b/Utilities.C index 2ed0af54..7d57360d 100644 --- a/Utilities.C +++ b/Utilities.C @@ -1,4 +1,5 @@ #include "Utilities.H" +#include using namespace Foam; @@ -29,7 +30,7 @@ void adapterInfo(const std::string message, const std::string level) // and exit the functionObject. // It will also exit the simulation, unless it // is called inside the functionObject's read(). - FatalErrorInFunction + FatalError << "\033[31m" // red color << "Error in the preCICE adapter: " << "\033[0m" // restore color @@ -38,24 +39,6 @@ void adapterInfo(const std::string message, const std::string level) << nl << exit(FatalError); } - else if (level.compare("error-deferred") == 0) - { - // Produce an warning message with red header. - // OpenFOAM degrades errors inside read() - // to warnings, stops the function object, but does - // not exit. We throw a warning which is described - // as an error, so that OpenFOAM does not exit, - // but the user still sees that this is the actual - // problem. We catch these errors and exit later. - WarningInFunction - << "\033[31m" // red color - << "Error (deferred - will exit later) in the preCICE adapter: " - << "\033[0m" // restore color - << nl - << message.c_str() - << nl - << nl; - } else if (level.compare("debug") == 0) { Pout << INFO_STR_ADAPTER @@ -82,3 +65,10 @@ void adapterInfo(const std::string message, const std::string level) return; } + +bool matchingStrings(std::string s, std::string match) +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + std::transform(match.begin(), match.end(), match.begin(), [](unsigned char c) { return std::tolower(c); }); + return s.find(match) == 0; +} diff --git a/Utilities.H b/Utilities.H index 8099d33d..8b852923 100644 --- a/Utilities.H +++ b/Utilities.H @@ -41,4 +41,7 @@ void adapterInfo(const std::string message, const std::string level = "debug"); +// Returns true if the match is found in the beginning of the string s, ignoring case +bool matchingStrings(std::string s, std::string match); + #endif diff --git a/docs/README.md b/docs/README.md index 106bb92a..a84ae539 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,9 @@ --- title: The OpenFOAM adapter permalink: adapter-openfoam-overview.html +aliases: + - /adapter-openfoam-overview.html + - /adapter-openfoam.html redirect_from: adapter-openfoam.html keywords: adapter, openfoam, cite, versions summary: An OpenFOAM function object for CHT, FSI, and fluid-fluid coupled simulations using preCICE. @@ -12,32 +15,67 @@ This preCICE adapter is a plug-in (function object) for OpenFOAM, which can work ## What can it do? -This adapter can read/write the following fields in a surface coupling setup: - -- Temperature (read + write) -- Temperature surface-normal gradient (read + write) -- Heat flux (read + write) -- Sink temperature (read + write) -- Heat transfer coefficient (read + write) -- Force (read + write) -- Stress (write) -- Displacement (read + write) -- Displacement delta (read) -- Pressure (read + write) -- Pressure surface-normal gradient (read + write) -- Velocity (read + write) -- Velocity surface-normal gradient (read + write) -- Phase fraction (alpha) (read + write) -- Phase fraction (alpha) gradient (read + write) -- Phase flux (phi) (read + write) - -In addition, the adapter supports the following fields in a volume coupling setup: - -- Temperature (write) -- Pressure (write) -- Velocity (read + write) - -All features of preCICE are supported, including implicit coupling and nearest-projection mapping. Even though OpenFOAM is 3D, this adapter can also work in the 2D mode of preCICE, defining only one layer of interface nodes (automatically). +This adapter has been demonstrated on different use cases (conjugate heat transfer, fluid-structure interaction, fluid-fluid coupling, and CFD-DEM), both in 2D (3D with one layer of cells in the z-axis) and 3D, and both for flow and solid OpenFOAM-based solvers (see [tutorials](https://precice.org/tutorials.html)). +The fields to read/write are provided by different adapter modules that one needs to [configure](https://precice.org/adapter-openfoam-config.html) and are described in the following tables. + +Legend on locations to read/write: + +- **N:** Mesh nodes (surface coupling) +- **F:** Face centers (surface coupling) +- **C:** Cell centers (volume coupling) +- ***:** Mesh connectivity supported (for, e.g., nearest-projection mapping) + +Regarding nearest-projection mapping, note that, for consistent data (e.g., displacement), it only makes sense that the writing participant provides mesh connectivity. For conservative data (e.g., forces), it only makes sense that the reading participant provides mesh connectivity. + +### Module: Conjugate heat transfer + +| Field | Write | Read | Config prefix | +| --- | --- | --- | --- | +| Heat flux | N*, F | F | `Heat-Flux` | +| Heat transfer coefficient | N*, F | F | `Heat-Transfer-Coefficient` | +| Sink temperature | N*, F | F | `Sink-Temperature` | +| Temperature | N*, F, C | F, C | `Temperature` | + +All fields are supported for both flow (compressible/incompressible) and basic (e.g., laplacianFoam) solvers. + +OpenFOAM provides temperature on face centers and cell centers. For mesh nodes (with mesh connectivity enabled), the adapter interpolates from the faces to the nodes. +Similarly, heat flux, heat transfer coefficient, and sink temperature are computed on the face centers and interpolated to the mesh nodes when mesh connectivity is enabled. + +### Module: Fluid-structure interaction + +| Field | Write | Read | Config prefix | +| --- | --- | --- | --- | +| Displacement: absolute | N*, [F*](https://github.com/precice/openfoam-adapter/issues/153) | N, F | `Displacement` | +| Displacement: relative | - | N, F | `DisplacementDelta` | +| Force | F (flow solvers) | F | `Force` | +| Stress | F (flow solvers) | - | `Stress` | + +Displacement reading and writing are supported for both flow (compressible or incompressible) and structure solvers. +For mesh nodes, the `pointDisplacement` field is used; for face centers, the `cellDisplacement` field is used. + +Only flow solvers can write forces or stresses. Solid solvers can read forces, but not stresses. + +Originally, only flow OpenFOAM solvers have been considered. Reading more fields and providing connectivity by solid solvers can still be implemented. + +### Module: Fluid-fluid coupling + +| Field | Write | Read | Config prefix | +| --- | --- | --- | --- | +| Drag force | F, C | F, C | `DragForce` | +| Momentum: explicit | F, C | F, C | `ExplicitMomentum` | +| Momentum: implicit | F, C | F, C | `ImplicitMomentum` | +| Phase flux | F | F | `Phi` | +| Volume fraction | F, C | F, C | `Alpha` | +| Phase fraction gradient | F | F | `AlphaGradient` | +| Pressure | F, C | F, C | `Pressure` | +| Pressure: full gradient | F, C | F | `PressureGradientFull` | +| Pressure: surface-normal gradient | F | F | `PressureGradient` | +| Temperature | F | F | `FlowTemperature` | +| Temperature surface-normal gradient | F | F | `FlowTemperatureGradient` | +| Velocity | F, C | F, C | `Velocity` | +| Velocity surface-normal gradient | F | F | `VelocityGradient` | + +All fields assume a flow solver, and some of these fields are mainly meant for a CFD-DEM coupling (`DragForce`, `ExplicitMomentum`, `ImplicitMomentum`). ## Try @@ -60,23 +98,32 @@ Please cite this adapter using our [reference paper in the OpenFOAM Journal](htt This project is actively maintained on [precice/openfoam-adapter](https://github.com/precice/openfoam-adapter). Current maintainers: [@MakisH](https://github.com/MakisH/) and [@DavidSCN](https://github.com/DavidSCN). This adapter was developed as part of [Gerasimos Chourdakis' master's thesis](https://mediatum.ub.tum.de/1462269) [2]. -It is based on [previous work](https://github.com/ludcila/CHT-preCICE) by Lucia Cheung ([master's thesis](https://www5.in.tum.de/pub/Cheung2016_Thesis.pdf) [3], in cooperation with [SimScale](https://www.simscale.com/)). +It is based on [previous work](https://github.com/ludcila/CHT-preCICE) by Lucia Cheung ([master's thesis](https://mediatum.ub.tum.de/1461907) [3], in cooperation with [SimScale](https://www.simscale.com/)). The fluid-structure interaction module was developed in close collaboration between Gerasimos Chourdakis and Derek Risseeuw (TU Delft), in the context of the [master's thesis of the latter](http://resolver.tudelft.nl/uuid:70beddde-e870-4c62-9a2f-8758b4e49123) [4]. We would also like to thank David Schneider (Univ. Siegen / TUM) and Maximilian Müller (TU Braunschweig) for sharing the code and experience of their similar previous work. -The fluid-fluid coupling module was added by Gerasimos Chourdakis, in the context of his dissertation. [#67](https://github.com/precice/openfoam-adapter/pull/67) +The fluid-fluid coupling module was added by Gerasimos Chourdakis, in the context of his dissertation. [#67](https://github.com/precice/openfoam-adapter/pull/67). Further contributions in this direction by Markus Mühlhäußer ([master's thesis](https://mediatum.ub.tum.de/node?id=1696254&change_language=en) [5], [related publication](https://mediatum.ub.tum.de/node?id=1732401&change_language=en) [6]). + +The volume coupling functionality was contributed by Tina Vladimirova, in the context of her [interdisciplinary project](https://mediatum.ub.tum.de/1734883) [7], based on previous work by various community contributors. The adapter is [easily extensible](https://precice.org/adapter-openfoam-extend.html). ### Related literature -[1] Chourdakis, G., Schneider, D., & Uekermann, B. (2023). OpenFOAM-preCICE: Coupling OpenFOAM with External Solvers for Multi-Physics Simulations. OpenFOAM® Journal, 3, 1–25. [DOI: 10.51560/ofj.v3.88](https://doi.org/10.51560/ofj.v3.88) - -[2] Gerasimos Chourdakis. A general OpenFOAM adapter for the coupling library preCICE. Master's thesis, Department of Informatics, Technical University of Munich, 2017. - -[3] Lucia Cheung Yau. Conjugate heat transfer with the multiphysics coupling library preCICE. Master’s thesis, Department of Informatics, Technical University of Munich, 2016. - -[4] Derek Risseeuw. Fluid Structure Interaction Modelling of Flapping Wings. Master's thesis, Faculty of Aerospace Engineering, Delft University of Technology, 2019. +1. Chourdakis, G., Schneider, D., & Uekermann, B. (2023). OpenFOAM-preCICE: Coupling OpenFOAM with External Solvers for Multi-Physics Simulations. OpenFOAM® Journal, 3, 1–25. +[DOI: 10.51560/ofj.v3.88](https://doi.org/10.51560/ofj.v3.88) +2. Gerasimos Chourdakis. A general OpenFOAM adapter for the coupling library preCICE. Master's thesis, Department of Informatics, Technical University of Munich, 2017. +[URL: MediaTUM](https://mediatum.ub.tum.de/1462269) +3. Lucia Cheung Yau. Conjugate heat transfer with the multiphysics coupling library preCICE. Master’s thesis, Department of Informatics, Technical University of Munich, 2016. +[URL: MediaTUM](https://mediatum.ub.tum.de/1461907) +4. Derek Risseeuw. Fluid Structure Interaction Modelling of Flapping Wings. Master's thesis, Faculty of Aerospace Engineering, Delft University of Technology, 2019. +[URL: TUDelft](http://resolver.tudelft.nl/uuid:70beddde-e870-4c62-9a2f-8758b4e49123) +5. Markus Mühlhäußer. Partitioned flow simulations with preCICE and OpenFOAM. Master's thesis, School of Computation, Information and Technology, Technical University of Munich, 2022. +[URL: MediaTUM](https://mediatum.ub.tum.de/node?id=1696254&change_language=en) +6. Mühlhäußer, M., Chourdakis, G., & Uekermann, B. (2023). Partitioned flow simulations with preCICE and OpenFOAM. Proceedings of the ECCOMAS Coupled Problems 2023. +[DOI: 10.23967/c.coupled.2023.014](https://doi.org/10.23967/c.coupled.2023.014) +7. Tina Vladimirova. Design, implementation, and validation of a volume coupling extension for the OpenFOAM-preCICE adapter. IDP report, School of Computation, Information and Technology, Technical University of Munich, 2023. +[URL: MediaTUM](https://mediatum.ub.tum.de/1734883) {% disclaimer %} This offering is not approved or endorsed by OpenCFD Limited, producer and distributor of the OpenFOAM software via www.openfoam.com, and owner of the OPENFOAM® and OpenCFD® trade marks. diff --git a/docs/config.md b/docs/config.md index ae0b7d4d..983fbc4a 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,6 +1,8 @@ --- title: Configure the OpenFOAM adapter permalink: adapter-openfoam-config.html +aliases: + - /adapter-openfoam-config.html keywords: adapter, openfoam, configuration, preciceDict, controlDict summary: "Write a system/preciceDict, set compatible boundary conditions, and activate the adapter in your system/controlDict." --- @@ -137,6 +139,11 @@ For fluid-structure interaction, coupled quantities can be: You will run into problems when you use `Displacement(Delta)` as write data set and execute RBF mappings in parallel. This would affect users who use OpenFOAM and the adapter as the Solid participant in order to compute solid mechanics with OpenFOAM (currently not officially supported at all). Have a look [at this issue on GitHub](https://github.com/precice/openfoam-adapter/issues/153) for details. {% endwarning %} +{% tip %} +The `writeData` and `readData` names are case-insensitive since v1.4.0. This means that both `TEMPERATURE` and `Temperature` are valid, for example. +Additionally, since earlier versions, only the beginning of the name needs to match: `Temperatures0` is valid and matched to the temperature reader/writer, for example. +{% endtip %} + ## Configuration of the OpenFOAM case A few changes are required in the configuration of an OpenFOAM case, in order to specify the interfaces and load the adapter. For some solvers, additional parameters may be needed (see "advanced configuration"). @@ -217,7 +224,7 @@ solver displacementLaplacian; #### FF -The fluid-fluid coupling module supports reading and writing `Pressure`, `Velocity`, `PressureGradient`, `VelocityGradient`, `FlowTemperature`, `FlowTemperatureGradient`, `Alpha`, `AlphaGradient` and the face flux `Phi`. +The fluid-fluid coupling module supports reading and writing `Pressure`, `Velocity`, `PressureGradient` (surface-normal gradient), `PressureGradientFull` (full pressure gradient), `VelocityGradient`, `FlowTemperature`, `FlowTemperatureGradient`, `Alpha`, `AlphaGradient`, and `Phi` (face flux). In the same module, but mainly intended for coupling to particles, there are also the fields `DragForce`, `ExplicitMomentum`, `ImplicitMomentum`. Similarly to the CHT module, you need a `fixedValue` boundary condition of the respective primary field in order to read and apply values, and a `fixedGradient` boundary condition of the respective gradient field in order to read and apply gradients. @@ -252,6 +259,79 @@ The FF module is still experimental and the boundary conditions presented here h When coupling face flux `Phi`, usually no specific boundary condition needs to be set. The coupled boundary values are therefore not persistent and may change within a timestep. +#### Generic module + +The Generic module is an experimental addition that allows coupling any field by its name. It supports volume and surface coupling of scalar and vector fields (e.g., `Velocity`, `Pressure`, `Temperature`). If multiple modules are enabled, the Generic module will handle only those fields which have not been defined in the other modules. For example, if FF is also enabled, then fields whose data name starts with `Velocity` will be handled by the FF module instead. + +The module supports the dictionary-based configuration format, which allows setting additional options for each field: + +```cpp +// File system/preciceDict + +modules ( generic ); + +interfaces +{ + Interface1 + { + mesh Fluid-Mesh; + patches (interface); + // For volume coupling specify volumeCenters + locations faceCenters; + + writeData + ( + Velocity1 + { + name Velocity; // Data name as defined in preCICE config + solver_name U; // Optional: Field name in OpenFOAM (defaults to same as 'name') + operation value; // Optional: Operation to perform on data (defaults to 'value', other options are `gradient` and `surface-normal-gradient`) + flip-normal false; // Optional: Flip the normal direction (defaults to 'false') + } + + TemperatureGradient + { + name T_Grad; + solver_name T; + operation surface-normal-gradient; // Use surface normal gradient + } + ); + }; +}; +``` + +Explicitly setting `locations volumeCenters;` in preciceDict is required for volume coupling. If omitted, it defaults to surface coupling on `faceCenters`. Volume coupling data is always assumed as values for now, i.e., cannot set volume gradient. + +When reading on the OpenFOAM side, the Generic module automatically detects the boundary condition type of the coupled patch to apply the received data. The boundary condition must be respected, therefore the data received is applied either as a `fixedValue` or `fixedGradient` boundary condition, determined by the type set in the `0/` files. + +Additionally, the Robin or mixed boundary condition is now supported for scalar surface coupling. The OpenFOAM boundary type `mixed` or `mixedCoded` must be set for the coupled patch in the `0/` files like so: + +```cpp +// File 0/T +boundaryField +{ + interface + { + type mixed; + refValue uniform 1; + refGradient uniform 1; + valueFraction uniform 0.5; + } +} +``` + +Any of the three fields `refValue`, `refGradient`, and `valueFraction` can be read from another participant (the initial values will be overwritten by the adapter). To do so, specify the `operation` for each coupling data in `preciceDict` as `ref-value`, `ref-gradient`, or `value-fraction`. Be careful when using the `value-fraction` operation, as it is not a physical quantity and must be within the range of [0, 1]. + +These three operations are only supported for the read side. To write coupling data from a mixed boundary patch, just use the standard `value` or `surface-normal-gradient` operation. The `valueFraction` field of the mixed boundary condition is not exposed by the adapter for writing. + +Please note that if you intend to use the `mixed` boundary condition for a heat transfer problem, the `valueFraction` is not the same as the heat transfer coefficient and it's not possible to explicitly set the heat transfer coefficient as a parameter. + +Limitations: + +- The operation `gradient` is currently only supported for writing scalar fields (resulting to a vector field). +- The operation `surface-normal-gradient` is currently only supported for surface scalar field writing (resulting to a scalar field). +- The operation `flip-normal` is supported for all field types across all modules, but the most relevant use case might be for surface scalar fields, in order to change the direction of, e.g., a flux. + ### Volume coupling Besides surface coupling on the domain boundaries, the OpenFOAM adapter also supports coupling overlapping domains, which can be the complete domain, or regions of it. In contrast to surface coupling, though, reading volume data (source terms) requires a few additional configuration steps compared to writing data. @@ -357,6 +437,7 @@ functions preCICE_Adapter { type preciceAdapterFunctionObject; + errors strict; // optional } } ``` @@ -365,6 +446,8 @@ This directs the solver to use the `preciceAdapterFunctionObject` function objec which is part of the `libpreciceAdapterFunctionObject.so` shared library. The name `preCICE_Adapter` can be arbitrary. It is important that the library is loaded outside the `functions` dictionary when you want to use the custom boundary conditions that we provide with the FF module. +The `errors strict` option is optional and [available since OpenFOAM v2012](https://www.openfoam.com/news/main-news/openfoam-v20-12/post-processing#post-processing-function-object-error-handling). Since the adapter is necessary to do a coupled simulation, this option instructs OpenFOAM to stop in case it faces issues with loading the adapter. For OpenFOAM versions that don't support this, remove the option. + If you are using other function objects in your simulation, add the preCICE adapter to the end of the list. The adapter will then be executed last, which is important, as the adapter also controls the end of the simulation. When the end of the simulation is detected, the adapter also triggers the `end()` method of all function objects. *** @@ -423,6 +506,18 @@ It is important to notice that the target data location is again the face center This is implemented for all CHT-related fields mapped with a `consistent` constraint, but it is not implemented for the `FSI` and `FF` modules. {% endnote %} +### Notes on subcycling + +If it is necessary to subcycle, i.e., do multiple solver time steps per coupling time window, on either the OpenFOAM side or another solver, please consider additional nuances. A good way to start is reading up on subcycling and [waveform time interpolation](https://precice.org/couple-your-code-waveform). Subcycling is allowed by preCICE and the OpenFOAM-adapter, but you may obtain different results with subcycling enabled or not depending on your problem and coupling configuration. This has to do with how the subcycling data is modelled in the time-integration of your problem, where the two independent solvers (coupling participants) with non-matching timesteps march forward in time. This depends on the time stepping scheme and the type of physical quantity that you are coupling. + +Currently, when doing subcycling in OpenFOAM on an FSI problem, the OpenFOAM-adapter will give the following warning: + +`The adapter does not fully support subcycling for FSI and instabilities may occur.` + +There is an active discussion on a [GitHub issue](https://github.com/precice/openfoam-adapter/issues/368) about why you will get different results with subcycling on the perpendicular-flap case. Until this is resolved, it is recommended to not subcycle the fluid participant in FSI simulations. + +In short, preCICE will sample only the instantaneous state of the coupled data at end of the coupling time window. If your coupled quantity is an instantaneous state of the system, such as Temperature, Velocity or Displacement, then your time-dependent problem evolution with subcycling will be modelled correctly. However, if your coupled data is a rate-quantity or something which should be conserved over time, such as Heat-Flux, Momentum-Flux or Force, then you would have to consider the modelling more carefully. For example, to exchange the time-average of Heat-Flux samples over the coupling time window. + ### Additional properties for some solvers Some solvers may not read all the material properties that are required for a coupled simulation. These parameters need to be added in the `preciceDict`. @@ -593,7 +688,7 @@ FSI ``` {% important %} -The option here defines the way the interface mesh is initialized when restarting an FSI simulation in OpenFOAM. In order to restart a coupled simulation, your solid solver needs to be capable of restarting as well. Furthermore, the two participants need to follow the same assumption for the initialization, which for OpenFOAM you can configure with this option. You can find more information about restarting coupled simulations on [Dsicourse](https://precice.discourse.group/t/how-can-i-restart-a-coupled-simulation/675). +The option here defines the way the interface mesh is initialized when restarting an FSI simulation in OpenFOAM. In order to restart a coupled simulation, your solid solver needs to be capable of restarting as well. Furthermore, the two participants need to follow the same assumption for the initialization, which for OpenFOAM you can configure with this option. You can find more information about restarting coupled simulations on [Discourse](https://precice.discourse.group/t/how-can-i-restart-a-coupled-simulation/675). {% endimportant %} #### Debugging @@ -611,3 +706,24 @@ with the adapter configuration file usually named as `precice-adapter-config.yml We moved to a OpenFOAM dictionary format in [#105](https://github.com/precice/openfoam-adapter/pull/105), to reduce the dependencies. You may also find the [tutorials #69](https://github.com/precice/tutorials/pull/69) to be a useful reference (file changes). + +## Upcoming changes to the configuration format + +We are currently working on porting the adapter configuration file to the new [adapter configuration schema](https://github.com/precice/preeco-orga/tree/main/adapter-config-schema). Since v1.4.0, `readData` and `writeData` support parsing both the new format. Additional options (`solver_name`, `operation` and `flip-normal`) can be specified in the dictionaries. For example, the data `name` as known by preCICE can be different than the `solver_name` known by OpenFOAM. However, the new options are not yet functionally supported by the current modules FF, CHT and FSI. Support for the new options is planned in the Generic module. The legacy word list format is still supported, and both formats can even be mixed: + +```cpp +readData +( + // New dictionary entry + Velocity1 + { + name Velocity; // Data name as defined in preCICE config + solver_name U; // Optional: Field name in OpenFOAM (defaults to same as 'name') + operation value; // Optional: Operation to perform on data (defaults to 'value') + flip-normal false; // Optional: Flip the normal direction (defaults to 'false') + } + + // Legacy word entry + Temperature +); +``` diff --git a/docs/extend.md b/docs/extend.md index 6d7e66b7..90184a11 100644 --- a/docs/extend.md +++ b/docs/extend.md @@ -1,6 +1,8 @@ --- title: Extend the OpenFOAM adapter permalink: adapter-openfoam-extend.html +aliases: + - /adapter-openfoam-extend.html keywords: adapter, openfoam, development, modules summary: "An overview of the OpenFOAM adapter's architecture and which parts to modify if you want to add functionality." --- @@ -28,3 +30,123 @@ section of the `Make/options`. Since the adapter is a shared library, another missing library will trigger an "undefined symbol" runtime error. See also the notes and discussion in [issue #7: Create a module for fluid-structure interaction](https://github.com/precice/openfoam-adapter/issues/7). + +## Checkpointing + +In implicit coupling, the adapter manages the coupling loop, which entails checkpointing the simulation state (fields and mesh). +The checkpoints are stored and reloaded to repeat implicit time steps. +As the coupling data is exchanged, the adapter repeats the time step until convergence. +See this diagram for an overview: + +{% mermaid %} +flowchart TD + subgraph loop["Implicit Iteration Loop for Window t"] + direction TB + + B{"OpenFOAM Solve"} + C["Adapter::execute()"] + D["Adapter::writeCouplingData()"] + E["preCICE::advance()"] + F{"preCICE: Rollback Needed?"} + G["Adapter::readCheckpoint() (Rollback)"] + I["Adapter: Read NEW Coupled Solver Data"] + J{"preCICE: Save State Needed?"} + K["Adapter::writeCheckpoint()"] + L["Adapter: Read FINAL/Next Window Data"] + end + + B --> C + C --> D + D --> E + E --> F + + F -- Yes --> G + G --> I + I --> B + + F -- No --> J + J -- Yes --> K + J -- No --> L + K --> L + + L --> M["OpenFOAM: Advance to Next Window t+dt"] + + classDef solver fill:#f9f,stroke:#333 + classDef adapter fill:#eef,stroke:#333 + classDef writeOp fill:#ffdacc,stroke:#333 + classDef precice fill:#eee,stroke:#333 + classDef decision fill:#eee,stroke:#333,stroke-dasharray:5 5 + classDef readOp fill:#cfc,stroke:#333 + + class B,M solver + class C adapter + class D,K writeOp + class E precice + class F,J decision + class G,I,L readOp + + style loop fill:none,stroke:#aaa,stroke-width:1px,stroke-dasharray:3 3 +{% endmermaid %} + +(this graph is also available as an [image file](https://github.com/precice/openfoam-adapter/blob/develop/docs/images/docs-adapter-openfoam-implicit-loop.png)) + +While the adapter already takes care of checkpointing all of the usual field and mesh data, your solver or use specific scenario might need to checkpoint more objects. +Start by looking at the `setupCheckpointing()` function in the [Adapter.C](https://github.com/precice/openfoam-adapter/blob/master/Adapter.C). + +### Checkpointed fields + +For reference, here is a list of known checkpointed and non-checkpointed fields in the OpenFOAM adapter. The adapter automatically finds and checkpoints all objects registered in the OpenFOAM object registry (`mesh_.thisDb()`) that match the following types: + +#### Volume fields + +- `volScalarField`: e.g., `p`, `T`, `k`, `omega`, `nut`, `nu`, `rho` +- `volVectorField`: e.g., `U`, `Force`, `cellDisplacement` +- `volTensorField`: e.g., `grad(U)` (if exists, relevant closed issue [#158](https://github.com/precice/openfoam-adapter/issues/158)) +- `volSymmTensorField`: e.g., `R` (Reynolds stress) + +#### Surface fields + +- `surfaceScalarField`: e.g., `phi` (flux), `faceDiffusivity` +- `surfaceVectorField`: e.g., `Uf` +- `surfaceTensorField` + +#### Point fields + +- `pointScalarField` +- `pointVectorField`: e.g., `pointDisplacement` +- `pointTensorField` + +The adapter will only create the checkpoint list once during initialization: After the first solver step, it is assumed that no new fields have been created that need to be checkpointed. + +#### Mesh data + +To handle mesh motion (ALE), the adapter explicitly checkpoints: + +- Mesh Points `mesh_.points()` and `mesh_.oldPoints()` +- Mesh Motion Flux `mesh_.phi()` +- Cell Volumes `V`, `V0`, and `V00` + +### Non-checkpointed fields + +Certain fields are purposely not checkpointed, see issue [#324](https://github.com/precice/openfoam-adapter/issues/324) and explanation in [PR #387](https://github.com/precice/openfoam-adapter/pull/387). This is either because they are re-calculated when needed "on-demand" or because they are essentially static (dictionaries assumed unchanging). + +#### Derived mesh fields + +The following fields are recalculated by OpenFOAM when `fvMesh::movePoints` is called, so the adapter does not store them: + +- `C` (Cell Centers) +- `Cf` (Face Centers) +- `Sf` (Face Area Vectors) +- `magSf` (Face Area Magnitudes) +- `delta` (Face-Center to Cell-Center distances) + +#### Dictionaries + +The adapter does not checkpoint `IOdictionary` objects, assuming they are unchanged over the time windows or handled by OpenFOAM otherwise. Examples: + +- `controlDict` +- `transportProperties` +- `turbulenceProperties` +- `fvSchemes`, `fvSolution` +- `dynamicMeshDict` +- `preciceDict` diff --git a/docs/get.md b/docs/get.md index 5460106d..eacec717 100644 --- a/docs/get.md +++ b/docs/get.md @@ -1,6 +1,8 @@ --- title: Get the OpenFOAM adapter permalink: adapter-openfoam-get.html +aliases: + - /adapter-openfoam-get.html keywords: adapter, openfoam, building summary: "Get the code from GitHub and run ./Allwmake. If this fails, look into wmake.log and ldd.log." --- @@ -17,12 +19,12 @@ To build the adapter, you need to install a few dependencies and then execute th The adapter also requires [pkg-config](https://linux.die.net/man/1/pkg-config) to [link to preCICE](https://precice.org/installation-linking.html). This is a very common dependency on Linux and is usually already installed. -You can set compile flags by either changing the `ADAPTER_PREP_FLAGS` variable in the `Allwmake` script, or directly setting the value of `ADAPTER_PREP_FLAGS` as an environment variable. -To do so, `export ADAPTER_PREP_FLAGS="-D -D"` before compiling the adapter. +The `Allwmake` script uses two environment variables: -Adding the `-DADAPTER_DEBUG_MODE` flag to the `ADAPTER_PREP_FLAGS` activates additional debug messages. You may also change the target directory or specify the number of threads to use for the compilation. See the comments in `Allwmake` for more. - -Adding the `-DADAPTER_ENABLE_TIMINGS` flag to the `ADAPTER_PREP_FLAGS` activates time measurements for several regions of the adapter, printed at the end of the simulation output (available since v1.2.0). +* `PRECICE_OPENFOAM_TARGET_DIR`: Where the `libpreciceAdapterFunctionObject.so` will be installed. Defaults to `FOAM_USER_LIBBIN`. +* `PRECICE_OPENFOAM_CFLAGS`: Additional flags passed to wmake and used by the preprocessor/compiler. Defaults to an empty string. + * Adding `-DADAPTER_DEBUG_MODE` flag activates additional debug messages. + * Adding the `-DADAPTER_ENABLE_TIMINGS` flag activates time measurements for several regions of the adapter, printed at the end of the simulation output. If you are building the adapter often, you may want to build it in parallel. You can set the environment variable `WM_NCOMPPROCS` to the number of parallel threads you want WMake to use. diff --git a/docs/images/docs-adapter-openfoam-implicit-loop.png b/docs/images/docs-adapter-openfoam-implicit-loop.png new file mode 100644 index 00000000..cce3bd02 Binary files /dev/null and b/docs/images/docs-adapter-openfoam-implicit-loop.png differ diff --git a/docs/openfoam-support.md b/docs/openfoam-support.md index dbad735a..53e733ae 100644 --- a/docs/openfoam-support.md +++ b/docs/openfoam-support.md @@ -1,54 +1,267 @@ --- title: OpenFOAM support permalink: adapter-openfoam-support.html +aliases: + - /adapter-openfoam-support.html keywords: adapter, openfoam, support, versions summary: Recent OpenFOAM.com versions work out-of-the-box. Recent OpenFOAM.org versions are also supported, but you will need a version-specific branch. --- ## How to get OpenFOAM -The easiest way to start is to get binary packages for your Linux distribution. For example, to [get OpenFOAM v2406 on Ubuntu](https://develop.openfoam.com/Development/openfoam/-/wikis/precompiled/debian#precompiled-packages-debianubuntu): +The easiest way to start is to get binary packages for your Linux distribution. For example, to [get OpenFOAM v2606 on Ubuntu](https://gitlab.com/openfoam/core/openfoam/-/wikis/precompiled/debian): ```bash # Add the signing key, add the repository, update: wget -q -O - https://dl.openfoam.com/add-debian-repo.sh | sudo bash -# Install OpenFOAM v2406: -sudo apt-get install openfoam2406-dev +# Install OpenFOAM v2606: +sudo apt-get install openfoam2606-dev ``` -As these steps change your `.profile`, you need to log out and in again to make OpenFOAM fully discoverable. +Follow the instructions at the end to [make OpenFOAM discoverable](https://gitlab.com/openfoam/core/openfoam/-/wikis/running). +A common approach is to add the following to your `~/.bashrc`: + +```bash + echo "source /usr/lib/openfoam/openfoam2606/etc/bashrc" >> ~/.bashrc + source ~/.bashrc +``` + +## Supported OpenFOAM solvers + +We support mainstream OpenFOAM solvers such as pimpleFoam and solids4Foam for FSI, buoyantPimpleFoam, buoyantSimpleFoam, and laplacianFoam for CHT, or pimpleFoam and sonicLiquidFoam for FF. Our community has, additionally, tried the adapter with multiple different solvers that support function objects. ## Supported OpenFOAM versions OpenFOAM is a project with long history and many forks, of which we try to support as many as possible. Since several HPC systems only provide older versions, we try to also support a wide range of versions. +{% warning %} +Currently, and since v1.3.0 of the adapter (first one for preCICE v3), only the default variant of the adapter for the OpenCFD / ESI is available. We are working on supporting further versions again. +{% endwarning %} + We provide version-specific [release archives](https://github.com/precice/openfoam-adapter/releases/latest) and respective Git branches for: - OpenCFD / ESI (openfoam.com) - main focus: - - [OpenFOAM v1812-v2406](https://github.com/precice/openfoam-adapter) or newer - - OpenFOAM v2212 and newer is only supported since v1.2.2 of the adapter. + - [OpenFOAM v1812-v2606](https://github.com/precice/openfoam-adapter) or newer + - See [compatibility changes and from v2512 to v2606](https://github.com/precice/openfoam-adapter/pull/410) + - See [expected results differences in FSI cases (mesh motion) with OpenFOAM v2606](https://github.com/precice/openfoam-adapter/pull/411) - [OpenFOAM v1612-v1806](https://github.com/precice/openfoam-adapter/tree/OpenFOAMv1806) (not tested) - OpenFOAM Foundation (openfoam.org) - secondary, consider experimental: - [OpenFOAM 10](https://github.com/precice/openfoam-adapter/tree/OpenFOAM10) - - Several [changes to the tutorials](https://github.com/precice/tutorials/tree/OpenFOAM10) are also needed, read the [discussion](https://github.com/precice/tutorials/pull/283). - - Same limitations as for OpenFOAM 9. - [OpenFOAM 9](https://github.com/precice/openfoam-adapter/tree/OpenFOAM9) - - Rename `solver` to `motionSolver` in `constant/dynamicMeshDict`. - - Modify also `residualControl` to `outerCorrectorResidualControl` in `system/fvSolution`. - - Limitations in adjustable time step size ([#261](https://github.com/precice/openfoam-adapter/issues/261)). - [OpenFOAM 8](https://github.com/precice/openfoam-adapter/tree/OpenFOAM8) - [OpenFOAM 7](https://github.com/precice/openfoam-adapter/tree/OpenFOAM7) - [OpenFOAM 6](https://github.com/precice/openfoam-adapter/tree/OpenFOAM6) - - Modify also `residualControl` to `outerResidualControl` in `system/fvSolution`. - [OpenFOAM 5.x](https://github.com/precice/openfoam-adapter/tree/OpenFOAM5) - [OpenFOAM 4.0/4.1](https://github.com/precice/openfoam-adapter/tree/OpenFOAM4) (not tested) Known not supported versions: OpenFOAM v1606+ or older, OpenFOAM 3 or older, foam-extend (any version). -## Supported OpenFOAM solvers +## Availability of OpenFOAM packages + +We use the Ubuntu repositories of each OpenFOAM vendor for testing. Quick reference for maintainers: + +- OpenCFD / ESI (openfoam.com): + - [PPA repository](https://dl.openfoam.com/repos/deb/) + - More details on the [OpenFOAM documentation](https://gitlab.com/openfoam/core/openfoam/-/wikis/precompiled). +- OpenFOAM Foundation (openfoam.org): + - [PPA repository](https://dl.openfoam.org/ubuntu/) + - More details on the [OpenFOAM documentation](https://dl.openfoam.org/). +- foam-extend: + - The project distributes pre-built Debian packages as release artifacts on [SourceForge](https://sourceforge.net/projects/foam-extend/files/). + +The following table gives an overview, including versions that we do not support, but which might be useful for future or historical reference. + +| version | 24.04 | 22.04 | 20.04 | 18.04 | +| --- | --- | --- | --- | --- | +| (com) v2606 | x | x | x | x | +| (com) v2512 | x | x | x | x | +| (com) v2506 | x | x | x | x | +| (com) v2412 | x | x | x | x | +| (com) v2406 | x | x | x | x | +| (com) v2312 | x | x | x | x | +| (com) v2306 | x | x | x | x | +| (com) v2212 | x | x | x | x | +| (com) v2206 | - | x | x | x | +| (com) v2112 | - | x | x | x | +| (com) v2106 | - | x | x | x | +| (com) v2012 | - | x | x | x | +| (com) v2006 | - | x | x | x | +| (com) v1912 | - | x | x | x | +| (com) older | - | - | - | - | +| (org) 12 | x | x | x | - | +| (org) 11 | x | x | x | - | +| (org) 10 | - | x | x | x | +| (org) 9 | - | x | x | x | +| (org) 8 | - | - | x | x | +| (org) 7 | - | - | x | x | +| (org) 6 | - | - | - | x | +| (org) 5 | - | - | - | - | +| (org) 4 | - | - | - | - | +| (ext) 5.0 | - | x | x | - | +| (ext) 4.1 | - | - | - | x | + +## Differences between OpenFOAM versions + +We take into account the following relevant differences between OpenFOAM versions, compared to the latest OpenCFD version. This list is important for maintainers of version-specific ports of the adapter. + +### OpenFOAM v1612-v1806 + +- **clockValue:** The `clockValue.H` header is not available (used in `preciceAdapterFunctionObject.H`). + - Disable the timers feature, controlled by the `ADAPTER_ENABLE_TIMINGS` preprocessor variable (set in `Allwmake`). +- **dictionary access:** In dictionaries (essentially: in the `preciceDict`), some methods are not available. These are mainly used in the configuration step in `Adapter.C`, but also in `ForceBase.C`. + - Replace `preciceDict.get("...")` with `static_cast(preciceDict.lookup("...")`. + - Replace `preciceDict.get("...")` with `static_cast(preciceDict.lookup("..."))`. + - Replace `preciceDict.getOrDefault` with `preciceDict.lookupOrDefault`. + - Replace `preciceDict.get("...")` with `static_cast(preciceDict.lookup("..."))`. + - Replace `preciceDict.findDict("...")` with `preciceDict.subDictPtr("...")`. +- **Db access:** In the macro that deals with adding checkpointed fields, some methods are not available. + - Replace `mesh_.sortedNames()` with `mesh_.lookupClass().sortedToc()`. + - Replace `mesh_.thisDb().getObjectPtr(obj)` with `&(const_cast(mesh_.thisDb().lookupObject(obj)))`. +- **Pointers:** `std::unique_ptr` is not available. + - Replace `std::unique_ptr` with `Foam::autoPtr`. + - Replace calls to pointer `.reset()`, such as `ForceOwning_.reset(new volVectorField(`, with `ForceOwning_ = new volVectorField(`. Adjust the number of closing parentheses. + - Replace calls to pointer `.get();`, such as `ForceOwning_.get()`, with `ForceOwning_.ptr()`. + +### OpenFOAM 4 and 5 + +OpenFOAM 4 was [released](https://openfoam.org/release/4/) in June 2016 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-4.x), [Doxygen](https://cpp.openfoam.org/v4)). + +OpenFOAM 5 was [released](https://openfoam.org/release/5/) in July 2017 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-5.x), [Doxygen](https://cpp.openfoam.org/v5)). + +Same as in OpenFOAM v1612-v1806. + +### OpenFOAM 6 + +OpenFOAM 6 was [released](https://openfoam.org/release/6/) in July 2018 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-6), [Doxygen](https://cpp.openfoam.org/v6)). Compared to OpenFOAM 5: + +- **adjustTimeStep:** In the function objects interface, `adjustTimeStep()` was renamed to `setTimeStep()`. + - Replace `adjustTimeStep()` with `setTimeStep()`, both for the function object and the adapter members. +- **setDeltaT:** The `Time` method `setDeltaT(..., false)` was replaced by `setDeltaTNoAdjust(...)`. + - Replace `setDeltaT(timestepSolver_, false)` with `setDeltaTNoAdjust(timestepSolver_)`. +- **Case files:** + - In `system/fvSolution`, replace `residualControl` with `outerResidualControl`. + +Related work in the adapter: [PR #33](https://github.com/precice/openfoam-adapter/pull/33). + +### OpenFOAM 7 + +OpenFOAM 7 was [released](https://openfoam.org/release/7/) in July 2019 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-7), [Doxygen](https://cpp.openfoam.org/v7)). Compared to OpenFOAM 6: + +- **writeEntry:** The interface for `writeEntry` was changed. + - Where in the latest adapter (`FF/BoundaryConditions`) you see `this->writeEntry("value", os)`, replace it with `writeEntry(os, "value", *this)`. + - Similarly, where `this->valueFraction().writeEntry("valueFraction", os);`, replace it with `writeEntry(os, "valueFraction", this->valueFraction())`. Similarly for `this->refValue().writeEntry("refValue", os)`. +- **fileName:** `fileName::DIRECTORY` was renamed to `fileType::directory`. + - Replace this in the `Adapter.C`. + +Related work in the adapter: [PR #91](https://github.com/precice/openfoam-adapter/pull/91). + +### OpenFOAM 8 + +OpenFOAM 8 was [released](https://openfoam.org/release/8/) in July 2020 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-8), [Doxygen](https://cpp.openfoam.org/v8)). Compared to OpenFOAM 7: + +- **Function objects:** Function objects changed behavior, and the `execute()` method is now also executed at start. See the [adapter issue #179](https://github.com/precice/openfoam-adapter/issues/179) and [PR #180](https://github.com/precice/openfoam-adapter/pull/180). + - In the beginning of `preciceAdapterFunctionObject::read()`, add `this->executeAtStart_ = false;`. +- **Thermophysical models:** Some properties were moved from the previous `TurbulenceModels` and `transportModels` to the new `ThermophysicalTransportModels` and `MomentumTransportModels`. + - In `Make/options`, replace the following include paths: + - Remove the include paths to `transportModels/incompressible`, `transportModels/compressible`, `transportModels/twoPhaseMixture`, `transportModels/interfaceProperties`, `transportModels/immiscibleIncompressibleTwoPhaseMixture`, `TurbulenceModels/turbulenceModels`, `TurbulenceModels/compressible`, `TurbulenceModels/incompressible`. + - Add include paths to `ThermophysicalTransportModels`, `MomentumTransportModels/momentumTransportModels`, `MomentumTransportModels/compressible`, `MomentumTransportModels/incompressible`, `transportModels`. + - Remove the linking paths to `compressibleTurbulenceModels`, `incompressibleTurbulenceModels`. + - Add linking paths to `thermophysicalTransportModels`, `fluidThermoMomentumTransportModels`, `incompressibleMomentumTransportModels`. + - In `CHT/KappaEffective.H`: + - Remove the header inclusions `turbulentFluidThermoModel.H`, `turbulentTransportModel.H`. + - Include the headers `fluidThermoMomentumTransportModel.H`, `kinematicMomentumTransportModel.H`, `thermophysicalTransportModel.H`. + - Remove the variable declarations for `turbulence_` (2x), `nameRho_`, `nameCp_`, `namePr_`, `nameAlphat_`, `rho_`, `Cp_`, `Pr_` from the compressible and incompressible variants. + - In the incompressible variant's constructor signature, remove the `nameRho`, `nameCp`, `namePr`, `nameAlphat`. + - Add a new declaration: `const Foam::thermophysicalTransportModel& thermophysicalTransportModel_;` for each one of the `turbulence_` removed. + - In `CHT/KappaEffective.C`: + - Replace all instances of `turbulence_` with `thermophysicalTransportModel_` and update the respective comments. + - Replace `compressible::turbulenceModel` and `incompressible::turbulenceModel` with `thermophysicalTransportModel`. + - Replace `turbulenceModel::propertiesName` with `thermophysicalTransportModel::typeName`. + - In the incompressible variant, remove all lines of code working with `nameRho_`, `nameCp_`, `namePr_`, `nameAlphat_` from the constructor (which ends up with an empty body) and the `extract()` method. Replace the usage of `kappaEff_temp` with `thermophysicalTransportModel_.kappaEff()().boundaryField()[patchID]`. + - Replace the `const dictionary& CHTDict` with `const dictionary CHTDict`. + - In `CHT/HeatFlux.C` and in `CHT/HeatTransferCoefficient.C`: + - Replace `KappaEff_Incompressible(mesh, nameRho, nameCp, namePr, nameAlphat)` with `KappaEff_Incompressible(mesh)`. + - In `FSI/ForceBase.H`: + - Remove the header inclusions `immiscibleIncompressibleTwoPhaseMixture.H`, `turbulentFluidThermoModel.H`, `turbulentTransportModel.H`. + - Include the headers `fluidThermoMomentumTransportModel.H`, `kinematicMomentumTransportModel.H`. + - In `FSI/ForceBase.C`, in the `ForceBase::devRhoReff()`: + - Replace `compressible::turbulenceModel` with `compressible::momentumTransportModel`. + - Replace `incompressible::turbulenceModel` with `incompressible::momentumTransportModel`. + - Replace `cmpTurbModel::propertiesName` with `cmpTurbModel::typeName`. + - Replace `turb(mesh_.lookupObject<>())` with `turb = mesh_.lookupObject<>()` (where `const cmpTurbModel& turb`). + - Replace `turb.devRhoReff()` with `turb.devTau()`. + - Replace `turb.devReff()` with `turb.devSigma()`. + - Replace `U(mesh_.lookupObject<>())` with `U = mesh_.lookupObject<>()` (where `const volVectorField& U`). + - In `FSI/ForceBase.C`, in the `ForceBase::mu()`: + - Remove the line `typedef immiscibleIncompressibleTwoPhaseMixture iitpMixture;`. + - Replace the type `iitpMixture` with `fluidThermo` and rename the respective object from `mixture` to `thermo`. + - Replace `"mixture"` in the lookups with `basicThermo::dictName`. + +Related work in the adapter: [PR #130](https://github.com/precice/openfoam-adapter/pull/130). + +### OpenFOAM 9 + +OpenFOAM 9 was [released](https://openfoam.org/release/9/) in July 2021 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-9), [Doxygen](https://cpp.openfoam.org/v9)). Compared to OpenFOAM 8: + +- **Function objects:** The `setTimeStep()` method was removed. + - Remove the method from the `preciceAdapterFunctionObject.H` and `preciceAdapterFunctionObject.C`. Adjust the comments for the equivalent method in `Adapter.H`. + - **Impact:** See limitations in adjustable time step size ([#261](https://github.com/precice/openfoam-adapter/issues/261)). +- **Thermophysical models:** Different libraries need to be linked and different headers need to be included: + - In `Make/options`, remove the linking of `fluidThermoMomentumTransportModels` and `incompressibleMomentumTransportModels`. + - In `Make/options`, link to `transportModels`, `momentumTransportModels`, `incompressibleMomentumTransportModels`, `compressibleMomentumTransportModels`. + - In `CHT/KappaEffective.H`, replace the headers `fluidThermoMomentumTransportModel.H` and `kinematicMomentumTransportModel.H` with `momentumTransportModel.H`. + - In `FSI/ForceBase.H`, replace the header `fluidThermoMomentumTransportModel.H` with `compressibleMomentumTransportModel.H`. + - In `FSI/ForceBase.C`, add the header `fluidThermo.H`. + - In `FSI/ForceBase.C`, replace: + - `compressible::momentumTransportModel` with `compressibleMomentumTransportModel` + - `incompressible::momentumTransportModel` with `incompressibleMomentumTransportModel`. Further down, replace the usage `const incompressible::momentumTransportModel&` with `const icoTurbModel&`. +- **Triangulation:** In `Adapter.C`: + - Replace the header `faceTriangulation.H` with `polygonTriangulate.H`. +- **Case files:** + - In `constant/dynamicMeshDict`, replace `solver` with `motionSolver`. + - In `system/fvSolution`, replace `residualControl` with `outerCorrectorResidualControl`. This was already renamed to `outerResidualControl` in OpenFOAM 6. + +Related work in the adapter: [PR #221](https://github.com/precice/openfoam-adapter/pull/221). + +### OpenFOAM 10 + +OpenFOAM 10 was [released](https://openfoam.org/release/10/) in July 2022 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-10), [Doxygen](https://cpp.openfoam.org/v10)). Compared to OpenFOAM 9: + +- **Function objects:** The method `fields()` has been made pure virtual, meaning it needs to be implemented. + - In `preciceAdapterFunctionObject.H`, implement it as (more of a workaround to avoid implementation): + + ```c++ + virtual wordList fields() const + { + return wordList::null(); + } + ``` -We support mainstream OpenFOAM solvers such as pimpleFoam and solids4Foam for FSI, buoyantPimpleFoam, buoyantSimpleFoam, and laplacianFoam for CHT, or pimpleFoam and sonicLiquidFoam for FF. Our community has, additionally, tried the adapter with multiple different solvers that support function objects. +- **Physical properties and models:** The `physicalProperties` was replaced by `transportModels`. + - In `Make/options`, replace `physicalProperties` with `transportModels` in the included paths and in the linked libraries. + - In `FSI/ForceBase.H`, replace `kinematicMomentumTransportModel.H` with `incompressibleMomentumTransportModel.H`. + - In `FSI/ForceBase.C`, replace `basicThermo::dictName` with `physicalProperties::typeName`. +- **Fields `V0` and `V00`:** The fields `V0` and `V00` have been removed or renamed (not sure to what). + - In `Adapter.C`, method `preciceAdapter::Adapter::setupMeshVolCheckpointing()`, guard each of these two fields with a `if (mesh_.foundObject("V0"))` (and `if (... ("V00"))`, respectively). Alternatively, remove the implementation of this method altogether. + - **Impact:** This might lead to stability issues in FSI simulations with implicit coupling. Check your results. +- **Case files:** Several [changes to the tutorials](https://github.com/precice/tutorials/tree/OpenFOAM10) are needed, read the [discussion](https://github.com/precice/tutorials/pull/283). + +### OpenFOAM 11 + +OpenFOAM 11 was [released](https://openfoam.org/release/11/) in July 2023 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-11), [Doxygen](https://cpp.openfoam.org/v11)). We do not yet know which are relevant changes. Please help us port the adapter to OpenFOAM 11. + +Related work in the adapter: [PR #310](https://github.com/precice/openfoam-adapter/pull/310). + +### OpenFOAM 12 + +OpenFOAM 12 was [released](https://openfoam.org/release/12/) in July 2024 ([GitHub mirror](https://github.com/OpenFOAM/OpenFOAM-12), [Doxygen](https://cpp.openfoam.org/v12)). We do not yet know which are relevant changes. Please help us port the adapter to OpenFOAM 12. + +### foam-extend + +We have not yet documented all related changes that would make the adapter work for foam-extend. See the repository [`precice/foam-extend-adapter`](https://github.com/precice/foam-extend-adapter) for the latest state and please contribute to this page. + +Related work in the adapter: [PR #302](https://github.com/precice/openfoam-adapter/pull/302). ## Notes on OpenFOAM features diff --git a/module-generic/Generic.C b/module-generic/Generic.C new file mode 100644 index 00000000..0adadadf --- /dev/null +++ b/module-generic/Generic.C @@ -0,0 +1,118 @@ +#include "Generic.H" + +#include "Utilities.H" + +using namespace Foam; + +preciceAdapter::Generic::GenericInterface::GenericInterface( + const Foam::fvMesh& mesh) +: mesh_(mesh) {} + +bool preciceAdapter::Generic::GenericInterface::configure(const IOdictionary& adapterConfig) +{ + DEBUG(adapterInfo("Configuring the Generic module...")); + + // Scan OpenFOAM object registry once for all + // available volScalarFields and volVectorFields + for (const auto& solver_name : mesh_.sortedNames()) + { + availableVolScalarFields += solver_name + " "; + } + DEBUG(adapterInfo(" Available volScalarFields: " + availableVolScalarFields)); + + for (const auto& solver_name : mesh_.sortedNames()) + { + availableVolVectorFields += solver_name + " "; + } + DEBUG(adapterInfo(" Available volVectorFields: " + availableVolVectorFields)); + + // Read the Generic-module specific options from the adapter's configuration file + if (!readConfig(adapterConfig)) + { + return false; + } + + return true; +} + +bool preciceAdapter::Generic::GenericInterface::readConfig(const IOdictionary& adapterConfig) +{ + // Empty for now. No other specific configuration options for the Generic module. + return true; +} + +bool preciceAdapter::Generic::GenericInterface::addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) +{ + bool found = false; + + // Force to use the new schema with the Generic module + if (fieldConfig.solver_name != "Undefined (legacy mode)") + { + // Determine type of field + if (mesh_.foundObject(fieldConfig.solver_name)) + { + found = true; + interface->addCouplingDataWriter( + fieldConfig, + new ScalarFieldCoupler(mesh_, fieldConfig)); + } + else if (mesh_.foundObject(fieldConfig.solver_name)) + { + found = true; + interface->addCouplingDataWriter( + fieldConfig, + new VectorFieldCoupler(mesh_, fieldConfig)); + } + else + { + found = false; + std::string msg = "Generic module: Data \"" + fieldConfig.name + "\", solver name: \"" + fieldConfig.solver_name + "\" not found!\n"; + msg += "Available fields: " + availableVolScalarFields + availableVolVectorFields; + adapterInfo(msg, "warning"); + } + } + + if (found) + { + DEBUG(adapterInfo("Added writer: " + fieldConfig.name)); + } + return found; +} + +bool preciceAdapter::Generic::GenericInterface::addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface) +{ + bool found = false; + + // Force to use the new schema with the Generic modul + if (fieldConfig.solver_name != "Undefined (legacy mode)") + { + // Determine type of field + if (mesh_.foundObject(fieldConfig.solver_name)) + { + found = true; + interface->addCouplingDataReader( + fieldConfig, + new ScalarFieldCoupler(mesh_, fieldConfig)); + } + else if (mesh_.foundObject(fieldConfig.solver_name)) + { + found = true; + interface->addCouplingDataReader( + fieldConfig, + new VectorFieldCoupler(mesh_, fieldConfig)); + } + else + { + found = false; + std::string msg = "Generic module: Data \"" + fieldConfig.name + "\" (solver name: \"" + fieldConfig.solver_name + "\") not found!\n"; + msg += "Available fields: " + availableVolScalarFields + availableVolVectorFields; + adapterInfo(msg, "warning"); + } + } + + if (found) + { + DEBUG(adapterInfo("Added reader: " + fieldConfig.name)); + } + return found; +} diff --git a/module-generic/Generic.H b/module-generic/Generic.H new file mode 100644 index 00000000..de284cc6 --- /dev/null +++ b/module-generic/Generic.H @@ -0,0 +1,55 @@ +#ifndef GENERICMODULE_H +#define GENERICMODULE_H + +#include "Interface.H" + +#include "ReadWrite.H" + +#include "fvCFD.H" + +namespace preciceAdapter +{ +namespace Generic +{ + +class GenericInterface +{ +protected: + //- OpenFOAM fvMesh object + const Foam::fvMesh& mesh_; + + //- Read the Generic related options from the adapter's configuration file + bool readConfig(const IOdictionary& adapterConfig); + + //- Struct to store interfaces configuration (modified from Adapter.C) + struct InterfaceConfig + { + std::vector writeData; + std::vector readData; + }; + + //- Configuration interfaces + std::vector interfacesConfig_; + + //- List of available fields in the OpenFOAM object registry + std::string availableVolScalarFields; + std::string availableVolVectorFields; + +public: + //- Constructor + GenericInterface(const Foam::fvMesh& mesh); + + //- Configure + bool configure(const IOdictionary& adapterConfig); + + //- Add coupling data writers + bool addWriters(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); + + //- Add coupling data readers + bool addReaders(const preciceAdapter::FieldConfig& fieldConfig, Interface* interface); +}; + +} +} + +#endif diff --git a/module-generic/ModuleGeneric.C b/module-generic/ModuleGeneric.C new file mode 100644 index 00000000..ff567d14 --- /dev/null +++ b/module-generic/ModuleGeneric.C @@ -0,0 +1,3 @@ +// Central inlude file to reduce the number of build targets and build time +#include "ReadWrite.C" +#include "Generic.C" diff --git a/module-generic/ReadWrite.C b/module-generic/ReadWrite.C new file mode 100644 index 00000000..08db5756 --- /dev/null +++ b/module-generic/ReadWrite.C @@ -0,0 +1,556 @@ +#include "ReadWrite.H" +#include "primitivePatchInterpolation.H" +#include "fvCFD.H" + +#include "fixedValueFvPatchFields.H" +#include "fixedGradientFvPatchFields.H" +#include "mixedFvPatchFields.H" + +using namespace Foam; + +//----- preciceAdapter::Generic::scalarFieldCoupler ----------------------------------------- + +preciceAdapter::Generic::ScalarFieldCoupler::ScalarFieldCoupler( + const Foam::fvMesh& mesh, + const preciceAdapter::FieldConfig& fieldConfig) +: scalarField_( + const_cast( + &mesh.lookupObject(fieldConfig.solver_name))), + mesh_(mesh), + fieldConfig_(fieldConfig) +{ + if (fieldConfig_.operation == "gradient") + { + dataType_ = vector; + + // Allocate memory for gradient field + gradientFieldPtr_ = new volVectorField( + IOobject( + "grad" + fieldConfig_.solver_name, + mesh_.time().timeName(), + mesh_, + IOobject::NO_READ, + IOobject::NO_WRITE), + fvc::grad(*scalarField_)); + } + else + { + dataType_ = scalar; + } +} + +void preciceAdapter::Generic::ScalarFieldCoupler::initialize() +{ + if (fieldConfig_.operation == "surface-normal-gradient") + { + if (this->locationType_ != LocationType::faceCenters) + { + adapterInfo("Generic module: The surface-normal-gradient operation is only supported for faceCenters location type.", "error"); + } + } + + if (fieldConfig_.operation == "ref-value" || fieldConfig_.operation == "ref-gradient" || fieldConfig_.operation == "value-fraction") + { + if (this->locationType_ != LocationType::faceCenters) + { + adapterInfo("Generic module: Robin boundary conditions (ref-value, ref-gradient, and value-fraction operations) are supported only for surface locations (faceCenters).", "error"); + } + } +} + + +std::size_t preciceAdapter::Generic::ScalarFieldCoupler::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + + if (fieldConfig_.operation == "gradient") + { + // Calculate the full gradient (volVectorField) + *gradientFieldPtr_ = fvc::grad(*scalarField_); + volVectorField& gradScalarField = *gradientFieldPtr_; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : gradScalarField.internalField()) + { + // x-dimension + buffer[bufferIndex++] = cell.x(); + + // y-dimension + buffer[bufferIndex++] = cell.y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = cell.z(); + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(scalarField_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + buffer[bufferIndex++] = gradScalarField.internalField()[currentCell].x(); + + // y-dimension + buffer[bufferIndex++] = gradScalarField.internalField()[currentCell].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = gradScalarField.internalField()[currentCell].z(); + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // For every cell of the patch + forAll(gradScalarField.boundaryField()[patchID], i) + { + // Copy the velocity into the buffer + // x-dimension + buffer[bufferIndex++] = + gradScalarField.boundaryField()[patchID][i].x(); + + // y-dimension + buffer[bufferIndex++] = + gradScalarField.boundaryField()[patchID][i].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = + gradScalarField.boundaryField()[patchID][i].z(); + } + } + } + return bufferIndex; + } + + if (fieldConfig_.operation == "surface-normal-gradient") + { + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the surface normal gradient boundary patch + scalarField gradientPatch(scalarField_->boundaryField()[patchID].snGrad()); + + // For every cell of the patch + forAll(gradientPatch, i) + { + buffer[bufferIndex++] = gradientPatch[i]; + } + } + return bufferIndex; + } + + if (fieldConfig_.operation == "ref-value" || fieldConfig_.operation == "ref-gradient" || fieldConfig_.operation == "value-fraction") + { + adapterInfo( + "Generic module: The mixed boundary condition operations (ref-value, ref-gradient, and value-fraction) are only supported for the read side. If you wish to write these fields, just use the value or surface-normal-gradient operations instead.", + "error"); + } + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : scalarField_->internalField()) + { + buffer[bufferIndex++] = cell; + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(scalarField_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // Copy the scalar value into the buffer + buffer[bufferIndex++] = scalarField_->internalField()[currentCell]; + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + const auto& boundaryPatch(scalarField_->boundaryField()[patchID]); + + //If we use the mesh connectivity, we interpolate from the centres to the nodes + if (meshConnectivity) + { + //Create an Interpolation object at the boundary Field + primitivePatchInterpolation patchInterpolator(mesh_.boundaryMesh()[patchID]); + + //Interpolate from centers to nodes + scalarField pointValues( + patchInterpolator.faceToPointInterpolate(boundaryPatch)); + + forAll(pointValues, i) + { + // Copy the scalar value into the buffer + buffer[bufferIndex++] = pointValues[i]; + } + } + else + { + // For every cell of the patch + forAll(boundaryPatch, i) + { + buffer[bufferIndex++] = boundaryPatch[i]; + } + } + } + return bufferIndex; +} + +void preciceAdapter::Generic::ScalarFieldCoupler::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + if (fieldConfig_.operation == "gradient" || fieldConfig_.operation == "surface-normal-gradient") + { + adapterInfo("Generic module: The gradient operation is only supported for writing.", "error"); + } + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (auto& cell : scalarField_->ref()) + { + cell = buffer[bufferIndex++]; + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(scalarField_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // Copy the scalar value from the buffer + scalarField_->ref()[currentCell] = buffer[bufferIndex++]; + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + auto& bc = scalarField_->boundaryFieldRef()[patchID]; + + if (isA(bc)) + { + if (fieldConfig_.operation != "value") + { + adapterInfo( + "Generic module: Unsupported operation " + fieldConfig_.operation + " for boundary condition type " + bc.type() + ". Supported operation is value.", + "error"); + } + + auto& boundaryPatch = refCast(bc); + forAll(boundaryPatch, i) + { + boundaryPatch[i] = buffer[bufferIndex++]; + } + } + else if (isA(bc)) + { + if (fieldConfig_.operation != "value") + { + adapterInfo( + "Generic module: Unsupported operation " + fieldConfig_.operation + " for boundary condition type " + bc.type() + ". Supported operation is value.", + "error"); + } + + auto& boundaryPatch = refCast(bc); + forAll(boundaryPatch, i) + { + boundaryPatch.gradient()[i] = buffer[bufferIndex++]; + } + } + else if (isA(bc)) + { + auto& boundaryPatch = refCast(bc); + if (fieldConfig_.operation == "ref-value") + { + forAll(boundaryPatch, i) + { + boundaryPatch.refValue()[i] = buffer[bufferIndex++]; + } + } + else if (fieldConfig_.operation == "ref-gradient") + { + forAll(boundaryPatch, i) + { + boundaryPatch.refGrad()[i] = buffer[bufferIndex++]; + } + } + else if (fieldConfig_.operation == "value-fraction") + { + forAll(boundaryPatch, i) + { + boundaryPatch.valueFraction()[i] = buffer[bufferIndex++]; + } + } + else + { + adapterInfo( + "Generic module: Unsupported operation " + fieldConfig_.operation + " for boundary condition type " + bc.type() + ". Supported operations are ref-value, ref-gradient, and value-fraction.", + "error"); + } + } + else + { + adapterInfo("Generic module: Unsupported boundary condition type " + bc.type(), "error"); + } + } +} + +bool preciceAdapter::Generic::ScalarFieldCoupler::isLocationTypeSupported(const bool meshConnectivity) const +{ + if (meshConnectivity) + { + return (this->locationType_ == LocationType::faceNodes); + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); + } +} + +std::string preciceAdapter::Generic::ScalarFieldCoupler::getDataName() const +{ + return fieldConfig_.name; +} + +//----- preciceAdapter::Generic::VectorFieldCoupler ----------------------------------------- + +preciceAdapter::Generic::VectorFieldCoupler::VectorFieldCoupler( + const Foam::fvMesh& mesh, + const preciceAdapter::FieldConfig& fieldConfig) +: vectorField_( + const_cast( + &mesh.lookupObject(fieldConfig.solver_name))), + mesh_(mesh), + fieldConfig_(fieldConfig) +{ + dataType_ = vector; +} + +void preciceAdapter::Generic::VectorFieldCoupler::initialize() +{ + // In the Generic module, for vector fields only the value operation is supported for now, i.e., cannot write gradients. + if (fieldConfig_.operation == "gradient" || fieldConfig_.operation == "surface-normal-gradient") + { + adapterInfo("Generic module: The gradient operation is not yet supported for vector fields.", "error"); + } +} + +std::size_t preciceAdapter::Generic::VectorFieldCoupler::write(double* buffer, bool meshConnectivity, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (const auto& cell : vectorField_->internalField()) + { + // x-dimension + buffer[bufferIndex++] = cell.x(); + + // y-dimension + buffer[bufferIndex++] = cell.y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = cell.z(); + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(vectorField_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + buffer[bufferIndex++] = vectorField_->internalField()[currentCell].x(); + + // y-dimension + buffer[bufferIndex++] = vectorField_->internalField()[currentCell].y(); + + if (dim == 3) + { + // z-dimension + buffer[bufferIndex++] = vectorField_->internalField()[currentCell].z(); + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + // Get the vector field boundary patch + auto& boundaryPatch = vectorField_->boundaryField()[patchID]; + + // For every cell of the patch + forAll(boundaryPatch, i) + { + buffer[bufferIndex++] = boundaryPatch[i].x(); + + buffer[bufferIndex++] = boundaryPatch[i].y(); + + if (dim == 3) + { + buffer[bufferIndex++] = boundaryPatch[i].z(); + } + } + } + return bufferIndex; +} + +void preciceAdapter::Generic::VectorFieldCoupler::read(double* buffer, const unsigned int dim) +{ + int bufferIndex = 0; + + if (this->locationType_ == LocationType::volumeCenters) + { + if (cellSetNames_.empty()) + { + for (auto& cell : vectorField_->ref()) + { + // x-dimension + cell.x() = buffer[bufferIndex++]; + + // y-dimension + cell.y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + cell.z() = buffer[bufferIndex++]; + } + } + } + else + { + for (const auto& cellSetName : cellSetNames_) + { + cellSet overlapRegion(vectorField_->mesh(), cellSetName); + const labelList& cells = overlapRegion.toc(); + + for (const auto& currentCell : cells) + { + // x-dimension + vectorField_->ref()[currentCell].x() = buffer[bufferIndex++]; + + // y-dimension + vectorField_->ref()[currentCell].y() = buffer[bufferIndex++]; + + if (dim == 3) + { + // z-dimension + vectorField_->ref()[currentCell].z() = buffer[bufferIndex++]; + } + } + } + } + } + + // For every boundary patch of the interface + for (uint j = 0; j < patchIDs_.size(); j++) + { + int patchID = patchIDs_.at(j); + + auto& bc = vectorField_->boundaryFieldRef()[patchID]; + + if (isA(bc)) + { + auto& boundaryPatch = refCast(bc); + forAll(boundaryPatch, i) + { + boundaryPatch[i].x() = buffer[bufferIndex++]; + boundaryPatch[i].y() = buffer[bufferIndex++]; + + if (dim == 3) + { + boundaryPatch[i].z() = buffer[bufferIndex++]; + } + } + } + else if (isA(bc)) + { + auto& boundaryPatch = refCast(bc); + forAll(boundaryPatch, i) + { + boundaryPatch.gradient()[i].x() = buffer[bufferIndex++]; + boundaryPatch.gradient()[i].y() = buffer[bufferIndex++]; + + if (dim == 3) + { + boundaryPatch.gradient()[i].z() = buffer[bufferIndex++]; + } + } + } + else + { + adapterInfo("Generic module: Unsupported boundary condition type " + bc.type(), "error"); + } + } +} + +bool preciceAdapter::Generic::VectorFieldCoupler::isLocationTypeSupported(const bool meshConnectivity) const +{ + if (meshConnectivity) + { + return false; + } + else + { + return (this->locationType_ == LocationType::faceCenters || this->locationType_ == LocationType::volumeCenters); + } +} + +std::string preciceAdapter::Generic::VectorFieldCoupler::getDataName() const +{ + return fieldConfig_.name; +} diff --git a/module-generic/ReadWrite.H b/module-generic/ReadWrite.H new file mode 100644 index 00000000..de0f6bad --- /dev/null +++ b/module-generic/ReadWrite.H @@ -0,0 +1,57 @@ +#ifndef MODULE_ReadWrite_H +#define MODULE_ReadWrite_H + +#include "Interface.H" +#include "CouplingDataUser.H" + +#include "fvCFD.H" +#include "cellSet.H" + +namespace preciceAdapter +{ +namespace Generic +{ + +class ScalarFieldCoupler : public CouplingDataUser // One CouplingDataUser for each field +{ +protected: + Foam::volScalarField* scalarField_; + const Foam::fvMesh& mesh_; + const preciceAdapter::FieldConfig& fieldConfig_; + Foam::volVectorField* gradientFieldPtr_ = nullptr; + +public: + ScalarFieldCoupler(const Foam::fvMesh& mesh, const preciceAdapter::FieldConfig& fieldConfig); + + void initialize(); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + void read(double* buffer, const unsigned int dim); + bool isLocationTypeSupported(const bool meshConnectivity) const; + std::string getDataName() const; + + virtual ~ScalarFieldCoupler() { delete gradientFieldPtr_; }; +}; + +class VectorFieldCoupler : public CouplingDataUser +{ +protected: + Foam::volVectorField* vectorField_; + const Foam::fvMesh& mesh_; + const preciceAdapter::FieldConfig& fieldConfig_; + +public: + VectorFieldCoupler(const Foam::fvMesh& mesh, const preciceAdapter::FieldConfig& fieldConfig); + + void initialize(); + std::size_t write(double* buffer, bool meshConnectivity, const unsigned int dim); + void read(double* buffer, const unsigned int dim); + bool isLocationTypeSupported(const bool meshConnectivity) const; + std::string getDataName() const; + + virtual ~VectorFieldCoupler() {}; +}; + +} +} + +#endif diff --git a/tools/create-release-archives.sh b/tools/create-release-archives.sh index ff284a65..e853bb1f 100755 --- a/tools/create-release-archives.sh +++ b/tools/create-release-archives.sh @@ -4,7 +4,7 @@ set -u remote_name=origin -adapter_version="v1.3.1" +adapter_version="v1.4.0" # Declare branch names for which archives will be created declare -a branches=("master" "OpenFOAMv1806" "OpenFOAM10" "OpenFOAM9" "OpenFOAM8" "OpenFOAM7" "OpenFOAM6" "OpenFOAM5") @@ -12,6 +12,6 @@ declare -a branches=("master" "OpenFOAMv1806" "OpenFOAM10" "OpenFOAM9" "OpenFOAM mkdir -p release-archives for i in "${branches[@]}" do - archive_name=openefoam-adapter-"${adapter_version}"-"${i}" + archive_name=openfoam-adapter-"${adapter_version}"-"${i}" git archive --format=tar.gz --prefix="${archive_name}"/ "${remote_name}"/"${i}" >"release-archives/${archive_name}".tar.gz done