[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:
Uwe
2023-03-26 08:47:04 +02:00
parent b0eec67163
commit bd2b95562b
8 changed files with 118 additions and 10 deletions

View File

@@ -165,6 +165,14 @@ class Proxy(solverbase.Proxy):
4 | 8
)
obj.addProperty(
"App::PropertyLinkList",
"ElmerTimeResults",
"Base",
"",
4 | 8
)
obj.addProperty(
"App::PropertyLink",
"ElmerOutput",

View File

@@ -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:

View File

@@ -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(

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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