diff --git a/src/Mod/Fem/App/FemPostPipeline.cpp b/src/Mod/Fem/App/FemPostPipeline.cpp index 85e3b988bb..e76954d4a9 100644 --- a/src/Mod/Fem/App/FemPostPipeline.cpp +++ b/src/Mod/Fem/App/FemPostPipeline.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #include #include @@ -313,10 +314,23 @@ void FemPostPipeline::read(std::vector& files, std::string& frame_type) { if (files.size() != values.size()) { - Base::Console().Error("Result files and frame values have different length.\n"); - return; + throw Base::ValueError("Result files and frame values have different length"); } + // make sure we do not have invalid values + for (auto& value : values) { + if (!std::isfinite(value)) { + throw Base::ValueError("Values need to be finite"); + } + } + + // ensure no double values for frames + std::set value_set(values.begin(), values.end()); + if (value_set.size() != values.size()) { + throw Base::ValueError("Values need to be unique"); + } + + // setup the time information for the multiblock vtkStringArray* TimeInfo = vtkStringArray::New(); TimeInfo->SetName("TimeInfo"); @@ -650,8 +664,20 @@ void FemPostPipeline::load(std::vector& res, { if (res.size() != values.size()) { - Base::Console().Error("Result values and frame values have different length.\n"); - return; + throw Base::ValueError("Result values and frame values have different length"); + } + + // make sure we do not have invalid values + for (auto& value : values) { + if (!std::isfinite(value)) { + throw Base::ValueError("Values need to be finite"); + } + } + + // ensure no double values for frames + std::set value_set(values.begin(), values.end()); + if (value_set.size() != values.size()) { + throw Base::ValueError("Values need to be unique"); } // setup the time information for the multiblock @@ -664,8 +690,7 @@ void FemPostPipeline::load(std::vector& res, for (ulong i = 0; i < res.size(); i++) { if (!res[i]->Mesh.getValue()->isDerivedFrom()) { - Base::Console().Error("Result mesh object is not derived from Fem::FemMeshObject.\n"); - return; + throw Base::ValueError("Result mesh object is not derived from Fem::FemMeshObject"); } // first copy the mesh over diff --git a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp index 9de89e34cb..be59cdefb2 100644 --- a/src/Mod/Fem/App/FemPostPipelinePyImp.cpp +++ b/src/Mod/Fem/App/FemPostPipelinePyImp.cpp @@ -172,7 +172,8 @@ PyObject* FemPostPipelinePy::load(PyObject* args) std::string error = std::string( "Result and value must be list of ResultObject and number respectively."); - throw Base::TypeError(error); + PyErr_SetString(PyExc_TypeError, error.c_str()); + return nullptr; } // extract the result objects @@ -186,11 +187,15 @@ PyObject* FemPostPipelinePy::load(PyObject* args) if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) { std::string error = std::string("type in result list must be 'ResultObject', not "); - throw Base::TypeError(error); + PyErr_SetString(PyExc_TypeError, error.c_str()); + return nullptr; } auto obj = static_cast(*item)->getDocumentObjectPtr(); if (!obj->isDerivedFrom()) { - throw Base::TypeError("object is not a result object"); + std::string error = + std::string("type in result list must be 'ResultObject', not "); + PyErr_SetString(PyExc_TypeError, error.c_str()); + return nullptr; } results[i] = static_cast(obj); } @@ -202,12 +207,12 @@ PyObject* FemPostPipelinePy::load(PyObject* args) values.resize(size); for (Py::Sequence::size_type i = 0; i < size; i++) { - Py::Object item = values_list[i]; - if (!PyFloat_Check(*item)) { - std::string error = std::string("Values must be float"); - throw Base::TypeError(error); + Py::Object value = values_list[i]; + if (!value.isNumeric()) { + PyErr_SetString(PyExc_TypeError, "Values must be numbers"); + return nullptr; } - values[i] = PyFloat_AsDouble(*item); + values[i] = Py::Float(value).as_double(); } // extract the unit @@ -223,7 +228,8 @@ PyObject* FemPostPipelinePy::load(PyObject* args) else { std::string error = std::string( "Multistep load requires 4 arguments: ResultList, ValueList, unit, type"); - throw Base::TypeError(error); + PyErr_SetString(PyExc_ValueError, error.c_str()); + return nullptr; } } return nullptr; diff --git a/src/Mod/Fem/App/PropertyPostDataObject.cpp b/src/Mod/Fem/App/PropertyPostDataObject.cpp index e075a9cbeb..b8d953fcca 100644 --- a/src/Mod/Fem/App/PropertyPostDataObject.cpp +++ b/src/Mod/Fem/App/PropertyPostDataObject.cpp @@ -211,7 +211,7 @@ void PropertyPostDataObject::createDataObjectByExternalType(vtkSmartPointer::New(); break; default: - break; + throw Base::TypeError("Unsupported VTK data type"); }; } @@ -432,7 +432,7 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader) // TODO: read in of composite data structures need to be coded, // including replace of "GetOutputAsDataSet()" - vtkSmartPointer xmlReader; + vtkSmartPointer xmlReader = nullptr; if (extension == "vtp") { xmlReader = vtkSmartPointer::New(); } @@ -489,31 +489,39 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader) xmlReader = vtkSmartPointer::New(); } - xmlReader->SetFileName(fi.filePath().c_str()); - xmlReader->Update(); + if (xmlReader) { + xmlReader->SetFileName(fi.filePath().c_str()); + xmlReader->Update(); - if (!xmlReader->GetOutputDataObject(0)) { - // Note: Do NOT throw an exception here because if the tmp. created file could - // not be read it's NOT an indication for an invalid input stream 'reader'. - // We only print an error message but continue reading the next files from the - // stream... - App::PropertyContainer* father = this->getContainer(); - if (father && father->isDerivedFrom()) { - App::DocumentObject* obj = static_cast(father); - Base::Console().Error("Dataset file '%s' with data of '%s' seems to be empty\n", - fi.filePath().c_str(), - obj->Label.getValue()); + if (!xmlReader->GetOutputDataObject(0)) { + // Note: Do NOT throw an exception here because if the tmp. created file could + // not be read it's NOT an indication for an invalid input stream 'reader'. + // We only print an error message but continue reading the next files from the + // stream... + App::PropertyContainer* father = this->getContainer(); + if (father && father->isDerivedFrom()) { + App::DocumentObject* obj = static_cast(father); + Base::Console().Error("Dataset file '%s' with data of '%s' seems to be empty\n", + fi.filePath().c_str(), + obj->Label.getValue()); + } + else { + Base::Console().Warning("Loaded Dataset file '%s' seems to be empty\n", + fi.filePath().c_str()); + } } else { - Base::Console().Warning("Loaded Dataset file '%s' seems to be empty\n", - fi.filePath().c_str()); + aboutToSetValue(); + createDataObjectByExternalType(xmlReader->GetOutputDataObject(0)); + m_dataObject->DeepCopy(xmlReader->GetOutputDataObject(0)); + hasSetValue(); } } else { - aboutToSetValue(); - createDataObjectByExternalType(xmlReader->GetOutputDataObject(0)); - m_dataObject->DeepCopy(xmlReader->GetOutputDataObject(0)); - hasSetValue(); + Base::Console().Error( + "Dataset file '%s' is of unsupported type: %s. Data not loaded.\n", + fi.filePath().c_str(), + extension); } } diff --git a/src/Mod/Fem/Gui/TaskPostBoxes.cpp b/src/Mod/Fem/Gui/TaskPostBoxes.cpp index 37047f9883..fefe174507 100644 --- a/src/Mod/Fem/Gui/TaskPostBoxes.cpp +++ b/src/Mod/Fem/Gui/TaskPostBoxes.cpp @@ -316,9 +316,11 @@ void TaskDlgPost::appendBox(TaskPostBox* box) void TaskDlgPost::open() { - // a transaction is already open at creation time of the pad - QString msg = QObject::tr("Edit post processing object"); - Gui::Command::openCommand(msg.toUtf8().constData()); + // only open a new command if none is pending (e.g. if the object was newly created) + if (!Gui::Command::hasPendingCommand()) { + auto text = std::string("Edit ") + m_view->getObject()->Label.getValue(); + Gui::Command::openCommand(text.c_str()); + } } void TaskDlgPost::clicked(int button) diff --git a/src/Mod/Fem/feminout/importCcxFrdResults.py b/src/Mod/Fem/feminout/importCcxFrdResults.py index 79d88ec032..82859cd10d 100644 --- a/src/Mod/Fem/feminout/importCcxFrdResults.py +++ b/src/Mod/Fem/feminout/importCcxFrdResults.py @@ -32,6 +32,7 @@ __url__ = "https://www.freecad.org" # \brief FreeCAD Calculix FRD Reader for FEM workbench import os +import math import FreeCAD from FreeCAD import Console @@ -133,6 +134,8 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty else: eigenmode_number = 0 step_time = result_set["time"] + if not math.isfinite(step_time): + step_time = 0 step_time = round(step_time, 2) if eigenmode_number > 0: results_name = "{}EigenMode_{}_Results".format( @@ -252,6 +255,7 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty elif result_analysis_type == "check": results_name = f"{result_name_prefix}Check" res_obj = make_result_mesh(results_name) + setupPipeline(doc, analysis, results_name, [res_obj]) if analysis: analysis.addObject(res_obj) @@ -276,6 +280,7 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty results_name = "Results" res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object + setupPipeline(doc, analysis, results_name, [res_obj]) # TODO, node numbers in result obj could be set if analysis: analysis.addObject(res_obj)