From a9c97f1293cdbb7bdb21b213ac75cfa283278d41 Mon Sep 17 00:00:00 2001 From: qingfengxia Date: Thu, 16 Feb 2017 07:54:16 +0100 Subject: [PATCH] FEM: VTK post processing, changes: - vtk result file reading - some changes in vtk post processing pipe line --- src/Mod/Fem/App/AppFemPy.cpp | 14 +- src/Mod/Fem/App/FemPostPipeline.cpp | 12 +- src/Mod/Fem/App/FemVTKTools.cpp | 551 +++++++++++++++------------- src/Mod/Fem/App/FemVTKTools.h | 8 +- src/Mod/Fem/Gui/Command.cpp | 22 +- 5 files changed, 332 insertions(+), 275 deletions(-) diff --git a/src/Mod/Fem/App/AppFemPy.cpp b/src/Mod/Fem/App/AppFemPy.cpp index 8c87238ded..5f6d3d429e 100644 --- a/src/Mod/Fem/App/AppFemPy.cpp +++ b/src/Mod/Fem/App/AppFemPy.cpp @@ -95,8 +95,8 @@ public: "Read a mesh from a file and returns a Mesh object." ); #ifdef FC_USE_VTK - add_varargs_method("readCfdResult",&Module::readCfdResult, - "Read a CFD result from a file (file format detected from file suffix)" + add_varargs_method("readResult",&Module::readResult, + "Read a CFD or Mechanical result (auto detect) from a file (file format detected from file suffix)" ); add_varargs_method("writeResult",&Module::writeResult, "write a CFD or FEM result (auto detect) to a file (file format detected from file suffix)" @@ -247,7 +247,7 @@ private: } #ifdef FC_USE_VTK - Py::Object readCfdResult(const Py::Tuple& args) + Py::Object readResult(const Py::Tuple& args) { char* fileName = NULL; char* objName = NULL; @@ -263,11 +263,11 @@ private: { App::Document* pcDoc = App::GetApplication().getActiveDocument(); App::DocumentObject* obj = pcDoc->getObject(resName.c_str()); - FemVTKTools::readFluidicResult(EncodedName.c_str(), obj); + FemVTKTools::readResult(EncodedName.c_str(), obj); } else - FemVTKTools::readFluidicResult(EncodedName.c_str()); //assuming activeObject can hold Result - + FemVTKTools::readResult(EncodedName.c_str()); //assuming activeObject can hold Result + return Py::None(); } @@ -291,7 +291,7 @@ private: } else FemVTKTools::writeResult(EncodedName.c_str()); - + return Py::None(); } #endif diff --git a/src/Mod/Fem/App/FemPostPipeline.cpp b/src/Mod/Fem/App/FemPostPipeline.cpp index 8451ac67f0..3bb9cab461 100644 --- a/src/Mod/Fem/App/FemPostPipeline.cpp +++ b/src/Mod/Fem/App/FemPostPipeline.cpp @@ -116,11 +116,12 @@ DocumentObjectExecReturn* FemPostPipeline::execute(void) { bool FemPostPipeline::canRead(Base::FileInfo File) { if (File.hasExtension("vtk") || + // from FemResult only unstructural mesh is supported in femvtktoools.cpp File.hasExtension("vtp") || File.hasExtension("vts") || File.hasExtension("vtr") || - File.hasExtension("vtu") || - File.hasExtension("vti")) + File.hasExtension("vti") || + File.hasExtension("vtu")) return true; return false; @@ -244,9 +245,10 @@ bool FemPostPipeline::holdsPostObject(FemPostObject* obj) { } void FemPostPipeline::load(FemResultObject* res) { - - if(!res->Mesh.getValue() || !res->Mesh.getValue()->isDerivedFrom(Fem::FemMeshObject::getClassTypeId())) + if(!res->Mesh.getValue() || !res->Mesh.getValue()->isDerivedFrom(Fem::FemMeshObject::getClassTypeId())) { + Base::Console().Warning("Mesh of result object is empty or not derived from Fem::FemMeshObject\n"); return; + } //first copy the mesh over //######################## @@ -256,7 +258,7 @@ void FemPostPipeline::load(FemResultObject* res) { //Now copy the point data over //############################ - if(res->getPropertyByName("Velocity")){ + if(res->getPropertyByName("Velocity")){ // consider better way to detect result type, res->Type == "CfdResult" FemVTKTools::exportFluidicResult(res, grid); } else{ diff --git a/src/Mod/Fem/App/FemVTKTools.cpp b/src/Mod/Fem/App/FemVTKTools.cpp index 0f06ed00b7..d1c413fb35 100644 --- a/src/Mod/Fem/App/FemVTKTools.cpp +++ b/src/Mod/Fem/App/FemVTKTools.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2009 * + * Copyright (c) Qingfeng Xia (qingfeng.xia at oxford uni) 2017 * * * * This file is part of the FreeCAD CAx development system. * * * @@ -27,6 +28,7 @@ # include # include # include +# include # include # include @@ -110,12 +112,7 @@ template void writeVTKFile(const char* filename, vtkSmartPointer< writer->Write(); } -/* - double scale = 1000; - p[0] = p[0]* scale; // scale back to mm - p[1] = p[1]* scale; - p[1] = p[1]* scale; - */ + void FemVTKTools::importVTKMesh(vtkSmartPointer dataset, FemMesh* mesh, float scale) { const vtkIdType nPoints = dataset->GetNumberOfPoints(); @@ -387,7 +384,7 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo } -void FemVTKTools::exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid) +void FemVTKTools::exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid, float scale) { SMESH_Mesh* smesh = const_cast(mesh->getSMesh()); @@ -401,7 +398,7 @@ void FemVTKTools::exportVTKMesh(const FemMesh* mesh, vtkSmartPointerSetNumberOfPoints(info.NbNodes()); for(; aNodeIter->more(); ) { const SMDS_MeshNode* node = aNodeIter->next(); // why float, not double? - double coords[3] = {double(node->X()), double(node->Y()), double(node->Z())}; + double coords[3] = {double(node->X()*scale), double(node->Y()*scale), double(node->Z()*scale)}; points->SetPoint(node->GetID()-1, coords); } grid->SetPoints(points); @@ -489,7 +486,7 @@ App::DocumentObject* createObjectByType(const Base::Type type) } -App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::DocumentObject* res) +App::DocumentObject* FemVTKTools::readResult(const char* filename, App::DocumentObject* res) { Base::TimeInfo Start; Base::Console().Log("Start: read FemResult with FemMesh from VTK file ======================\n"); @@ -502,6 +499,7 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D { scale = 1000.0; // convert from meter in length of CFD result file } + vtkSmartPointer ds; if(f.hasExtension("vtu")) { @@ -531,7 +529,7 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D else { Base::Console().Log("FemResultObject pointer is NULL, trying to get the active object\n"); - if (obj->getTypeId() == Base::Type::fromName("Fem::FemResultObjectPython")) + if(obj->getTypeId() == Base::Type::fromName("Fem::FemResultObject")) result = obj; else { @@ -546,10 +544,23 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D static_cast(mesh->getPropertyByName("FemMesh"))->setValue(*fmesh); static_cast(result->getPropertyByName("Mesh"))->setValue(mesh); // PropertyLink is the property type to store DocumentObject pointer - - importFluidicResult(dataset, result); + + vtkSmartPointer pd = dataset->GetPointData(); + vtkSmartPointer displ = pd->GetArray("Displacement"); // name in vtk file, not the property name + vtkSmartPointer vel = pd->GetArray("U"); // name in vtk file, not the property name + if(vel) + { + importFluidicResult(dataset, result); + } + else if (displ) + { + importMechanicalResult(dataset, result); + } + else + { + Base::Console().Error("FemResult type, fluidic (array name of `U`) or mechanical (array name of `Displacement`) can not be detected\n"); + } pcDoc->recompute(); - Base::Console().Log(" %f: Done \n", Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); return result; @@ -565,29 +576,38 @@ void FemVTKTools::writeResult(const char* filename, const App::DocumentObject* r Base::Console().Message("No active document is found thus do nothing and return\n"); return; } - res = pcDoc->getActiveObject(); //type checking + res = pcDoc->getActiveObject(); //type checking is done by caller } if(!res) { Base::Console().Error("Result object pointer is invalid and it is not active oject"); return; } + auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Units"); + int unitSchema = hGrp->GetInt("UserSchema",0); + float scale = 1.0; + if(unitSchema == 0) // standard mm + { + scale = 0.001; // convert from mm in FreeCAD to SI length in result file + } + Base::TimeInfo Start; - Base::Console().Log("Start: write FemResult or CfdResult to VTK unstructuredGrid dataset =======\n"); + Base::Console().Message("Start: write FemResult or CfdResult to VTK unstructuredGrid dataset =======\n"); Base::FileInfo f(filename); vtkSmartPointer grid = vtkSmartPointer::New(); App::DocumentObject* mesh = static_cast(res->getPropertyByName("Mesh"))->getValue(); const FemMesh& fmesh = static_cast(mesh->getPropertyByName("FemMesh"))->getValue(); - FemVTKTools::exportVTKMesh(&fmesh, grid); + FemVTKTools::exportVTKMesh(&fmesh, grid, scale); - if(res->getPropertyByName("Velocity")){ + if(res->getPropertyByName("Velocity")){ // consider better way to detect result type, res->Type == "CfdResult" FemVTKTools::exportFluidicResult(res, grid); } else if(res->getPropertyByName("DisplacementVectors")){ FemVTKTools::exportMechanicalResult(res, grid); } else{ + Base::Console().Error("Result type can not be detected from unique property name like Velocity or DisplacementVectors\n"); return; } @@ -602,61 +622,19 @@ void FemVTKTools::writeResult(const char* filename, const App::DocumentObject* r Base::Console().Error("file name extension is not supported to write VTK\n"); } - Base::Console().Log(" %f: Done \n",Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); + Base::Console().Message(" %f: result writing is done \n",Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); } - -void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App::DocumentObject* res) { - - // velocity and pressure are essential, Temperature is optional, so are turbulence related variables - std::map vars; // varable name defined in openfoam -> property defined in CfdResult.py - vars["Pressure"] = "p"; - vars["Velocity"] = "U"; - vars["Temperature"] = "T"; - vars["TurbulenceThermalDiffusivity"] = "alphat"; - vars["TurbulenceViscosity"] = "nut"; - vars["TurbulenceEnergy"] = "k"; - vars["TurbulenceDissipationRate"] = "epsilon"; - vars["TurbulenceSpecificDissipation"] = "omega"; - - const int max_var_index = 11; - std::vector stats(3*max_var_index, 0.0); - - std::map varids; // must agree with definition in _TaskPanelCfdResult.py - varids["Ux"] = 0; - varids["Uy"] = 1; - varids["Uz"] = 2; - varids["Umag"] = 3; - varids["Pressure"] = 4; - varids["Temperature"] = 5; - varids["TurbulenceEnergy"] = 6; - varids["TurbulenceViscosity"] = 7; - varids["TurbulenceDissipationRate"] = 8; - //varids["TurbulenceThermalDiffusivity"] = 9; - //varids["TurbulenceSpecificDissipation"] = 10; - - double ts = 0.0; // t=0.0 for static simulation - static_cast(res->getPropertyByName("Time"))->setValue(ts); - - vtkSmartPointer pd = dataset->GetPointData(); - const vtkIdType nPoints = dataset->GetNumberOfPoints(); - if(pd->GetNumberOfArrays() == 0) { - Base::Console().Error("No point data array is found in vtk data set, do nothin\n"); - // if pointData is empty, data may be in cellDate, cellData -> pointData interpolation is possible in VTK - return; - } - - std::vector nodeIds(nPoints); - vtkSmartPointer vel = pd->GetArray(vars["Velocity"]); - if(nPoints && vel && vel->GetNumberOfComponents() == 3) { - std::vector vec(nPoints); - double vmin=1.0e100, vmean=0.0, vmax=0.0; +// it is an internal utility func to avoid code duplication +void _calcStat(const std::vector& vel, std::vector& stats) { + vtkIdType nPoints = vel.size(); + double vmin=1.0e100, vmean=0.0, vmax=-1.0e100; //stat of Vx, Vy, Vz is not necessary - double vmins[3] = {0.0, 0.0, 0.0}; + double vmins[3] = {1.0e100, 1.0e100, 1.0e100}; // set up a very big positive float then reduce it double vmeans[3] = {0.0, 0.0, 0.0}; - double vmaxs[3] = {0.0, 0.0, 0.0}; - for(vtkIdType i=0; iGetTuple(i); // both vtkFloatArray and vtkDoubleArray return double* for GetTuple(i) + double vmaxs[3] = {-1.0e100, -1.0e100, -1.0e100}; + for(std::vector::const_iterator it=vel.begin(); it!=vel.end(); ++it) { + double p[] = {it->x, it->y, it->z}; double vmag = std::sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]); for(int ii=0; ii<3; ii++) { vmeans[ii] += p[ii]; @@ -666,9 +644,6 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: vmean += vmag; if(vmag > vmax) vmax = vmag; if(vmag < vmin) vmin = vmag; - - vec[i] = (Base::Vector3d(p[0], p[1], p[2])); - nodeIds[i] = i; } for(int ii=0; ii<3; ii++) { @@ -676,41 +651,99 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: stats[ii*3 + 2] = vmaxs[ii]; stats[ii*3 + 1] = vmeans[ii]/nPoints; } - int index = varids["Umag"]; + int index = 3; // velocity mag or displacement mag stats[index*3] = vmin; stats[index*3 + 2] = vmax; stats[index*3 + 1] = vmean/nPoints; +} - App::PropertyVectorList* velocity = static_cast(res->getPropertyByName("Velocity")); - if(velocity) { - //PropertyVectorList will not show up in PropertyEditor - velocity->setValues(vec); - static_cast(res->getPropertyByName("NodeNumbers"))->setValues(nodeIds); - Base::Console().Message("Velocity field has been loaded \n"); - } - else - Base::Console().Error("Velocity property is not found in Cfd Result object \n"); - } - else { - Base::Console().Error("Velocity field is not found in Cfd Result vtk file \n"); +void _importResult(const vtkSmartPointer dataset, App::DocumentObject* res, + const std::map& vectors, const std::map scalers, + const std::map varids, const std::string& essential_property){ + const int max_var_index = 11; + // all code below can be shared! + std::vector stats(3*max_var_index, 0.0); + + double ts = 0.0; // t=0.0 for static simulation + static_cast(res->getPropertyByName("Time"))->setValue(ts); + + vtkSmartPointer pd = dataset->GetPointData(); + const vtkIdType nPoints = dataset->GetNumberOfPoints(); + if(pd->GetNumberOfArrays() == 0) { + Base::Console().Error("No point data array is found in vtk data set, do nothing\n"); + // if pointData is empty, data may be in cellDate, cellData -> pointData interpolation is possible in VTK return; } - for(auto const& kv: vars){ - if (std::string(kv.first) == std::string("Velocity")) - continue; - vtkDataArray* vec = vtkDataArray::SafeDownCast(pd->GetArray(kv.second)); + auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Units"); + int unitSchema = hGrp->GetInt("UserSchema",0); + float scale = 1.0; + if(unitSchema == 0) // standard mm + { + scale = 1000; // convert from SI length in result file to mm in FreeCAD + } + + const char* essential_var = vectors.at(essential_property).c_str(); + vtkSmartPointer essential_array = pd->GetArray(essential_var); // a vector must exist + if(nPoints && essential_array) { + int dim = 3; // Fixme: currently 3D only + for(auto const& kv: vectors){ + vtkDataArray* vector_field = vtkDataArray::SafeDownCast(pd->GetArray(kv.second.c_str())); + if(!vector_field) + vector_field = vtkDataArray::SafeDownCast(pd->GetArray(kv.first.c_str())); // name from FreeCAD export + if(vector_field && vector_field->GetNumberOfComponents() == dim) { + App::PropertyVectorList* vector_list = static_cast(res->getPropertyByName(kv.first.c_str())); + if(vector_list) { + std::vector vec(nPoints); + if(kv.first == std::string(essential_property)) { // is there any other var need to scale? + for(vtkIdType i=0; iGetTuple(i); // both vtkFloatArray and vtkDoubleArray return double* for GetTuple(i) + vec[i] = (Base::Vector3d(p[0]*scale, p[1]*scale, p[2]*scale)); + } + } + else{ + for(vtkIdType i=0; iGetTuple(i); // both vtkFloatArray and vtkDoubleArray return double* for GetTuple(i) + vec[i] = (Base::Vector3d(p[0], p[1], p[2])); + } + } + if (kv.first == std::string(essential_property)) // for displacement or velocity calc min and max of each components + _calcStat(vec, stats); + //PropertyVectorList will not show up in PropertyEditor + vector_list->setValues(vec); + Base::Console().Message("PropertyVectorList %s has been loaded \n", kv.first.c_str()); + } + else { + Base::Console().Error("static_cast((res->getPropertyByName(\"%s\")) failed \n", kv.first.c_str()); + continue; + } + } + + std::vector nodeIds(nPoints); + for(vtkIdType i=0; i(res->getPropertyByName("NodeNumbers"))->setValues(nodeIds); + } + } + else{ + Base::Console().Error("essential_property %s corresponding essential array %s in VTK file is not found", essential_property.c_str(), essential_var); + } + + for(auto const& kv: scalers){ + vtkDataArray* vec = vtkDataArray::SafeDownCast(pd->GetArray(kv.second.c_str())); // name from OpenFOAM/Fem solver export + if(!vec) + vec = vtkDataArray::SafeDownCast(pd->GetArray(kv.first.c_str())); // name from FreeCAD export if(nPoints && vec && vec->GetNumberOfComponents() == 1) { - App::PropertyFloatList* field = static_cast(res->getPropertyByName(kv.first)); + App::PropertyFloatList* field = static_cast(res->getPropertyByName(kv.first.c_str())); if (!field) { - Base::Console().Error("static_cast((res->getPropertyByName(\"%s\")) failed \n", kv.first); + Base::Console().Error("static_cast((res->getPropertyByName(\"%s\")) failed \n", kv.first.c_str()); continue; } - double vmin=1.0e100, vmean=0.0, vmax=0.0; + double vmin=1.0e100, vmean=0.0, vmax=-1.0e100; std::vector values(nPoints, 0.0); - for(vtkIdType i = 0; i < vec->GetNumberOfTuples(); i++) - { + for(vtkIdType i = 0; i < vec->GetNumberOfTuples(); i++) { double v = *(vec->GetTuple(i)); values[i] = v; vmean += v; @@ -719,199 +752,209 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: } field->setValues(values); - int index = varids[kv.first]; - stats[index*3] = vmin; - stats[index*3 + 2] = vmax; - stats[index*3 + 1] = vmean/nPoints; + if(varids.find(kv.first) != varids.end()) { + const int index = varids.at(kv.first); + stats[index*3] = vmin; + stats[index*3 + 1] = vmean/nPoints; + stats[index*3 + 2] = vmax; + } - Base::Console().Message("field \"%s\" has been loaded \n", kv.first); + Base::Console().Message("field \"%s\" has been loaded \n", kv.first.c_str()); } } static_cast(res->getPropertyByName("Stats"))->setValues(stats); + } -/* -void FemVTKTools::importMechanicalResult(const vtkDataSet* grid, App::DocumentObject* res) { - // to be implemented later by FemWorkbench developer -} - * */ +void _exportResult(const App::DocumentObject* result, vtkSmartPointer grid, + const std::map& vectors, const std::map scalers, + const std::string& essential_property){ -void FemVTKTools::exportFluidicResult(const App::DocumentObject* res, vtkSmartPointer grid) { - if(!res->getPropertyByName("Velocity")){ - Base::Console().Message("Warning: essential field like `velocity` is not found in CfdResult\n"); - return; + const Fem::FemResultObject* res = static_cast(result); + + auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Units"); + int unitSchema = hGrp->GetInt("UserSchema",0); + float scale = 1.0; + if(unitSchema == 0) // standard mm + { + scale = 0.001; // convert from mm in FreeCAD to SI length in result file } - App::PropertyVectorList* velocity = static_cast(res->getPropertyByName("Velocity")); - const std::vector& vel = velocity->getValues(); - if(!vel.empty()) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfComponents(3); - data->SetName("Velocity"); - for(std::vector::const_iterator it=vel.begin(); it!=vel.end(); ++it) { - double tuple[] = {it->x, it->y, it->z}; - data->InsertNextTuple(tuple); + const vtkIdType nPoints = grid->GetNumberOfPoints(); + for (auto const& kv: vectors) { + const int dim = 3; //Fixme, detect dim + App::PropertyVectorList* field = nullptr; + if (res->getPropertyByName(kv.first.c_str())) + field = static_cast(res->getPropertyByName(kv.first.c_str())); + else + Base::Console().Error("PropertyVectorList %s not found \n", kv.first.c_str()); + if(field && field->getValues().size()>1) { // FreeCAD property list + const std::vector& vel = field->getValues(); + vtkSmartPointer data = vtkSmartPointer::New(); + if(nPoints != field->getSize()) + Base::Console().Error("PropertyVectorList->getSize() = %d, not equal to mesh point number \n", field->getSize()); + data->SetNumberOfComponents(dim); + data->SetNumberOfTuples(vel.size()); + data->SetName(kv.second.c_str()); // kv.first may be a better name, without space + + vtkIdType i=0; + if(kv.first == essential_property) { + for(std::vector::const_iterator it=vel.begin(); it!=vel.end(); ++it) { + Base::Vector3d v = vel.at(i); + double tuple[] = {v.x*scale, v.y*scale, v.z*scale}; + //double tuple[] = {it->x*scale, it->y*scale, it->z*scale}; + data->SetTuple(i, tuple); + ++i; + } + } + else{ + for(std::vector::const_iterator it=vel.begin(); it!=vel.end(); ++it) { + double tuple[] = {it->x, it->y, it->z}; + data->SetTuple(i, tuple); + ++i; + } + } + grid->GetPointData()->AddArray(data); + Base::Console().Message("Info: PropertyVectorList %s exported as vtk array name '%s'\n", kv.first.c_str(), kv.second.c_str()); } + else + Base::Console().Error("field = static_cast failed or empty for field: %s", kv.first.c_str()); + } - grid->GetPointData()->AddArray(data); - } - else{ - Base::Console().Message("Warning: essential fields pressure and velocity is empty in CfdResult\n"); - } - // Temperature is optional, so are other turbulence related variables - std::vector vars; // varable names are defined in CfdResult.py - vars.push_back("Pressure"); - vars.push_back("Temperature"); - vars.push_back("TurbulenceThermalDiffusivity"); - vars.push_back("TurbulenceViscosity"); - vars.push_back("TurbulenceEnergy"); - vars.push_back("TurbulenceDissipationRate"); - vars.push_back("TurbulenceSpecificDissipation"); - for(auto const& var: vars){ + for (auto const& kv: scalers) { App::PropertyFloatList* field = nullptr; - if (res->getPropertyByName(var)) - field = static_cast(res->getPropertyByName(var)); - if(!field && !field->getValues().empty()) { + if (res->getPropertyByName(kv.first.c_str())) + field = static_cast(res->getPropertyByName(kv.first.c_str())); + if(field && field->getValues().size()>1) { const std::vector& vec = field->getValues(); vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); - data->SetName(var); + data->SetName(kv.second.c_str()); for(size_t i=0; iSetValue(i, vec[i]); grid->GetPointData()->AddArray(data); + Base::Console().Message("Info: PropertyFloatList %s exported as vtk array name '%s'\n", kv.first.c_str(), kv.second.c_str()); } } + +} + +void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App::DocumentObject* res) { + // velocity and pressure are essential, Temperature is optional, so are turbulence related variables + std::map cfd_vectors; // vector field defined in openfoam -> property defined in CfdResult.py + cfd_vectors["Velocity"] = "U"; + + std::map cfd_scalers; // varable name defined in openfoam -> property defined in CfdResult.py + cfd_scalers["Pressure"] = "p"; + cfd_scalers["Temperature"] = "T"; + cfd_scalers["TurbulenceEnergy"] = "k"; + cfd_scalers["TurbulenceViscosity"] = "nut"; + cfd_scalers["TurbulenceDissipationRate"] = "epsilon"; + cfd_scalers["TurbulenceSpecificDissipation"] = "omega"; + cfd_scalers["TurbulenceThermalDiffusivity"] = "alphat"; + + std::map cfd_varids; // must agree with definition in Stat calc Cfd/_TaskPanelCfdResult.py + cfd_varids["Ux"] = 0; + cfd_varids["Uy"] = 1; + cfd_varids["Uz"] = 2; + cfd_varids["Umag"] = 3; + cfd_varids["Pressure"] = 4; + cfd_varids["Temperature"] = 5; + cfd_varids["TurbulenceEnergy"] = 6; + cfd_varids["TurbulenceViscosity"] = 7; + cfd_varids["TurbulenceDissipationRate"] = 8; + //cfd_varids["TurbulenceSpecificDissipation"] = 9; + //cfd_varids["TurbulenceThermalDiffusivity"] = 10; + + std::string essential_property = std::string("Velocity"); + + _importResult(dataset, res, cfd_vectors, cfd_scalers, cfd_varids, essential_property); + +} + +void FemVTKTools::exportFluidicResult(const App::DocumentObject* res, vtkSmartPointer grid) { + // velocity and pressure are essential, Temperature is optional, so are turbulence related variables + static std::map cfd_vectors; // vector field defined in openfoam -> property defined in CfdResult.py + cfd_vectors["Velocity"] = "U"; + + static std::map cfd_scalers; // varable name defined in openfoam -> property defined in CfdResult.py + cfd_scalers["Pressure"] = "p"; + cfd_scalers["Temperature"] = "T"; + cfd_scalers["TurbulenceEnergy"] = "k"; + cfd_scalers["TurbulenceViscosity"] = "nut"; + cfd_scalers["TurbulenceDissipationRate"] = "epsilon"; + cfd_scalers["TurbulenceSpecificDissipation"] = "omega"; + cfd_scalers["TurbulenceThermalDiffusivity"] = "alphat"; + + std::string essential_property = std::string("Velocity"); + + if(!res->getPropertyByName("Velocity")){ + Base::Console().Error("essential field like `velocity` is not found in CfdResult\n"); + return; + } + _exportResult(res, grid, cfd_vectors, cfd_scalers, essential_property); } -void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmartPointer grid) { - // code redundance can be avoided by property inspection, consider refactoring - const FemResultObject* res = static_cast(obj); - if(!res->StressValues.getValues().empty()) { - const std::vector& vec = res->StressValues.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("Von Mises stress"); +void FemVTKTools::importMechanicalResult(vtkSmartPointer dataset, App::DocumentObject* res) { + // field names are defined in this cpp, exportMechanicalResult() + // DisplaceVectors are essential, Temperature and other is optional + std::map vectors; // property defined in MechanicalResult.py -> variable name in vtk + vectors["DisplacementVectors"] = "Displacement"; + vectors["StrainVectors"] = "Strain vectors"; + vectors["StressVectors"] = "Stress vectors"; + std::map scalers; // App::FloatListProperty name -> vtk name + scalers["UserDefined"] = "User Defined Results"; + scalers["Temperature"] = "Temperature"; + scalers["PrincipalMax"] = "Maximum Principal stress"; + scalers["PrincipalMed"] = "Median Principal stress"; + scalers["PrincipalMin"] = "Minimum Principal stress"; + scalers["MaxShear"] = "Max shear stress (Tresca)"; + scalers["StressValues"] = "Von Mises stress"; + //scalers["DisplacementLengths"] = ""; // not yet exported in exportMechanicalResult() - for(size_t i=0; iSetValue(i, vec[i]); + std::map varids; + // id sequence must agree with definition in get_result_stats() of Fem/_TaskPanelShowResult.py + varids["U1"] = 0; // U1, displacement x axis + varids["U2"] = 1; + varids["U3"] = 2; + varids["Uabs"] = 3; + varids["StressValues"] = 4; // Sabs + varids["PrincipalMax"] = 5; // MaxPrin + varids["PrincipalMed"] = 6; // MidPrin + varids["PrincipalMin"] = 7; // MinPrin + varids["MaxShear"] = 8; // - grid->GetPointData()->AddArray(data); - }} + std::string essential_property = std::string("DisplacementVectors"); - if(!res->MaxShear.getValues().empty()) { - const std::vector& vec = res->MaxShear.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("Max shear stress (Tresca)"); + _importResult(dataset, res, vectors, scalers, varids, essential_property); - for(size_t i=0; iSetValue(i, vec[i]); - - grid->GetPointData()->AddArray(data); - }} - - if(!res->PrincipalMax.getValues().empty()) { - const std::vector& vec = res->PrincipalMax.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("Maximum Principal stress"); - - for(size_t i=0; iSetValue(i, vec[i]); - - grid->GetPointData()->AddArray(data); - }} - - if(!res->PrincipalMax.getValues().empty()) { - const std::vector& vec = res->PrincipalMin.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("Minimum Principal stress"); - - for(size_t i=0; iSetValue(i, vec[i]); - - grid->GetPointData()->AddArray(data); - }} - - if (!res->Temperature.getValues().empty()) { - const std::vector& vec = res->Temperature.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("Temperature"); - - for(size_t i=0; iSetValue(i, vec[i]); - - grid->GetPointData()->AddArray(data); - }} - - if (!res->UserDefined.getValues().empty()) { - const std::vector& vec = res->UserDefined.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfValues(vec.size()); - data->SetName("User Defined Results"); - - for(size_t i=0; iSetValue(i, vec[i]); - - grid->GetPointData()->AddArray(data); - }} +} - if(!res->DisplacementVectors.getValues().empty()) { - const std::vector& vec = res->DisplacementVectors.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfComponents(3); - data->SetName("Displacement"); +void FemVTKTools::exportMechanicalResult(const App::DocumentObject* res, vtkSmartPointer grid) { + if(!res->getPropertyByName("DisplacementVectors")){ + Base::Console().Error("essential field like `DisplacementVectors` is not found in this Result object\n"); + return; + } + std::map vectors; // property defined in MechanicalResult.py -> variable name in vtk + vectors["DisplacementVectors"] = "Displacement"; + vectors["StrainVectors"] = "Strain vectors"; + vectors["StressVectors"] = "Stress vectors"; + std::map scalers; // App::FloatListProperty name -> vtk name + scalers["UserDefined"] = "User Defined Results"; + scalers["Temperature"] = "Temperature"; + scalers["PrincipalMax"] = "Maximum Principal stress"; + scalers["PrincipalMed"] = "Median Principal stress"; + scalers["PrincipalMin"] = "Minimum Principal stress"; + scalers["MaxShear"] = "Max shear stress (Tresca)"; + scalers["StressValues"] = "Von Mises stress"; + //scalers["DisplacementLengths"] = ""; // not yet exported in exportMechanicalResult() - for(std::vector::const_iterator it=vec.begin(); it!=vec.end(); ++it) { - double tuple[] = {it->x, it->y, it->z}; - data->InsertNextTuple(tuple); - } - - grid->GetPointData()->AddArray(data); - }} - - if(!res->StressVectors.getValues().empty()) { - const std::vector& vec = res->StressVectors.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfComponents(3); - data->SetName("Stress Vectors"); - - for(std::vector::const_iterator it=vec.begin(); it!=vec.end(); ++it) { - double tuple[] = {it->x, it->y , it->z}; - data->InsertNextTuple(tuple); - } - - grid->GetPointData()->AddArray(data); - }} - - if(!res->StrainVectors.getValues().empty()) { - const std::vector& vec = res->StrainVectors.getValues(); - if (vec.size()>1) { - vtkSmartPointer data = vtkSmartPointer::New(); - data->SetNumberOfComponents(3); - data->SetName("Strain Vectors"); - - for(std::vector::const_iterator it=vec.begin(); it!=vec.end(); ++it) { - double tuple[] = {it->x, it->y, it->z}; - data->InsertNextTuple(tuple); - } - - grid->GetPointData()->AddArray(data); - }} + std::string essential_property = std::string("DisplacementVectors"); + _exportResult(res, grid, vectors, scalers, essential_property); } diff --git a/src/Mod/Fem/App/FemVTKTools.h b/src/Mod/Fem/App/FemVTKTools.h index 5ae2556403..ac299db6f9 100644 --- a/src/Mod/Fem/App/FemVTKTools.h +++ b/src/Mod/Fem/App/FemVTKTools.h @@ -54,7 +54,7 @@ namespace Fem /*! FemMesh export to vtkUnstructuredGrid instance */ - static void exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid); + static void exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid, float scale = 1.0); /*! FemMesh write to vtkUnstructuredGrid data file */ @@ -64,7 +64,7 @@ namespace Fem * FemResult import from vtkUnstructuredGrid object */ static void importFluidicResult(vtkSmartPointer dataset, App::DocumentObject* res); - // importMechanicalResult can be defined if necessary in the future + static void importMechanicalResult(vtkSmartPointer dataset, App::DocumentObject* res); /*! * FemResult export to vtkUnstructuredGrid object @@ -73,9 +73,9 @@ namespace Fem static void exportMechanicalResult(const App::DocumentObject* res, vtkSmartPointer grid); /*! - * FemResult (active or created if res= NULL) read from vtkUnstructuredGrid dataset file + * FemResult (activeObject or created if res= NULL) read from vtkUnstructuredGrid dataset file */ - static App::DocumentObject* readFluidicResult(const char* Filename, App::DocumentObject* res = NULL); + static App::DocumentObject* readResult(const char* Filename, App::DocumentObject* res = NULL); /*! * write FemResult (activeObject if res= NULL) to vtkUnstructuredGrid dataset file diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index d500549c76..a212a5c8da 100644 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -1525,13 +1525,25 @@ CmdFemPostPipelineFromResult::CmdFemPostPipelineFromResult() void CmdFemPostPipelineFromResult::activated(int) { + /* Gui::SelectionFilter ResultFilter("SELECT Fem::FemResultObject COUNT 1"); - if (ResultFilter.match()) { - + Base::Console().Message("Debug: `SELECT Fem::FemResultObject COUNT 1` has matched obj"); Fem::FemResultObject* result = static_cast(ResultFilter.Result[0][0].getObject()); + //static_cast failed here + Base::Console().Message("Debug: FemResultObject pointer = %p", result ); + + */ + App::Document* pcDoc = App::GetApplication().getActiveDocument(); + if(!pcDoc) + { + Base::Console().Message("No active document is found thus do nothing and return\n"); + return; + } + Fem::FemResultObject* result= static_cast(pcDoc->getActiveObject()); + if(result) + { std::string FeatName = getUniqueObjectName("Pipeline"); - openCommand("Create pipeline from result"); doCommand(Doc,"App.activeDocument().addObject('Fem::FemPostPipeline','%s')",FeatName.c_str()); @@ -1543,8 +1555,8 @@ void CmdFemPostPipelineFromResult::activated(int) } else { QMessageBox::warning(Gui::getMainWindow(), - qApp->translate("CmdFemPostCreateClipFilter", "Wrong selection"), - qApp->translate("CmdFemPostCreateClipFilter", "Select a result, please.")); + qApp->translate("CmdFemPostPipelineFromResult", "Wrong selection type"), + qApp->translate("CmdFemPostPipelineFromResult", "Select a result object, please.")); } }