diff --git a/Interface.C b/Interface.C index 85c5dc21..b8b51891 100644 --- a/Interface.C +++ b/Interface.C @@ -460,6 +460,27 @@ 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"); + } } @@ -490,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() diff --git a/docs/config.md b/docs/config.md index 7d78adda..07fb167d 100644 --- a/docs/config.md +++ b/docs/config.md @@ -283,7 +283,7 @@ interfaces { 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') + 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') } @@ -302,7 +302,11 @@ Explicitly setting `locations volumeCenters;` in preciceDict is required for vol 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. -The `operation surface-normal-gradient;` is currently only supported for scalar field surface writing. The operation `gradient` as well as the option `flip-normal` are not yet supported by the adapter. +Limitations: + +- The operation `gradient` is currently only supported for 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 diff --git a/module-generic/ReadWrite.C b/module-generic/ReadWrite.C index 873d4c11..57237f3c 100644 --- a/module-generic/ReadWrite.C +++ b/module-generic/ReadWrite.C @@ -18,7 +18,14 @@ preciceAdapter::Generic::ScalarFieldCoupler::ScalarFieldCoupler( mesh_(mesh), fieldConfig_(fieldConfig) { - dataType_ = scalar; + if (fieldConfig_.operation == "gradient") + { + dataType_ = vector; + } + else + { + dataType_ = scalar; + } } void preciceAdapter::Generic::ScalarFieldCoupler::initialize() @@ -30,11 +37,6 @@ void preciceAdapter::Generic::ScalarFieldCoupler::initialize() adapterInfo("Generic module: The surface-normal-gradient operation is only supported for faceCenters location type.", "error"); } } - - if (fieldConfig_.operation == "gradient") - { - adapterInfo("Generic module: The gradient operation is not yet supported for scalar fields. Maybe you meant surface-normal-gradient?", "error"); - } } @@ -42,6 +44,85 @@ std::size_t preciceAdapter::Generic::ScalarFieldCoupler::write(double* buffer, b { int bufferIndex = 0; + if (fieldConfig_.operation == "gradient") + { + // Calculate the full gradient (volVectorField) + // Temporary field discarded afterwards + Foam::tmp tmpObject(fvc::grad(*scalarField_)); + const volVectorField& gradScalarField = tmpObject(); // dereference + + 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 @@ -125,6 +206,11 @@ void preciceAdapter::Generic::ScalarFieldCoupler::read(double* buffer, const uns { 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())