1096 lines
45 KiB
C++
1096 lines
45 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2013 Jan Rheinländer *
|
|
* <jrheinlaender@users.sourceforge.net> *
|
|
* Copyright (c) 2016 Qingfeng Xia <qingfeng.xia[at]iesensor.com> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
#include <QAction>
|
|
#include <QMessageBox>
|
|
#include <TopoDS.hxx>
|
|
#include <TopoDS_Shape.hxx>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#endif
|
|
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <Base/Console.h>
|
|
#include <Base/Tools.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/Selection/SelectionObject.h>
|
|
#include <Gui/ViewProvider.h>
|
|
#include <Mod/Fem/App/FemAnalysis.h>
|
|
#include <Mod/Fem/App/FemConstraintFluidBoundary.h>
|
|
#include <Mod/Fem/App/FemMeshObject.h>
|
|
#include <Mod/Fem/App/FemSolverObject.h>
|
|
#include <Mod/Fem/App/FemTools.h>
|
|
#include <Mod/Part/App/PartFeature.h>
|
|
|
|
#include "ActiveAnalysisObserver.h"
|
|
#include "TaskFemConstraintFluidBoundary.h"
|
|
#include "ui_TaskFemConstraintFluidBoundary.h"
|
|
|
|
|
|
using namespace FemGui;
|
|
using namespace Gui;
|
|
using namespace Fem;
|
|
|
|
// also defined in FemConstrainFluidBoundary and foamcasebuilder/basicbuilder.py, please update
|
|
// simultaneously the second (index 1) is the default enum, as index 0 causes compiling error static
|
|
// const char* BoundaryTypes[] = {"inlet","wall","outlet","freestream", "interface", NULL};
|
|
static const char* WallSubtypes[] =
|
|
{"unspecific", "fixed", "slip", "partialSlip", "moving", "rough", nullptr};
|
|
static const char* InletSubtypes[] = {"unspecific",
|
|
"totalPressure",
|
|
"uniformVelocity",
|
|
"volumetricFlowRate",
|
|
"massFlowRate",
|
|
nullptr};
|
|
static const char* OutletSubtypes[] =
|
|
{"unspecific", "totalPressure", "staticPressure", "uniformVelocity", "outFlow", nullptr};
|
|
static const char* InterfaceSubtypes[] =
|
|
{"unspecific", "symmetry", "wedge", "cyclic", "empty", "coupled", nullptr};
|
|
static const char* FreestreamSubtypes[] = {"unspecific", "freestream", nullptr};
|
|
|
|
static const char* InterfaceSubtypeHelpTexts[] = {
|
|
"invalid,select other valid interface subtype",
|
|
"symmetry plane but not axis-sym axis line",
|
|
"axis symmetric front and back surfaces",
|
|
"periodic boundary in pair, treated as physical connected",
|
|
"front and back for single layer 2D mesh, also axis-sym axis line",
|
|
"exchange boundary vale with external program, need extra manual setup like file name",
|
|
nullptr};
|
|
|
|
// defined in file FemConstraintFluidBoundary:
|
|
// see Ansys fluet manual: Turbulence Specification method
|
|
// static const char* TurbulenceSpecifications[] = {"intensity&DissipationRate",
|
|
// "intensity&LengthScale","intensity&ViscosityRatio", "intensity&HydraulicDiameter",NULL}; activate
|
|
// the heat transfer and radiation model in Solver object explorer
|
|
static const char* TurbulenceSpecificationHelpTexts[] = {
|
|
"explicitly specific intensity k [SI unit] and dissipation rate epsilon [] / omega []",
|
|
"intensity (0.05 ~ 0.15) and characteristic length scale of max eddy [m]",
|
|
"intensity (0.05 ~ 0.15) and turbulent viscosity ratio",
|
|
"for fully developed internal flow, Turbulence intensity (0-1.0) 0.05 typical",
|
|
nullptr};
|
|
|
|
// static const char* ThermalBoundaryTypes[] = {"fixedValue","zeroGradient", "fixedGradient",
|
|
// "mixed", "heatFlux", "HTC","coupled", NULL};
|
|
static const char* ThermalBoundaryHelpTexts[] = {"fixed Temperature [K]",
|
|
"no heat transfer on boundary",
|
|
"fixed value gradient [K/m]",
|
|
"mixed fixedGradient and fixedValue",
|
|
"fixed heat flux [W/m2]",
|
|
"Heat transfer coeff [W/(M2)/K]",
|
|
"conjugate heat transfer with solid",
|
|
nullptr};
|
|
// enable & disable quantityUI once valueType is selected
|
|
|
|
// internal function not declared in header file
|
|
void initComboBox(QComboBox* combo,
|
|
const std::vector<std::string>& textItems,
|
|
const std::string& sItem)
|
|
{
|
|
combo->blockSignals(true);
|
|
|
|
int iItem = 1; // the first one is "unspecific" (index 0)
|
|
combo->clear();
|
|
for (unsigned int it = 0; it < textItems.size(); it++) {
|
|
combo->insertItem(it, QString::fromStdString(textItems[it]));
|
|
if (sItem == textItems[it]) {
|
|
iItem = it;
|
|
}
|
|
}
|
|
combo->setCurrentIndex(iItem);
|
|
combo->blockSignals(false);
|
|
}
|
|
|
|
/* TRANSLATOR FemGui::TaskFemConstraintFluidBoundary */
|
|
TaskFemConstraintFluidBoundary::TaskFemConstraintFluidBoundary(
|
|
ViewProviderFemConstraintFluidBoundary* ConstraintView,
|
|
QWidget* parent)
|
|
: TaskFemConstraintOnBoundary(ConstraintView, parent, "FEM_ConstraintFluidBoundary")
|
|
, ui(new Ui_TaskFemConstraintFluidBoundary)
|
|
, dimension(-1)
|
|
{
|
|
// we need a separate container widget to add all controls to
|
|
proxy = new QWidget(this);
|
|
ui->setupUi(proxy);
|
|
QMetaObject::connectSlotsByName(this);
|
|
|
|
// create a context menu for the listview of the references
|
|
createDeleteAction(ui->listReferences);
|
|
connect(deleteAction,
|
|
&QAction::triggered,
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onReferenceDeleted);
|
|
|
|
// setup ranges
|
|
constexpr float max = std::numeric_limits<float>::max();
|
|
ui->spinBoundaryValue->setMinimum(-max);
|
|
ui->spinBoundaryValue->setMaximum(max);
|
|
ui->spinTurbulentIntensityValue->setMinimum(0.0);
|
|
ui->spinTurbulentIntensityValue->setMaximum(max);
|
|
ui->spinTurbulentLengthValue->setMinimum(0.0);
|
|
ui->spinTurbulentLengthValue->setMaximum(max);
|
|
ui->spinTemperatureValue->setMinimum(-273.15);
|
|
ui->spinTemperatureValue->setMaximum(max);
|
|
ui->spinHeatFluxValue->setMinimum(0.0);
|
|
ui->spinHeatFluxValue->setMaximum(max);
|
|
ui->spinHTCoeffValue->setMinimum(0.0);
|
|
ui->spinHTCoeffValue->setMaximum(max);
|
|
|
|
connect(ui->comboBoundaryType,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onBoundaryTypeChanged);
|
|
connect(ui->comboSubtype,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onSubtypeChanged);
|
|
connect(ui->spinBoundaryValue,
|
|
qOverload<double>(&QDoubleSpinBox::valueChanged),
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onBoundaryValueChanged);
|
|
|
|
connect(ui->comboTurbulenceSpecification,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onTurbulenceSpecificationChanged);
|
|
connect(ui->comboThermalBoundaryType,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onThermalBoundaryTypeChanged);
|
|
|
|
connect(ui->buttonDirection, &QPushButton::pressed, this, [this] {
|
|
onButtonDirection(true);
|
|
});
|
|
connect(ui->checkReverse,
|
|
&QCheckBox::toggled,
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::onCheckReverse);
|
|
|
|
connect(ui->listReferences,
|
|
&QListWidget::itemClicked,
|
|
this,
|
|
&TaskFemConstraintFluidBoundary::setSelection);
|
|
|
|
this->groupLayout()->addWidget(proxy);
|
|
|
|
// Temporarily prevent unnecessary feature recomputes
|
|
ui->spinBoundaryValue->blockSignals(true);
|
|
ui->listReferences->blockSignals(true);
|
|
// boundaryType and subType combo signal is Temporarily prevented in initComboBox()
|
|
ui->buttonDirection->blockSignals(true);
|
|
ui->checkReverse->blockSignals(true);
|
|
|
|
// Selection buttons
|
|
buttonGroup->addButton(ui->btnAdd, (int)SelectionChangeModes::refAdd);
|
|
buttonGroup->addButton(ui->btnRemove, (int)SelectionChangeModes::refRemove);
|
|
|
|
// Get the feature data
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
|
|
Fem::FemAnalysis* pcAnalysis = nullptr;
|
|
if (FemGui::ActiveAnalysisObserver::instance()->hasActiveObject()) {
|
|
pcAnalysis = FemGui::ActiveAnalysisObserver::instance()->getActiveObject();
|
|
}
|
|
else {
|
|
App::Document* aDoc = pcConstraint->getDocument();
|
|
std::vector<App::DocumentObject*> fem =
|
|
aDoc->getObjectsOfType(Fem::FemAnalysis::getClassTypeId());
|
|
if (!fem.empty()) {
|
|
pcAnalysis = static_cast<Fem::FemAnalysis*>(fem[0]); // get the first
|
|
}
|
|
}
|
|
|
|
Fem::FemMeshObject* pcMesh = nullptr;
|
|
if (pcAnalysis) {
|
|
std::vector<App::DocumentObject*> fem = pcAnalysis->Group.getValues();
|
|
for (auto it : fem) {
|
|
if (it->isDerivedFrom<Fem::FemMeshObject>()) {
|
|
pcMesh = static_cast<Fem::FemMeshObject*>(it);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Base::Console().Log("FemAnalysis object is not activated or no FemAnalysis in the active "
|
|
"document, mesh dimension is unknown\n");
|
|
dimension = -1; // unknown dimension of mesh
|
|
}
|
|
if (pcMesh) {
|
|
App::Property* prop = pcMesh->getPropertyByName("Shape"); // PropertyLink
|
|
if (prop && prop->isDerivedFrom<App::PropertyLink>()) {
|
|
App::PropertyLink* pcLink = static_cast<App::PropertyLink*>(prop);
|
|
Part::Feature* pcPart = dynamic_cast<Part::Feature*>(pcLink->getValue());
|
|
if (pcPart) { // deduct dimension from part_obj.Shape.ShapeType
|
|
const TopoDS_Shape& pShape = pcPart->Shape.getShape().getShape();
|
|
const TopAbs_ShapeEnum shapeType =
|
|
pShape.IsNull() ? TopAbs_SHAPE : pShape.ShapeType();
|
|
if (shapeType == TopAbs_SOLID
|
|
|| shapeType == TopAbs_COMPSOLID) { // COMPSOLID is solids connected by faces
|
|
dimension = 3;
|
|
}
|
|
else if (shapeType == TopAbs_FACE || shapeType == TopAbs_SHELL) {
|
|
dimension = 2;
|
|
}
|
|
else if (shapeType == TopAbs_EDGE || shapeType == TopAbs_WIRE) {
|
|
dimension = 1;
|
|
}
|
|
else {
|
|
dimension =
|
|
-1; // Vertex (0D) can not make mesh, Compound type might contain any types
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pcSolver = nullptr; // this is an private object of type Fem::FemSolverObject*
|
|
if (pcAnalysis) {
|
|
std::vector<App::DocumentObject*> fem = pcAnalysis->Group.getValues();
|
|
for (auto it : fem) {
|
|
if (it->isDerivedFrom<Fem::FemSolverObject>()) {
|
|
pcSolver = static_cast<Fem::FemSolverObject*>(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
pHeatTransferring = nullptr;
|
|
pTurbulenceModel = nullptr;
|
|
if (pcSolver) {
|
|
// if only it is CFD solver, otherwise exit by SIGSEGV error, detect getPropertyByName() !=
|
|
// NULL
|
|
if (pcSolver->getPropertyByName("HeatTransferring")) {
|
|
pHeatTransferring =
|
|
static_cast<App::PropertyBool*>(pcSolver->getPropertyByName("HeatTransferring"));
|
|
if (pHeatTransferring->getValue()) {
|
|
ui->tabThermalBoundary->setEnabled(true);
|
|
initComboBox(ui->comboThermalBoundaryType,
|
|
pcConstraint->ThermalBoundaryType.getEnumVector(),
|
|
pcConstraint->ThermalBoundaryType.getValueAsString());
|
|
ui->spinHTCoeffValue->setValue(pcConstraint->HTCoeffValue.getValue());
|
|
ui->spinHeatFluxValue->setValue(pcConstraint->HeatFluxValue.getValue());
|
|
ui->spinTemperatureValue->setValue(pcConstraint->TemperatureValue.getValue());
|
|
updateThermalBoundaryUI();
|
|
}
|
|
else {
|
|
ui->tabThermalBoundary->setEnabled(false); // could be hidden
|
|
// Base::Console().Message("retrieve solver property HeatTransferring as false\n");
|
|
}
|
|
}
|
|
else {
|
|
ui->tabThermalBoundary->setEnabled(false);
|
|
}
|
|
if (pcSolver->getPropertyByName("TurbulenceModel")) {
|
|
pTurbulenceModel = static_cast<App::PropertyEnumeration*>(
|
|
pcSolver->getPropertyByName("TurbulenceModel"));
|
|
if (pTurbulenceModel->getValueAsString() == std::string("laminar")) {
|
|
ui->tabTurbulenceBoundary->setEnabled(false);
|
|
}
|
|
else {
|
|
ui->tabTurbulenceBoundary->setEnabled(true);
|
|
ui->labelTurbulenceSpecification->setText(
|
|
QString::fromStdString(pTurbulenceModel->getValueAsString()));
|
|
initComboBox(ui->comboTurbulenceSpecification,
|
|
pcConstraint->TurbulenceSpecification.getEnumVector(),
|
|
pcConstraint->TurbulenceSpecification.getValueAsString());
|
|
ui->spinTurbulentIntensityValue->setValue(
|
|
pcConstraint->TurbulentIntensityValue.getValue());
|
|
ui->spinTurbulentLengthValue->setValue(
|
|
pcConstraint->TurbulentLengthValue.getValue());
|
|
updateTurbulenceUI();
|
|
}
|
|
}
|
|
else {
|
|
ui->tabTurbulenceBoundary->setEnabled(false);
|
|
}
|
|
}
|
|
else {
|
|
Base::Console().Warning(
|
|
"No solver object inside FemAnalysis object, default to non-thermal, non-turbulence\n");
|
|
}
|
|
ui->tabWidget->setTabText(0, tr("Basic"));
|
|
ui->tabWidget->setTabText(1, tr("Turbulence"));
|
|
ui->tabWidget->setTabText(2, tr("Thermal"));
|
|
ui->tabWidget->setCurrentIndex(0);
|
|
ui->labelHelpText->setText(tr("select boundary type, faces and set value"));
|
|
|
|
initComboBox(ui->comboBoundaryType,
|
|
pcConstraint->BoundaryType.getEnumVector(),
|
|
pcConstraint->BoundaryType.getValueAsString());
|
|
updateBoundaryTypeUI();
|
|
std::vector<std::string> subtypes = pcConstraint->Subtype.getEnumVector();
|
|
initComboBox(ui->comboSubtype, subtypes, pcConstraint->Subtype.getValueAsString());
|
|
updateSubtypeUI();
|
|
|
|
std::vector<App::DocumentObject*> Objects = pcConstraint->References.getValues();
|
|
std::vector<std::string> SubElements = pcConstraint->References.getSubValues();
|
|
std::vector<std::string> dirStrings = pcConstraint->Direction.getSubValues();
|
|
QString dir;
|
|
if (!dirStrings.empty()) {
|
|
dir = makeRefText(pcConstraint->Direction.getValue(), dirStrings.front());
|
|
}
|
|
|
|
// Fill data into dialog elements
|
|
double f = pcConstraint->BoundaryValue.getValue();
|
|
ui->spinBoundaryValue->setMinimum(std::numeric_limits<float>::min()); // ZERO is not flexible
|
|
ui->spinBoundaryValue->setMaximum(std::numeric_limits<float>::max());
|
|
ui->spinBoundaryValue->setValue(f);
|
|
ui->listReferences->clear();
|
|
for (std::size_t i = 0; i < Objects.size(); i++) {
|
|
ui->listReferences->addItem(makeRefText(Objects[i], SubElements[i]));
|
|
}
|
|
if (!Objects.empty()) {
|
|
ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
ui->lineDirection->setText(dir.isEmpty() ? tr("") : dir);
|
|
ui->checkReverse->setVisible(true); // it is still useful to swap direction of an edge
|
|
|
|
ui->listReferences->blockSignals(false);
|
|
ui->spinBoundaryValue->blockSignals(false);
|
|
ui->buttonDirection->blockSignals(false);
|
|
ui->checkReverse->blockSignals(false);
|
|
|
|
updateUI();
|
|
}
|
|
|
|
const Fem::FemSolverObject* TaskFemConstraintFluidBoundary::getFemSolver() const
|
|
{
|
|
return pcSolver;
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::updateBoundaryTypeUI()
|
|
{
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
std::string boundaryType = ui->comboBoundaryType->currentText().toStdString();
|
|
// std::string boundaryType = pcConstraint->BoundaryType.getValueAsString();
|
|
|
|
// Update subtypes, any change here should be written back to FemConstraintFluidBoundary.cpp
|
|
if (boundaryType == "wall") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("velocity (m/s)"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
pcConstraint->Subtype.setEnums(WallSubtypes);
|
|
}
|
|
else if (boundaryType == "interface") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("value not needed"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
pcConstraint->Subtype.setEnums(InterfaceSubtypes);
|
|
}
|
|
else if (boundaryType == "freestream") {
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
ui->labelBoundaryValue->setText(QStringLiteral("value not needed"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
pcConstraint->Subtype.setEnums(FreestreamSubtypes);
|
|
}
|
|
else if (boundaryType == "inlet") {
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
pcConstraint->Subtype.setEnums(InletSubtypes);
|
|
ui->labelBoundaryValue->setText(QStringLiteral("Pressure [Pa]")); // default to pressure
|
|
pcConstraint->Reversed.setValue(true); // inlet must point into volume
|
|
}
|
|
else if (boundaryType == "outlet") {
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
pcConstraint->Subtype.setEnums(OutletSubtypes);
|
|
ui->labelBoundaryValue->setText(QStringLiteral("Pressure [Pa]"));
|
|
pcConstraint->Reversed.setValue(false); // outlet must point outward
|
|
}
|
|
else {
|
|
Base::Console().Error("Error: Fluid boundary type `%s` is not defined\n",
|
|
boundaryType.c_str());
|
|
}
|
|
// std::string subtypeLabel = boundaryType + std::string(" type");
|
|
// ui->labelSubtype->setText(QString::fromUtf8(subtypeLabel)); // too long to show in UI
|
|
ui->tabWidget->setCurrentIndex(0); // activate the basic pressure-momentum setting tab
|
|
|
|
std::vector<std::string> subtypes = pcConstraint->Subtype.getEnumVector();
|
|
initComboBox(ui->comboSubtype, subtypes, "default to the second subtype");
|
|
updateSubtypeUI();
|
|
}
|
|
|
|
|
|
void TaskFemConstraintFluidBoundary::updateSubtypeUI()
|
|
{
|
|
|
|
std::string boundaryType = ui->comboBoundaryType->currentText().toStdString();
|
|
std::string subtype = ui->comboSubtype->currentText().toStdString();
|
|
|
|
if (boundaryType == "inlet" || boundaryType == "outlet") {
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
if (subtype == "totalPressure" || subtype == "staticPressure") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("pressure [Pa]"));
|
|
ui->buttonDirection->setEnabled(false);
|
|
ui->lineDirection->setEnabled(false);
|
|
}
|
|
else if (subtype == "uniformVelocity") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("velocity [m/s]"));
|
|
ui->buttonDirection->setEnabled(true);
|
|
ui->lineDirection->setEnabled(true);
|
|
}
|
|
else if (subtype == "massFlowrate") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("flowrate [kg/s]"));
|
|
ui->buttonDirection->setEnabled(false);
|
|
ui->lineDirection->setEnabled(false);
|
|
}
|
|
else if (subtype == "volumetricFlowRate") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("flowrate [m3/s]"));
|
|
ui->buttonDirection->setEnabled(false);
|
|
ui->lineDirection->setEnabled(false);
|
|
}
|
|
else {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("unspecific"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
}
|
|
}
|
|
else if (boundaryType == "wall") {
|
|
if (subtype == "moving") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("moving speed (m/s)"));
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
ui->buttonDirection->setEnabled(false); // moving speed must be parallel to wall
|
|
ui->lineDirection->setEnabled(false);
|
|
}
|
|
else if (subtype == "slip") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("not needed"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
}
|
|
else if (subtype == "partialSlip") {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("slip ratio(0~1)"));
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
ui->buttonDirection->setEnabled(false);
|
|
ui->lineDirection->setEnabled(false);
|
|
}
|
|
else {
|
|
ui->labelBoundaryValue->setText(QStringLiteral("unspecific"));
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
}
|
|
}
|
|
else if (boundaryType == "interface") {
|
|
ui->tabBasicBoundary->setEnabled(false);
|
|
// show help text
|
|
int iInterface = ui->comboSubtype->currentIndex();
|
|
ui->labelHelpText->setText(tr(InterfaceSubtypeHelpTexts[iInterface]));
|
|
}
|
|
else if (boundaryType == "freestream") {
|
|
ui->tabBasicBoundary->setEnabled(true);
|
|
}
|
|
else {
|
|
Base::Console().Error("Fluid boundary type `%s` is not defined\n", boundaryType.c_str());
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::updateTurbulenceUI()
|
|
{
|
|
ui->labelHelpText->setText(
|
|
tr(TurbulenceSpecificationHelpTexts[ui->comboTurbulenceSpecification->currentIndex()]));
|
|
/// hide/disable UI only happened in constructor, update helptext and label text here
|
|
std::string turbulenceSpec = ui->comboTurbulenceSpecification->currentText().toStdString();
|
|
ui->labelTurbulentIntensityValue->setText(tr("Intensity [0~1]"));
|
|
if (turbulenceSpec == "intensity&DissipationRate") {
|
|
ui->labelTurbulentLengthValue->setText(tr("Dissipation Rate [m2/s3]"));
|
|
}
|
|
else if (turbulenceSpec == "intensity&LengthScale") {
|
|
ui->labelTurbulentLengthValue->setText(tr("Length Scale [m]"));
|
|
}
|
|
else if (turbulenceSpec == "intensity&ViscosityRatio") {
|
|
ui->labelTurbulentLengthValue->setText(tr("Viscosity Ratio [1]"));
|
|
}
|
|
else if (turbulenceSpec == "intensity&HydraulicDiameter") {
|
|
ui->labelTurbulentLengthValue->setText(tr("Hydraulic Diameter [m]"));
|
|
}
|
|
else {
|
|
Base::Console().Error("turbulence Spec type `%s` is not defined\n", turbulenceSpec.c_str());
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::updateThermalBoundaryUI()
|
|
{
|
|
// Fem::ConstraintFluidBoundary* pcConstraint =
|
|
// ConstraintView->getObject<Fem::ConstraintFluidBoundary>(); std::string
|
|
// thermalBoundaryType = pcConstraint->ThermalBoundaryType.getValueAsString();
|
|
|
|
ui->labelHelpText->setText(
|
|
tr(ThermalBoundaryHelpTexts[ui->comboThermalBoundaryType->currentIndex()]));
|
|
// to hide/disable UI according to subtype
|
|
std::string thermalBoundaryType = ui->comboThermalBoundaryType->currentText().toStdString();
|
|
ui->spinHTCoeffValue->setEnabled(false);
|
|
ui->spinTemperatureValue->setEnabled(false);
|
|
ui->spinHeatFluxValue->setEnabled(false);
|
|
if (thermalBoundaryType == "zeroGradient" || thermalBoundaryType == "coupled") {
|
|
return;
|
|
}
|
|
else if (thermalBoundaryType == "fixedValue") {
|
|
ui->spinTemperatureValue->setEnabled(true);
|
|
}
|
|
else if (thermalBoundaryType == "fixedGradient") {
|
|
ui->spinHeatFluxValue->setEnabled(true);
|
|
ui->labelHeatFlux->setText(tr("Gradient [K/m]"));
|
|
}
|
|
else if (thermalBoundaryType == "mixed") {
|
|
ui->spinTemperatureValue->setEnabled(true);
|
|
ui->spinHeatFluxValue->setEnabled(true);
|
|
ui->labelHeatFlux->setText(tr("Gradient [K/m]"));
|
|
}
|
|
else if (thermalBoundaryType == "heatFlux") {
|
|
ui->spinHeatFluxValue->setEnabled(true);
|
|
ui->labelHeatFlux->setText(tr("Flux [W/m2]"));
|
|
}
|
|
else if (thermalBoundaryType == "HTC") {
|
|
ui->spinHTCoeffValue->setEnabled(true);
|
|
ui->spinTemperatureValue->setEnabled(true);
|
|
}
|
|
else {
|
|
Base::Console().Error("Thermal boundary type `%s` is not defined\n",
|
|
thermalBoundaryType.c_str());
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onBoundaryTypeChanged()
|
|
{
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
// temporarily change BoundaryType property, but command transaction should reset it back if you
|
|
// 'reject' late
|
|
pcConstraint->BoundaryType.setValue(ui->comboBoundaryType->currentIndex());
|
|
updateBoundaryTypeUI();
|
|
|
|
ConstraintView->updateData(&pcConstraint->BoundaryType); // force a 3D redraw
|
|
|
|
// update view provider once BoundaryType changed, updateData() may be just enough
|
|
// FreeCAD.getDocument(pcConstraint->Document.getName()).recompute();
|
|
bool ret = pcConstraint->recomputeFeature();
|
|
if (!ret) {
|
|
std::string boundaryType = ui->comboBoundaryType->currentText().toStdString();
|
|
Base::Console().Error("Fluid boundary recomputationg failed for boundaryType `%s` \n",
|
|
boundaryType.c_str());
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onSubtypeChanged()
|
|
{
|
|
updateSubtypeUI(); // todo: change color for different kind of subtype,
|
|
// Fem::ConstraintFluidBoundary::onChanged() and viewProvider
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onBoundaryValueChanged(double)
|
|
{
|
|
// left empty for future extension
|
|
}
|
|
void TaskFemConstraintFluidBoundary::onTurbulenceSpecificationChanged()
|
|
{
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
pcConstraint->TurbulenceSpecification.setValue(
|
|
ui->comboTurbulenceSpecification->currentIndex());
|
|
updateTurbulenceUI();
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onThermalBoundaryTypeChanged()
|
|
{
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
pcConstraint->ThermalBoundaryType.setValue(ui->comboThermalBoundaryType->currentIndex());
|
|
updateThermalBoundaryUI();
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onReferenceDeleted()
|
|
{
|
|
TaskFemConstraintFluidBoundary::removeFromSelection(); // On right-click face is automatically
|
|
// selected, so just remove
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onButtonDirection(const bool pressed)
|
|
{
|
|
// sets the normal vector of the currently selecteed planar face as direction
|
|
|
|
Q_UNUSED(pressed);
|
|
|
|
clearButtons(SelectionChangeModes::none);
|
|
|
|
// get vector of selected objects of active document
|
|
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
|
|
if (selection.empty()) {
|
|
QMessageBox::warning(this, tr("Empty selection"), tr("Select an edge or a face, please."));
|
|
return;
|
|
}
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
|
|
// we only handle the first selected object
|
|
Gui::SelectionObject& selectionElement = selection.at(0);
|
|
|
|
// we can only handle part objects
|
|
if (!selectionElement.isObjectTypeOf(Part::Feature::getClassTypeId())) {
|
|
QMessageBox::warning(this,
|
|
tr("Wrong selection"),
|
|
tr("Selected object is not a part object!"));
|
|
return;
|
|
}
|
|
// get the names of the subobjects
|
|
const std::vector<std::string>& subNames = selectionElement.getSubNames();
|
|
|
|
if (subNames.size() != 1) {
|
|
QMessageBox::warning(this,
|
|
tr("Wrong selection"),
|
|
tr("Only one planar face or edge can be selected!"));
|
|
return;
|
|
}
|
|
|
|
// we are now sure we only have one object
|
|
std::string subNamesElement = subNames[0];
|
|
// vector for the direction
|
|
std::vector<std::string> direction(1, subNamesElement);
|
|
|
|
Part::Feature* feat = static_cast<Part::Feature*>(selectionElement.getObject());
|
|
TopoDS_Shape ref = feat->Shape.getShape().getSubShape(subNamesElement.c_str());
|
|
|
|
if (subNamesElement.substr(0, 4) == "Face") {
|
|
if (!Fem::Tools::isPlanar(TopoDS::Face(ref))) {
|
|
QMessageBox::warning(this,
|
|
tr("Wrong selection"),
|
|
tr("Only planar faces can be picked for 3D"));
|
|
return;
|
|
}
|
|
}
|
|
else if (subNamesElement.substr(0, 4) == "Edge") { // 2D or 3D can use edge as direction vector
|
|
if (!Fem::Tools::isLinear(TopoDS::Edge(ref))) {
|
|
QMessageBox::warning(this,
|
|
tr("Wrong selection"),
|
|
tr("Only planar edges can be picked for 2D"));
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
QMessageBox::warning(this,
|
|
tr("Wrong selection"),
|
|
tr("Only faces for 3D part or edges for 2D can be picked"));
|
|
return;
|
|
}
|
|
|
|
// update the direction
|
|
pcConstraint->Direction.setValue(feat, direction);
|
|
ui->lineDirection->setText(makeRefText(feat, subNamesElement));
|
|
|
|
// Update UI
|
|
updateUI();
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::onCheckReverse(const bool pressed)
|
|
{
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
pcConstraint->Reversed.setValue(pressed);
|
|
}
|
|
|
|
std::string TaskFemConstraintFluidBoundary::getBoundaryType() const
|
|
{
|
|
return ui->comboBoundaryType->currentText().toStdString();
|
|
}
|
|
|
|
std::string TaskFemConstraintFluidBoundary::getSubtype() const
|
|
{
|
|
return ui->comboSubtype->currentText().toStdString();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getBoundaryValue() const
|
|
{
|
|
return ui->spinBoundaryValue->value();
|
|
}
|
|
|
|
|
|
std::string TaskFemConstraintFluidBoundary::getTurbulenceModel() const
|
|
{
|
|
if (pTurbulenceModel) {
|
|
return pTurbulenceModel->getValueAsString();
|
|
}
|
|
else {
|
|
return "laminar";
|
|
}
|
|
}
|
|
|
|
std::string TaskFemConstraintFluidBoundary::getTurbulenceSpecification() const
|
|
{
|
|
return ui->comboTurbulenceSpecification->currentText().toStdString();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getTurbulentIntensityValue() const
|
|
{
|
|
return ui->spinTurbulentIntensityValue->value();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getTurbulentLengthValue() const
|
|
{
|
|
return ui->spinTurbulentLengthValue->value();
|
|
}
|
|
|
|
bool TaskFemConstraintFluidBoundary::getHeatTransferring() const
|
|
{
|
|
if (pHeatTransferring) {
|
|
return pHeatTransferring->getValue();
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string TaskFemConstraintFluidBoundary::getThermalBoundaryType() const
|
|
{
|
|
return ui->comboThermalBoundaryType->currentText().toStdString();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getTemperatureValue() const
|
|
{
|
|
return ui->spinTemperatureValue->value();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getHeatFluxValue() const
|
|
{
|
|
return ui->spinHeatFluxValue->value();
|
|
}
|
|
|
|
double TaskFemConstraintFluidBoundary::getHTCoeffValue() const
|
|
{
|
|
return ui->spinHTCoeffValue->value();
|
|
}
|
|
|
|
const std::string TaskFemConstraintFluidBoundary::getReferences() const
|
|
{
|
|
int rows = ui->listReferences->model()->rowCount();
|
|
|
|
std::vector<std::string> items;
|
|
for (int r = 0; r < rows; r++) {
|
|
items.push_back(ui->listReferences->item(r)->text().toStdString());
|
|
}
|
|
return TaskFemConstraint::getReferences(items);
|
|
}
|
|
|
|
const std::string TaskFemConstraintFluidBoundary::getDirectionName() const
|
|
{
|
|
std::string dir = ui->lineDirection->text().toStdString();
|
|
if (dir.empty()) {
|
|
return "";
|
|
}
|
|
|
|
int pos = dir.find_last_of(":");
|
|
return dir.substr(0, pos).c_str();
|
|
}
|
|
|
|
const std::string TaskFemConstraintFluidBoundary::getDirectionObject() const
|
|
{
|
|
std::string dir = ui->lineDirection->text().toStdString();
|
|
if (dir.empty()) {
|
|
return "";
|
|
}
|
|
|
|
int pos = dir.find_last_of(":");
|
|
return dir.substr(pos + 1).c_str();
|
|
}
|
|
|
|
bool TaskFemConstraintFluidBoundary::getReverse() const
|
|
{
|
|
return ui->checkReverse->isChecked();
|
|
}
|
|
|
|
TaskFemConstraintFluidBoundary::~TaskFemConstraintFluidBoundary() = default;
|
|
|
|
void TaskFemConstraintFluidBoundary::addToSelection()
|
|
{
|
|
std::vector<Gui::SelectionObject> selection =
|
|
Gui::Selection().getSelectionEx(); // gets vector of selected objects of active document
|
|
if (selection.empty()) {
|
|
QMessageBox::warning(this, tr("Selection error"), tr("Nothing selected!"));
|
|
return;
|
|
}
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
std::vector<App::DocumentObject*> Objects = pcConstraint->References.getValues();
|
|
std::vector<std::string> SubElements = pcConstraint->References.getSubValues();
|
|
|
|
for (auto& it : selection) { // for every selected object
|
|
if (!it.isObjectTypeOf(Part::Feature::getClassTypeId())) {
|
|
QMessageBox::warning(this, tr("Selection error"), tr("Selected object is not a part!"));
|
|
return;
|
|
}
|
|
const std::vector<std::string>& subNames = it.getSubNames();
|
|
App::DocumentObject* obj = it.getObject();
|
|
for (const auto& subName : subNames) { // for every selected sub element
|
|
bool addMe = true;
|
|
for (auto itr = std::ranges::find(SubElements, subName); itr != SubElements.end();
|
|
itr = std::find(++itr,
|
|
SubElements.end(),
|
|
subName)) { // for every sub element in selection that
|
|
// matches one in old list
|
|
if (obj
|
|
== Objects[std::distance(
|
|
SubElements.begin(),
|
|
itr)]) { // if selected sub element's object equals the one in old list
|
|
// then it was added before so don't add
|
|
addMe = false;
|
|
}
|
|
}
|
|
// limit constraint such that only vertexes or faces or edges can be used depending on
|
|
// what was selected first
|
|
std::string searchStr;
|
|
if (subName.find("Vertex") != std::string::npos) {
|
|
searchStr = "Vertex";
|
|
}
|
|
else if (subName.find("Edge") != std::string::npos) {
|
|
searchStr = "Edge";
|
|
}
|
|
else {
|
|
searchStr = "Face";
|
|
}
|
|
|
|
for (const auto& SubElement : SubElements) {
|
|
if (SubElement.find(searchStr) == std::string::npos) {
|
|
QString msg = tr("Only one type of selection (vertex, face or edge) per "
|
|
"analysis feature allowed!");
|
|
QMessageBox::warning(this, tr("Selection error"), msg);
|
|
addMe = false;
|
|
break;
|
|
}
|
|
}
|
|
if (addMe) {
|
|
QSignalBlocker block(ui->listReferences);
|
|
Objects.push_back(obj);
|
|
SubElements.push_back(subName);
|
|
ui->listReferences->addItem(makeRefText(obj, subName));
|
|
}
|
|
}
|
|
}
|
|
// Update UI
|
|
pcConstraint->References.setValues(Objects, SubElements);
|
|
updateUI();
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::removeFromSelection()
|
|
{
|
|
std::vector<Gui::SelectionObject> selection =
|
|
Gui::Selection().getSelectionEx(); // gets vector of selected objects of active document
|
|
if (selection.empty()) {
|
|
QMessageBox::warning(this, tr("Selection error"), tr("Nothing selected!"));
|
|
return;
|
|
}
|
|
Fem::ConstraintFluidBoundary* pcConstraint =
|
|
ConstraintView->getObject<Fem::ConstraintFluidBoundary>();
|
|
std::vector<App::DocumentObject*> Objects = pcConstraint->References.getValues();
|
|
std::vector<std::string> SubElements = pcConstraint->References.getSubValues();
|
|
std::vector<size_t> itemsToDel;
|
|
for (const auto& it : selection) { // for every selected object
|
|
if (!it.isObjectTypeOf(Part::Feature::getClassTypeId())) {
|
|
QMessageBox::warning(this, tr("Selection error"), tr("Selected object is not a part!"));
|
|
return;
|
|
}
|
|
const std::vector<std::string>& subNames = it.getSubNames();
|
|
const App::DocumentObject* obj = it.getObject();
|
|
|
|
for (const auto& subName : subNames) { // for every selected sub element
|
|
for (auto itr = std::ranges::find(SubElements, subName); itr != SubElements.end();
|
|
itr = std::find(++itr,
|
|
SubElements.end(),
|
|
subName)) { // for every sub element in selection that
|
|
// matches one in old list
|
|
if (obj
|
|
== Objects[std::distance(
|
|
SubElements.begin(),
|
|
itr)]) { // if selected sub element's object equals the one in old list
|
|
// then it was added before so mark for deletion
|
|
itemsToDel.push_back(std::distance(SubElements.begin(), itr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
std::sort(itemsToDel.begin(), itemsToDel.end());
|
|
while (!itemsToDel.empty()) {
|
|
Objects.erase(Objects.begin() + itemsToDel.back());
|
|
SubElements.erase(SubElements.begin() + itemsToDel.back());
|
|
itemsToDel.pop_back();
|
|
}
|
|
// Update UI
|
|
{
|
|
QSignalBlocker block(ui->listReferences);
|
|
ui->listReferences->clear();
|
|
for (size_t j = 0; j < Objects.size(); j++) {
|
|
ui->listReferences->addItem(makeRefText(Objects[j], SubElements[j]));
|
|
}
|
|
}
|
|
pcConstraint->References.setValues(Objects, SubElements);
|
|
updateUI();
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::updateUI()
|
|
{
|
|
if (ui->listReferences->model()->rowCount() == 0) {
|
|
// Go into reference selection mode if no reference has been selected yet
|
|
onButtonReference(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::changeEvent(QEvent* e)
|
|
{
|
|
TaskBox::changeEvent(e);
|
|
if (e->type() == QEvent::LanguageChange) {
|
|
ui->spinBoundaryValue->blockSignals(true);
|
|
// more ui widget? those UI are does not support tr yet!
|
|
ui->retranslateUi(proxy);
|
|
|
|
ui->spinBoundaryValue->blockSignals(false);
|
|
}
|
|
}
|
|
|
|
void TaskFemConstraintFluidBoundary::clearButtons(const SelectionChangeModes notThis)
|
|
{
|
|
if (notThis != SelectionChangeModes::refAdd) {
|
|
ui->btnAdd->setChecked(false);
|
|
}
|
|
if (notThis != SelectionChangeModes::refRemove) {
|
|
ui->btnRemove->setChecked(false);
|
|
}
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// TaskDialog
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TaskDlgFemConstraintFluidBoundary::TaskDlgFemConstraintFluidBoundary(
|
|
ViewProviderFemConstraintFluidBoundary* ConstraintView)
|
|
{
|
|
this->ConstraintView = ConstraintView;
|
|
assert(ConstraintView);
|
|
this->parameter = new TaskFemConstraintFluidBoundary(ConstraintView);
|
|
|
|
Content.push_back(parameter);
|
|
}
|
|
|
|
//==== calls from the TaskView ===============================================================
|
|
|
|
bool TaskDlgFemConstraintFluidBoundary::accept()
|
|
{
|
|
std::string name = ConstraintView->getObject()->getNameInDocument();
|
|
const TaskFemConstraintFluidBoundary* boundary =
|
|
static_cast<const TaskFemConstraintFluidBoundary*>(parameter);
|
|
|
|
// no need to backup pcConstraint object content, if rejected, content can be recovered by
|
|
// transaction manager
|
|
try {
|
|
// Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Fluid boundary condition
|
|
// changed"));
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.BoundaryType = '%s'",
|
|
name.c_str(),
|
|
boundary->getBoundaryType().c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.Subtype = '%s'",
|
|
name.c_str(),
|
|
boundary->getSubtype().c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.BoundaryValue = %f",
|
|
name.c_str(),
|
|
boundary->getBoundaryValue());
|
|
|
|
std::string dirname = boundary->getDirectionName().data();
|
|
std::string dirobj = boundary->getDirectionObject().data();
|
|
|
|
if (!dirname.empty()) {
|
|
QString buf = QStringLiteral("(App.ActiveDocument.%1,[\"%2\"])");
|
|
buf = buf.arg(QString::fromStdString(dirname));
|
|
buf = buf.arg(QString::fromStdString(dirobj));
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.Direction = %s",
|
|
name.c_str(),
|
|
buf.toStdString().c_str());
|
|
}
|
|
else {
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.Direction = None",
|
|
name.c_str());
|
|
}
|
|
// Reverse control is done at BoundaryType selection, this UI is hidden from user
|
|
// Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %s",
|
|
// name.c_str(), boundary->getReverse() ? "True" : "False");
|
|
|
|
// solver specific setting, physical model selection
|
|
const Fem::FemSolverObject* pcSolver = boundary->getFemSolver();
|
|
|
|
if (pcSolver) {
|
|
App::PropertyBool* pHeatTransferring = nullptr;
|
|
App::PropertyEnumeration* pTurbulenceModel = nullptr;
|
|
pHeatTransferring =
|
|
static_cast<App::PropertyBool*>(pcSolver->getPropertyByName("HeatTransferring"));
|
|
pTurbulenceModel = static_cast<App::PropertyEnumeration*>(
|
|
pcSolver->getPropertyByName("TurbulenceModel"));
|
|
|
|
if (pHeatTransferring && pHeatTransferring->getValue()) {
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.ThermalBoundaryType = '%s'",
|
|
name.c_str(),
|
|
boundary->getThermalBoundaryType().c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.TemperatureValue = %f",
|
|
name.c_str(),
|
|
boundary->getTemperatureValue());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.HeatFluxValue = %f",
|
|
name.c_str(),
|
|
boundary->getHeatFluxValue());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.HTCoeffValue = %f",
|
|
name.c_str(),
|
|
boundary->getHTCoeffValue());
|
|
}
|
|
if (pTurbulenceModel
|
|
&& std::string(pTurbulenceModel->getValueAsString())
|
|
!= "laminar") { // Invisic and DNS flow also does not need this
|
|
// update turbulence and thermal boundary settings, only if those models are
|
|
// activated
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.TurbulenceSpecification = '%s'",
|
|
name.c_str(),
|
|
boundary->getTurbulenceSpecification().c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.TurbulentIntensityValue = %f",
|
|
name.c_str(),
|
|
boundary->getTurbulentIntensityValue());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.ActiveDocument.%s.TurbulentLengthValue = %f",
|
|
name.c_str(),
|
|
boundary->getTurbulentLengthValue());
|
|
}
|
|
}
|
|
else {
|
|
Base::Console().Warning("FemSolverObject is not found in the FemAnalysis object, "
|
|
"thermal and turbulence setting is not accepted\n");
|
|
}
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::warning(parameter, tr("Input error"), QString::fromLatin1(e.what()));
|
|
return false;
|
|
}
|
|
|
|
return TaskDlgFemConstraint::accept();
|
|
}
|
|
|
|
#include "moc_TaskFemConstraintFluidBoundary.cpp"
|