From 96ed82f0cfa2932fc13f9664e31ee286246ac91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Sun, 17 Aug 2025 19:52:09 +0200 Subject: [PATCH] FEM: Keep result pipeline and only reload data. Fixes #20542 --- src/App/GroupExtension.pyi | 7 +++++ src/App/GroupExtensionPyImp.cpp | 16 ++++++++++ src/Mod/Fem/feminout/importCcxFrdResults.py | 18 +++++++++-- src/Mod/Fem/femresult/resulttools.py | 35 +++++++++++++++++---- src/Mod/Fem/femtools/ccxtools.py | 6 +++- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/App/GroupExtension.pyi b/src/App/GroupExtension.pyi index 8d50bc416b..d5d66a924b 100644 --- a/src/App/GroupExtension.pyi +++ b/src/App/GroupExtension.pyi @@ -61,6 +61,13 @@ class GroupExtension(DocumentObjectExtension): """ ... + def getObjectsOfType(self, typename: str) -> List[Any]: + """ + Returns all object in the group of given type + @param typename The Freecad type identifier + """ + ... + def hasObject(self, obj: Any, recursive: bool = False) -> bool: """ hasObject(obj, recursive=false) diff --git a/src/App/GroupExtensionPyImp.cpp b/src/App/GroupExtensionPyImp.cpp index fc281215d8..72e2eacc80 100644 --- a/src/App/GroupExtensionPyImp.cpp +++ b/src/App/GroupExtensionPyImp.cpp @@ -278,6 +278,22 @@ PyObject* GroupExtensionPy::getObject(PyObject* args) } } +PyObject* GroupExtensionPy::getObjectsOfType(PyObject* args) +{ + char* pcName; + if (!PyArg_ParseTuple(args, "s", &pcName)) { + return nullptr; + } + + std::vector objs = getGroupExtensionPtr()->getObjectsOfType(Base::Type::fromName(pcName)); + Py::List result; + for (App::DocumentObject* obj : objs) { + result.append(Py::asObject(obj->getPyObject())); + } + + return Py::new_reference_to(result); +} + PyObject* GroupExtensionPy::hasObject(PyObject* args) { PyObject* object; diff --git a/src/Mod/Fem/feminout/importCcxFrdResults.py b/src/Mod/Fem/feminout/importCcxFrdResults.py index 32735e28ff..a26a97c8ad 100644 --- a/src/Mod/Fem/feminout/importCcxFrdResults.py +++ b/src/Mod/Fem/feminout/importCcxFrdResults.py @@ -66,21 +66,33 @@ def setupPipeline(doc, analysis, results_name, result_data): if not "BUILD_FEM_VTK" in FreeCAD.__cmake__: return - # create a results pipeline if not already existing + # create a results pipeline (dependend on user settings) pipeline_name = "Pipeline_" + results_name - pipeline_obj = doc.getObject(pipeline_name) - if pipeline_obj is None: + pipelines = analysis.getObjectsOfType("Fem::FemPostPipeline") + fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") + keep_results_on_rerun = fem_prefs.GetBool("KeepResultsOnReRun", False) + if not pipelines or keep_results_on_rerun: + # needs to create a new pipeline! pipeline_obj = ObjectsFem.makePostVtkResult(doc, result_data, results_name) pipeline_visibility = True if analysis: analysis.addObject(pipeline_obj) else: + # by default get the last one + pipeline_obj = pipelines[-1] + # maybe there is one with the correct name + named_pipeline = analysis.getObject(pipeline_name) + if named_pipeline: + pipeline_obj = named_pipeline + if FreeCAD.GuiUp: # store pipeline visibility because pipeline_obj.load makes the # pipeline always visible pipeline_visibility = pipeline_obj.ViewObject.Visibility + # relabel the pipeline and load the data into it + pipeline_obj.Label = pipeline_name pipeline_obj.load(*result_data) # update the pipeline diff --git a/src/Mod/Fem/femresult/resulttools.py b/src/Mod/Fem/femresult/resulttools.py index 445143c5aa..d434a64396 100644 --- a/src/Mod/Fem/femresult/resulttools.py +++ b/src/Mod/Fem/femresult/resulttools.py @@ -34,7 +34,7 @@ import FreeCAD from femtools.femutils import is_of_type -def purge_results(analysis): +def purge_result_objects(analysis): """Removes all result objects and result meshes from an analysis group. Parameters @@ -43,11 +43,6 @@ def purge_results(analysis): analysis group as a container for all objects needed for the analysis """ - # if analysis type check is used, result mesh - # without result obj is created in the analysis - # we could run into trouble in one loop because - # we will delete objects and try to access them later - # result object for m in analysis.Group: if m.isDerivedFrom("Fem::FemResultObject"): @@ -68,6 +63,16 @@ def purge_results(analysis): analysis.Document.removeObject(m.Name) analysis.Document.recompute() + +def purge_postprocessing_objects(analysis): + """Removes all postprocessing objects and visualizations form the analysis + + Parameters + ---------- + analysis : Fem::FemAnalysis + analysis group as a container for all objects needed for the analysis + """ + # result pipeline and filter for m in analysis.Group: if is_of_type(m, "Fem::FemPostPipeline"): @@ -82,6 +87,24 @@ def purge_results(analysis): analysis.Document.recompute() +def purge_results(analysis): + """Removes all result and postprocessing objects and result meshes from an analysis group. + + Parameters + ---------- + analysis : Fem::FemAnalysis + analysis group as a container for all objects needed for the analysis + """ + + # if analysis type check is used, result mesh + # without result obj is created in the analysis + # we could run into trouble in one loop because + # we will delete objects and try to access them later + + purge_result_objects(analysis) + purge_postprocessing_objects(analysis) + + def reset_mesh_deformation(resultobj): """Resets result mesh deformation. diff --git a/src/Mod/Fem/femtools/ccxtools.py b/src/Mod/Fem/femtools/ccxtools.py index 9589db4a6b..c718621fa5 100644 --- a/src/Mod/Fem/femtools/ccxtools.py +++ b/src/Mod/Fem/femtools/ccxtools.py @@ -162,7 +162,11 @@ class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") keep_results_on_rerun = self.fem_prefs.GetBool("KeepResultsOnReRun", False) if not keep_results_on_rerun: - self.purge_results() + # we remove the result objects only, not the postprocessing ones. + # Reason: "Not keep results" means for the user override the data. For postprocessing + # this means keeping all filters, just change the data. + from femresult.resulttools import purge_result_objects as purge + purge(self.analysis) def reset_all(self): """Reset mesh color, deformation and removes all result objects"""