250 lines
9.1 KiB
Python
250 lines
9.1 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
|
# * *
|
|
# * This file is part of the FreeCAD CAx development system. *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program 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 program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
__title__ = "FreeCAD post extractors 2D"
|
|
__author__ = "Stefan Tröger"
|
|
__url__ = "https://www.freecad.org"
|
|
|
|
## @package post_histogram
|
|
# \ingroup FEM
|
|
# \brief Post processing plot displaying lines
|
|
|
|
import FreeCAD
|
|
|
|
from . import base_fempostextractors
|
|
from . import base_fempythonobject
|
|
|
|
_PropHelper = base_fempythonobject._PropHelper
|
|
|
|
from vtkmodules.vtkCommonCore import vtkDoubleArray
|
|
from vtkmodules.vtkCommonDataModel import vtkTable
|
|
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
|
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
|
|
|
|
class PostFieldData2D(base_fempostextractors.Extractor2D):
|
|
"""
|
|
A post processing extraction of two dimensional field data
|
|
"""
|
|
|
|
ExtractionType = "Field"
|
|
|
|
def __init__(self, obj):
|
|
super().__init__(obj)
|
|
|
|
def _get_properties(self):
|
|
prop = [
|
|
_PropHelper(
|
|
type="App::PropertyBool",
|
|
name="ExtractFrames",
|
|
group="Multiframe",
|
|
doc=QT_TRANSLATE_NOOP(
|
|
"FEM", "Specify if the field shall be extracted for every available frame"
|
|
),
|
|
value=False,
|
|
),
|
|
]
|
|
return super()._get_properties() + prop
|
|
|
|
def execute(self, obj):
|
|
|
|
# on execution we populate the vtk table
|
|
table = vtkTable()
|
|
|
|
if not obj.Source:
|
|
obj.Table = table
|
|
return
|
|
|
|
dataset = obj.Source.getDataSet()
|
|
if not dataset:
|
|
obj.Table = table
|
|
return
|
|
|
|
timesteps = []
|
|
if obj.ExtractFrames:
|
|
# check if we have timesteps
|
|
info = obj.Source.getOutputAlgorithm().GetOutputInformation(0)
|
|
if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
|
|
timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
|
|
else:
|
|
FreeCAD.Console.PrintWarning(
|
|
'No frames available in data, ignoring "ExtractFrames" property'
|
|
)
|
|
|
|
if not timesteps:
|
|
# get the dataset and extract the correct array
|
|
xarray = self._x_array_from_dataset(obj, dataset)
|
|
if xarray.GetNumberOfComponents() > 1:
|
|
xarray.SetName(obj.XField + " (" + obj.XComponent + ")")
|
|
else:
|
|
xarray.SetName(obj.XField)
|
|
|
|
self._x_array_component_to_table(obj, xarray, table)
|
|
|
|
yarray = self._y_array_from_dataset(obj, dataset)
|
|
if yarray.GetNumberOfComponents() > 1:
|
|
yarray.SetName(obj.YField + " (" + obj.YComponent + ")")
|
|
else:
|
|
yarray.SetName(obj.YField)
|
|
|
|
self._y_array_component_to_table(obj, yarray, table)
|
|
|
|
else:
|
|
algo = obj.Source.getOutputAlgorithm()
|
|
for timestep in timesteps:
|
|
algo.UpdateTimeStep(timestep)
|
|
dataset = algo.GetOutputDataObject(0)
|
|
|
|
xarray = self._x_array_from_dataset(obj, dataset)
|
|
if xarray.GetNumberOfComponents() > 1:
|
|
xarray.SetName(f"X - {obj.XField} ({obj.XComponent}) - {timestep}")
|
|
else:
|
|
xarray.SetName(f"X - {obj.XField} - {timestep}")
|
|
self._x_array_component_to_table(obj, xarray, table)
|
|
|
|
yarray = self._y_array_from_dataset(obj, dataset)
|
|
if yarray.GetNumberOfComponents() > 1:
|
|
yarray.SetName(f"{obj.YField} ({obj.YComponent}) - {timestep}")
|
|
else:
|
|
yarray.SetName(f"{obj.YField} - {timestep}")
|
|
self._y_array_component_to_table(obj, yarray, table)
|
|
|
|
# set the final table
|
|
obj.Table = table
|
|
|
|
|
|
class PostIndexOverFrames2D(base_fempostextractors.Extractor2D):
|
|
"""
|
|
A post processing extraction for two dimensional data with X always being the frames
|
|
"""
|
|
|
|
ExtractionType = "Index"
|
|
|
|
def __init__(self, obj):
|
|
super().__init__(obj)
|
|
|
|
def _get_properties(self):
|
|
prop = [
|
|
_PropHelper(
|
|
type="App::PropertyInteger",
|
|
name="Index",
|
|
group="Data",
|
|
doc=QT_TRANSLATE_NOOP(
|
|
"FEM", "Specify for which point index the data should be extracted"
|
|
),
|
|
value=0,
|
|
),
|
|
]
|
|
return super()._get_properties() + prop
|
|
|
|
def _setup_x_component_property(self, obj, point_data):
|
|
# override to only allow "Frames" as X data
|
|
obj.XComponent = ["Not a vector"]
|
|
|
|
def _setup_x_properties(self, obj, dataset):
|
|
# override to only allow "Frames" as X data
|
|
obj.XField = ["Frames"]
|
|
|
|
def execute(self, obj):
|
|
|
|
# on execution we populate the vtk table
|
|
table = vtkTable()
|
|
|
|
if not obj.Source:
|
|
obj.Table = table
|
|
return
|
|
|
|
dataset = obj.Source.getDataSet()
|
|
if not dataset:
|
|
obj.Table = table
|
|
return
|
|
|
|
# check if we have timesteps (required!)
|
|
timesteps = []
|
|
info = obj.Source.getOutputAlgorithm().GetOutputInformation(0)
|
|
if info.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
|
|
timesteps = info.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
|
|
|
|
algo = obj.Source.getOutputAlgorithm()
|
|
|
|
frame_x_array = vtkDoubleArray()
|
|
frame_y_array = vtkDoubleArray()
|
|
idx = obj.Index
|
|
|
|
if timesteps:
|
|
setup = False
|
|
frame_x_array.SetNumberOfTuples(len(timesteps))
|
|
frame_x_array.SetNumberOfComponents(1)
|
|
for i, timestep in enumerate(timesteps):
|
|
|
|
frame_x_array.SetTuple1(i, timestep)
|
|
|
|
algo.UpdateTimeStep(timestep)
|
|
dataset = algo.GetOutputDataObject(0)
|
|
array = self._y_array_from_dataset(obj, dataset, copy=False)
|
|
|
|
# safeguard for invalid access
|
|
if idx < 0 or array.GetNumberOfTuples() - 1 < idx:
|
|
raise Exception(
|
|
f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}"
|
|
)
|
|
|
|
if not setup:
|
|
frame_y_array.SetNumberOfComponents(array.GetNumberOfComponents())
|
|
frame_y_array.SetNumberOfTuples(len(timesteps))
|
|
setup = True
|
|
|
|
frame_y_array.SetTuple(i, idx, array)
|
|
|
|
else:
|
|
frame_x_array.SetNumberOfTuples(1)
|
|
frame_x_array.SetNumberOfComponents(1)
|
|
frame_x_array.SetTuple1(0, 0)
|
|
|
|
algo.Update()
|
|
dataset = algo.GetOutputDataObject(0)
|
|
array = self._y_array_from_dataset(obj, dataset, copy=False)
|
|
|
|
# safeguard for invalid access
|
|
if idx < 0 or array.GetNumberOfTuples() - 1 < idx:
|
|
raise Exception(
|
|
f"Invalid index: {idx} is not in range 0 - {array.GetNumberOfTuples()-1}"
|
|
)
|
|
|
|
frame_y_array.SetNumberOfComponents(array.GetNumberOfComponents())
|
|
frame_y_array.SetNumberOfTuples(1)
|
|
frame_y_array.SetTuple(0, idx, array)
|
|
|
|
frame_x_array.SetName("Frames")
|
|
if frame_y_array.GetNumberOfComponents() > 1:
|
|
frame_y_array.SetName(f"{obj.YField} ({obj.YComponent}) @Idx {obj.Index}")
|
|
else:
|
|
frame_y_array.SetName(f"{obj.YField} @Idx {obj.Index}")
|
|
|
|
table.AddColumn(frame_x_array)
|
|
self._y_array_component_to_table(obj, frame_y_array, table)
|
|
|
|
# set the final table
|
|
obj.Table = table
|