[FEM] proper support for transient analyses
- for the first time ever you get now for every time step a result in FreeCAD - this way also change output filename prefix to "FreeCAD" to avoid we depend on the default name Elmer gives and that was already changed in the past and to distinguish the *.vtu files from those created e.g. directly by ElmerGui - also remove an unnecessary output to the case.sif file
This commit is contained in:
@@ -165,6 +165,14 @@ class Proxy(solverbase.Proxy):
|
||||
4 | 8
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyLinkList",
|
||||
"ElmerTimeResults",
|
||||
"Base",
|
||||
"",
|
||||
4 | 8
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"ElmerOutput",
|
||||
|
||||
@@ -244,6 +244,12 @@ class Solve(run.Solve):
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
if self.solver.SimulationType == "Steady State":
|
||||
self._handleStedyStateResult()
|
||||
else:
|
||||
self._handleTransientResults()
|
||||
|
||||
def _handleStedyStateResult(self):
|
||||
if self.solver.ElmerResult is None:
|
||||
self._createResults()
|
||||
postPath = self._getResultFile()
|
||||
@@ -253,7 +259,7 @@ class Results(run.Results):
|
||||
return
|
||||
self.solver.ElmerResult.read(postPath)
|
||||
# at the moment we scale the mesh back using Elmer
|
||||
# this might be changed in future, therefore leave this
|
||||
# this might be changed in future, this commented code is left as info
|
||||
# self.solver.ElmerResult.scale(1000)
|
||||
|
||||
# for eigen analyses the resulting values are by a factor 1000 to high
|
||||
@@ -269,20 +275,100 @@ class Results(run.Results):
|
||||
def _createResults(self):
|
||||
self.solver.ElmerResult = self.analysis.Document.addObject(
|
||||
"Fem::FemPostPipeline", self.solver.Name + "Result")
|
||||
self.solver.ElmerResult.Label = self.solver.Label + "Result"
|
||||
self.solver.ElmerResult.Label = self.solver.ElmerResult.Name
|
||||
self.solver.ElmerResult.ViewObject.SelectionStyle = "BoundBox"
|
||||
self.analysis.addObject(self.solver.ElmerResult)
|
||||
# to assure the user sees something, set the default to Surface
|
||||
self.solver.ElmerResult.ViewObject.DisplayMode = "Surface"
|
||||
|
||||
def _handleTransientResults(self):
|
||||
# for transient results we must create a result pipeline for every time
|
||||
# the connection between result files and and their time is in the FreeCAD.pvd file
|
||||
# therefore first open FreeCAD.pvd
|
||||
pvdFilePath = os.path.join(self.directory, "FreeCAD.pvd")
|
||||
if not os.path.exists(pvdFilePath):
|
||||
self.pushStatus("\nNo result file was created.\n")
|
||||
self.fail()
|
||||
return
|
||||
pvdFile = open(pvdFilePath, "r")
|
||||
# read all lines
|
||||
pvdContent = pvdFile.readlines()
|
||||
# skip header and footer line and evaluate all lines
|
||||
# a line has the form like this:
|
||||
# <DataSet timestep=" 5.000E-02" group="" part="0" file="FreeCAD_t0001.vtu"/>
|
||||
# so .split("\"") gives as 2nd the time and as 7th the filename
|
||||
for i in range(0, len(pvdContent) - 2):
|
||||
# get time
|
||||
lineArray = pvdContent[i+1].split("\"")
|
||||
time = float(lineArray[1])
|
||||
filename = os.path.join(self.directory, lineArray[7])
|
||||
if os.path.isfile(filename):
|
||||
self._createTimeResults(time, i+1)
|
||||
self.solver.ElmerTimeResults[i].read(filename)
|
||||
|
||||
# for eigen analyses the resulting values are by a factor 1000 to high
|
||||
# therefore scale all *EigenMode results
|
||||
self.solver.ElmerTimeResults[i].ViewObject.transformField(
|
||||
"displacement EigenMode1", 0.001
|
||||
)
|
||||
|
||||
self.solver.ElmerTimeResults[i].recomputeChildren()
|
||||
# recompute() will update the result mesh data
|
||||
# but not the shape and bar coloring
|
||||
self.solver.ElmerTimeResults[i].ViewObject.updateColorBars()
|
||||
else:
|
||||
self.pushStatus("\nResult file for time {} is missing.\n".format(time))
|
||||
self.fail()
|
||||
return
|
||||
self.solver.Document.recompute()
|
||||
|
||||
def _createTimeResults(self, time, counter):
|
||||
# if self.solver.ElmerTimeResults[counter] exists, but time is different
|
||||
# recreate, other wise append
|
||||
# FreeCAD would replaces dots in object names with underscores, thus do the same
|
||||
newName = self.solver.Name + "_" + str(time).replace(".", "_") + "_" + "Result"
|
||||
if counter > len(self.solver.ElmerTimeResults):
|
||||
pipeline = self.analysis.Document.addObject(
|
||||
"Fem::FemPostPipeline", newName
|
||||
)
|
||||
# App::PropertyLinkList does not support append
|
||||
# thus we have to use a temporary list to append
|
||||
tmplist = self.solver.ElmerTimeResults
|
||||
tmplist.append(pipeline)
|
||||
self.solver.ElmerTimeResults = tmplist
|
||||
self._finishTimeResults(time, counter-1)
|
||||
else:
|
||||
# recreate if time is not equal
|
||||
if self.solver.ElmerTimeResults[counter-1].Name != newName:
|
||||
# store current list before removing object since object removal will automatically
|
||||
# remove entry from self.solver.ElmerTimeResults
|
||||
tmplist = self.solver.ElmerTimeResults
|
||||
self.analysis.Document.removeObject(
|
||||
self.solver.ElmerTimeResults[counter-1].Name
|
||||
)
|
||||
tmplist[counter-1] = self.analysis.Document.addObject(
|
||||
"Fem::FemPostPipeline", newName
|
||||
)
|
||||
self.solver.ElmerTimeResults = tmplist
|
||||
self._finishTimeResults(time, counter-1)
|
||||
|
||||
def _finishTimeResults(self, time, counter):
|
||||
# we purposely use the decimal dot in the label
|
||||
self.solver.ElmerTimeResults[counter].Label\
|
||||
= self.solver.Name + "_" + str(time) + "_" + "Result"
|
||||
self.solver.ElmerTimeResults[counter].ViewObject.OnTopWhenSelected = True
|
||||
self.analysis.addObject(self.solver.ElmerTimeResults[counter])
|
||||
# to assure the user sees something, set the default to Surface
|
||||
self.solver.ElmerTimeResults[counter].ViewObject.DisplayMode = "Surface"
|
||||
|
||||
def _getResultFile(self):
|
||||
postPath = None
|
||||
# elmer post file path changed with version x.x
|
||||
# see https://forum.freecadweb.org/viewtopic.php?f=18&t=42732
|
||||
# workaround
|
||||
possible_post_file_old = os.path.join(self.directory, "case0001.vtu")
|
||||
possible_post_file_single = os.path.join(self.directory, "case_t0001.vtu")
|
||||
possible_post_file_multi = os.path.join(self.directory, "case_t0001.pvtu")
|
||||
possible_post_file_single = os.path.join(self.directory, "FreeCAD_t0001.vtu")
|
||||
possible_post_file_multi = os.path.join(self.directory, "FreeCAD_t0001.pvtu")
|
||||
# depending on the currently set number of cores we try to load either
|
||||
# the multi-thread result or the single result
|
||||
if settings.get_cores("ElmerSolver") > 1:
|
||||
|
||||
@@ -331,7 +331,6 @@ class Writer(object):
|
||||
# Elmer uses SI base units, but our mesh is in mm, therefore we must tell
|
||||
# the solver that we have another scale
|
||||
self._simulation("Coordinate Scaling", 0.001)
|
||||
self._simulation("Output Intervals", 1)
|
||||
self._simulation("Simulation Type", self.solver.SimulationType)
|
||||
if self.solver.SimulationType == "Steady State":
|
||||
self._simulation(
|
||||
@@ -372,6 +371,14 @@ class Writer(object):
|
||||
"Order of time stepping method 'BDF'"
|
||||
)
|
||||
solver.BDFOrder = (2, 1, 5, 1)
|
||||
if not hasattr(self.solver, "ElmerTimeResults"):
|
||||
solver.addProperty(
|
||||
"App::PropertyLinkList",
|
||||
"ElmerTimeResults",
|
||||
"Base",
|
||||
"",
|
||||
4 | 8
|
||||
)
|
||||
if not hasattr(self.solver, "OutputIntervals"):
|
||||
solver.addProperty(
|
||||
"App::PropertyIntegerList",
|
||||
@@ -824,7 +831,9 @@ class Writer(object):
|
||||
else:
|
||||
s["Exec Solver"] = "After simulation"
|
||||
s["Procedure"] = sifio.FileAttr("ResultOutputSolve/ResultOutputSolver")
|
||||
s["Output File Name"] = sifio.FileAttr("FreeCAD")
|
||||
s["Vtu Format"] = True
|
||||
s["Vtu Time Collection"] = True
|
||||
if self.unit_schema == Units.Scheme.SI2:
|
||||
s["Coordinate Scaling Revert"] = True
|
||||
Console.PrintMessage(
|
||||
|
||||
@@ -27,7 +27,6 @@ Simulation
|
||||
Coordinate Mapping(3) = Integer 1 2 3
|
||||
Coordinate Scaling = Real 0.001
|
||||
Coordinate System = String "Cartesian"
|
||||
Output Intervals = Integer 1
|
||||
Simulation Type = String "Steady State"
|
||||
Steady State Max Iterations = Integer 1
|
||||
Steady State Min Iterations = Integer 0
|
||||
@@ -57,8 +56,10 @@ Solver 2
|
||||
Coordinate Scaling Revert = Logical True
|
||||
Equation = String "ResultOutput"
|
||||
Exec Solver = String "After simulation"
|
||||
Output File Name = File "FreeCAD"
|
||||
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
|
||||
Vtu Format = Logical True
|
||||
Vtu Time Collection = Logical True
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
|
||||
@@ -27,7 +27,6 @@ Simulation
|
||||
Coordinate Mapping(3) = Integer 1 2 3
|
||||
Coordinate Scaling = Real 0.001
|
||||
Coordinate System = String "Cartesian"
|
||||
Output Intervals = Integer 1
|
||||
Simulation Type = String "Steady State"
|
||||
Steady State Max Iterations = Integer 1
|
||||
Steady State Min Iterations = Integer 0
|
||||
@@ -57,8 +56,10 @@ Solver 2
|
||||
Coordinate Scaling Revert = Logical True
|
||||
Equation = String "ResultOutput"
|
||||
Exec Solver = String "After simulation"
|
||||
Output File Name = File "FreeCAD"
|
||||
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
|
||||
Vtu Format = Logical True
|
||||
Vtu Time Collection = Logical True
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
|
||||
@@ -27,7 +27,6 @@ Simulation
|
||||
Coordinate Mapping(3) = Integer 1 2 3
|
||||
Coordinate Scaling = Real 0.001
|
||||
Coordinate System = String "Cartesian"
|
||||
Output Intervals = Integer 1
|
||||
Simulation Type = String "Steady State"
|
||||
Steady State Max Iterations = Integer 1
|
||||
Steady State Min Iterations = Integer 0
|
||||
@@ -57,8 +56,10 @@ Solver 2
|
||||
Coordinate Scaling Revert = Logical True
|
||||
Equation = String "ResultOutput"
|
||||
Exec Solver = String "After simulation"
|
||||
Output File Name = File "FreeCAD"
|
||||
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
|
||||
Vtu Format = Logical True
|
||||
Vtu Time Collection = Logical True
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
|
||||
@@ -27,7 +27,6 @@ Simulation
|
||||
Coordinate Mapping(3) = Integer 1 2 3
|
||||
Coordinate Scaling = Real 0.001
|
||||
Coordinate System = String "Cartesian"
|
||||
Output Intervals = Integer 1
|
||||
Simulation Type = String "Steady State"
|
||||
Steady State Max Iterations = Integer 1
|
||||
Steady State Min Iterations = Integer 0
|
||||
@@ -57,8 +56,10 @@ Solver 2
|
||||
Coordinate Scaling Revert = Logical True
|
||||
Equation = String "ResultOutput"
|
||||
Exec Solver = String "After simulation"
|
||||
Output File Name = File "FreeCAD"
|
||||
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
|
||||
Vtu Format = Logical True
|
||||
Vtu Time Collection = Logical True
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
|
||||
@@ -27,7 +27,6 @@ Simulation
|
||||
Coordinate Mapping(3) = Integer 1 2 3
|
||||
Coordinate Scaling = Real 0.001
|
||||
Coordinate System = String "Cartesian"
|
||||
Output Intervals = Integer 1
|
||||
Simulation Type = String "Steady State"
|
||||
Steady State Max Iterations = Integer 1
|
||||
Steady State Min Iterations = Integer 0
|
||||
@@ -57,8 +56,10 @@ Solver 2
|
||||
Coordinate Scaling Revert = Logical True
|
||||
Equation = String "ResultOutput"
|
||||
Exec Solver = String "After simulation"
|
||||
Output File Name = File "FreeCAD"
|
||||
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
|
||||
Vtu Format = Logical True
|
||||
Vtu Time Collection = Logical True
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
|
||||
Reference in New Issue
Block a user