[FEM] Elmer writer: sort out heat equation
- sort out heat equation - next step to refactor writer.py
This commit is contained in:
@@ -259,6 +259,7 @@ SET(FemSolverElmerEquations_SRCS
|
||||
femsolver/elmer/equations/flow.py
|
||||
femsolver/elmer/equations/flux.py
|
||||
femsolver/elmer/equations/heat.py
|
||||
femsolver/elmer/equations/heat_writer.py
|
||||
femsolver/elmer/equations/linear.py
|
||||
femsolver/elmer/equations/nonlinear.py
|
||||
)
|
||||
|
||||
199
src/Mod/Fem/femsolver/elmer/equations/heat_writer.py
Normal file
199
src/Mod/Fem/femsolver/elmer/equations/heat_writer.py
Normal file
@@ -0,0 +1,199 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2023 Uwe Stöhr <uwestoehr@lyx.org> *
|
||||
# * *
|
||||
# * 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 FEM Heat Elmer writer"
|
||||
__author__ = "Uwe Stöhr"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
from .. import sifio
|
||||
from .. import writer as general_writer
|
||||
from femtools import membertools
|
||||
from femmesh import meshtools
|
||||
from . import heat
|
||||
|
||||
class Heatwriter:
|
||||
|
||||
def __init__(self, writer, solver):
|
||||
self.write = writer
|
||||
self.solver = solver
|
||||
|
||||
def getHeatSolver(self, equation):
|
||||
# check if we need to update the equation
|
||||
self._updateHeatSolver(equation)
|
||||
# output the equation parameters
|
||||
s = self.write.createNonlinearSolver(equation)
|
||||
s["Equation"] = equation.Name
|
||||
s["Procedure"] = sifio.FileAttr("HeatSolve/HeatSolver")
|
||||
s["Bubbles"] = equation.Bubbles
|
||||
s["Exec Solver"] = "Always"
|
||||
s["Optimize Bandwidth"] = True
|
||||
s["Stabilize"] = equation.Stabilize
|
||||
s["Variable"] = self.write.getUniqueVarName("Temperature")
|
||||
return s
|
||||
|
||||
def handleHeatConstants(self):
|
||||
self.write.constant(
|
||||
"Stefan Boltzmann",
|
||||
self.write.convert(self.write.constsdef["StefanBoltzmann"], "M/(O^4*T^3)")
|
||||
)
|
||||
|
||||
def handleHeatEquation(self, bodies, equation):
|
||||
for b in bodies:
|
||||
if equation.Convection != "None":
|
||||
self.write.equation(b, "Convection", equation.Convection)
|
||||
if equation.PhaseChangeModel != "None":
|
||||
self.write.equation(b, "Phase Change Model", equation.PhaseChangeModel)
|
||||
|
||||
def _updateHeatSolver(self, equation):
|
||||
# updates older Heat equations
|
||||
if not hasattr(equation, "Convection"):
|
||||
equation.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Convection",
|
||||
"Equation",
|
||||
"Type of convection to be used"
|
||||
)
|
||||
equation.Convection = heat.CONVECTION_TYPE
|
||||
equation.Convection = "None"
|
||||
if not hasattr(equation, "PhaseChangeModel"):
|
||||
equation.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"PhaseChangeModel",
|
||||
"Equation",
|
||||
"Model for phase change"
|
||||
)
|
||||
equation.PhaseChangeModel = heat.PHASE_CHANGE_MODEL
|
||||
equation.PhaseChangeModel = "None"
|
||||
|
||||
def handleHeatBndConditions(self):
|
||||
i = -1
|
||||
for obj in self.write.getMember("Fem::ConstraintTemperature"):
|
||||
i = i + 1
|
||||
femobjects = membertools.get_several_member(self.write.analysis, "Fem::ConstraintTemperature")
|
||||
femobjects[i]["Nodes"] = meshtools.get_femnodes_by_femobj_with_references(
|
||||
self.write.getSingleMember("Fem::FemMeshObject").FemMesh,
|
||||
femobjects[i]
|
||||
)
|
||||
NumberOfNodes = len(femobjects[i]["Nodes"])
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.ConstraintType == "Temperature":
|
||||
temp = self.write.getFromUi(obj.Temperature, "K", "O")
|
||||
self.write.boundary(name, "Temperature", temp)
|
||||
elif obj.ConstraintType == "CFlux":
|
||||
# the CFLUX property stores the value in µW
|
||||
# but the unit system is not aware of µW, only of mW
|
||||
flux = 0.001 * self.write.getFromUi(obj.CFlux, "mW", "M*L^2*T^-3")
|
||||
# CFLUX is the flux per mesh node
|
||||
flux = flux / NumberOfNodes
|
||||
self.write.boundary(name, "Temperature Load", flux)
|
||||
self.write.handled(obj)
|
||||
for obj in self.write.getMember("Fem::ConstraintHeatflux"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.ConstraintType == "Convection":
|
||||
film = self.write.getFromUi(obj.FilmCoef, "W/(m^2*K)", "M/(T^3*O)")
|
||||
temp = self.write.getFromUi(obj.AmbientTemp, "K", "O")
|
||||
self.write.boundary(name, "Heat Transfer Coefficient", film)
|
||||
self.write.boundary(name, "External Temperature", temp)
|
||||
elif obj.ConstraintType == "DFlux":
|
||||
flux = self.write.getFromUi(obj.DFlux, "W/m^2", "M*T^-3")
|
||||
self.write.boundary(name, "Heat Flux BC", True)
|
||||
self.write.boundary(name, "Heat Flux", flux)
|
||||
self.write.handled(obj)
|
||||
|
||||
def handleHeatInitial(self, bodies):
|
||||
obj = self.write.getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if obj is not None:
|
||||
for name in bodies:
|
||||
temp = self.write.getFromUi(obj.initialTemperature, "K", "O")
|
||||
self.write.initial(name, "Temperature", temp)
|
||||
self.write.handled(obj)
|
||||
|
||||
def _outputHeatBodyForce(self, obj, name):
|
||||
heatSource = self.write.getFromUi(obj.HeatSource, "W/kg", "L^2*T^-3")
|
||||
if heatSource == 0.0:
|
||||
# a zero heat would break Elmer (division by zero)
|
||||
raise general_writer.WriteError("The body heat source must not be zero!")
|
||||
self.write.bodyForce(name, "Heat Source", heatSource)
|
||||
|
||||
def handleHeatBodyForces(self, bodies):
|
||||
bodyHeats = self.write.getMember("Fem::ConstraintBodyHeatSource")
|
||||
for obj in bodyHeats:
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
self._outputHeatBodyForce(obj, name)
|
||||
self.write.handled(obj)
|
||||
else:
|
||||
# if there is only one body heat without a reference
|
||||
# add it to all bodies
|
||||
if len(bodyHeats) == 1:
|
||||
for name in bodies:
|
||||
self._outputHeatBodyForce(obj, name)
|
||||
else:
|
||||
raise general_writer.WriteError(
|
||||
"Several body heat constraints found without reference to a body.\n"
|
||||
"Please set a body for each body heat constraint."
|
||||
)
|
||||
self.write.handled(obj)
|
||||
|
||||
def handleHeatMaterial(self, bodies):
|
||||
tempObj = self.write.getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if tempObj is not None:
|
||||
refTemp = self.write.getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
for name in bodies:
|
||||
self.write.material(name, "Reference Temperature", refTemp)
|
||||
for obj in self.write.getMember("App::MaterialObject"):
|
||||
m = obj.Material
|
||||
refs = (
|
||||
obj.References[0][1]
|
||||
if obj.References
|
||||
else self.write.getAllBodies())
|
||||
for name in (n for n in refs if n in bodies):
|
||||
if "Density" not in m:
|
||||
raise general_writer.WriteError(
|
||||
"Used material does not specify the necessary 'Density'."
|
||||
)
|
||||
self.write.material(name, "Name", m["Name"])
|
||||
self.write.material(
|
||||
name, "Density",
|
||||
self.write.getDensity(m))
|
||||
if "ThermalConductivity" not in m:
|
||||
raise general_writer.WriteError(
|
||||
"Used material does not specify the necessary 'Thermal Conductivity'."
|
||||
)
|
||||
self.write.material(
|
||||
name, "Heat Conductivity",
|
||||
self.write.convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
|
||||
if "SpecificHeat" not in m:
|
||||
raise general_writer.WriteError(
|
||||
"Used material does not specify the necessary 'Specific Heat'."
|
||||
)
|
||||
self.write.material(
|
||||
name, "Heat Capacity",
|
||||
self.write.convert(m["SpecificHeat"], "L^2/(T^2*O)"))
|
||||
|
||||
## @}
|
||||
@@ -45,7 +45,6 @@ from . import sifio
|
||||
from . import solver as solverClass
|
||||
from .. import settings
|
||||
from femmesh import gmshtools
|
||||
from femmesh import meshtools
|
||||
from femtools import constants
|
||||
from femtools import femutils
|
||||
from femtools import membertools
|
||||
@@ -54,7 +53,7 @@ from .equations import electricforce
|
||||
from .equations import electrostatic_writer as ES_writer
|
||||
from .equations import flow
|
||||
from .equations import flux
|
||||
from .equations import heat
|
||||
from .equations import heat_writer
|
||||
|
||||
|
||||
_STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
|
||||
@@ -181,7 +180,7 @@ class Writer(object):
|
||||
.format(Units.listSchemas(self.unit_schema))
|
||||
)
|
||||
|
||||
def _getFromUi(self, value, unit, outputDim):
|
||||
def getFromUi(self, value, unit, outputDim):
|
||||
quantity = Units.Quantity(str(value) + str(unit))
|
||||
return self.convert(quantity, outputDim)
|
||||
|
||||
@@ -201,7 +200,7 @@ class Writer(object):
|
||||
}
|
||||
|
||||
def _writeMesh(self):
|
||||
mesh = self._getSingleMember("Fem::FemMeshObject")
|
||||
mesh = self.getSingleMember("Fem::FemMeshObject")
|
||||
unvPath = os.path.join(self.directory, "mesh.unv")
|
||||
groups = []
|
||||
groups.extend(self._builder.getBodyNames())
|
||||
@@ -682,7 +681,7 @@ class Writer(object):
|
||||
for obj in self.getMember("Fem::ConstraintPressure"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
pressure = self._getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
|
||||
pressure = self.getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
|
||||
if not obj.Reversed:
|
||||
pressure *= -1
|
||||
self.boundary(name, "Normal Force", pressure)
|
||||
@@ -697,7 +696,7 @@ class Writer(object):
|
||||
for obj in self.getMember("Fem::ConstraintForce"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
force = self._getFromUi(obj.Force, "N", "M*L*T^-2")
|
||||
force = self.getFromUi(obj.Force, "N", "M*L*T^-2")
|
||||
self.boundary(name, "Force 1", obj.DirectionVector.x * force)
|
||||
self.boundary(name, "Force 2", obj.DirectionVector.y * force)
|
||||
self.boundary(name, "Force 3", obj.DirectionVector.z * force)
|
||||
@@ -729,7 +728,7 @@ class Writer(object):
|
||||
pass
|
||||
|
||||
def _handleElasticityBodyForces(self, bodies):
|
||||
obj = self._getSingleMember("Fem::ConstraintSelfWeight")
|
||||
obj = self.getSingleMember("Fem::ConstraintSelfWeight")
|
||||
if obj is not None:
|
||||
for name in bodies:
|
||||
gravity = self.convert(self.constsdef["Gravity"], "L/T^2")
|
||||
@@ -775,13 +774,13 @@ class Writer(object):
|
||||
if equation.EigenAnalysis is True:
|
||||
density_needed = True
|
||||
break # there could be a second equation without frequency
|
||||
gravObj = self._getSingleMember("Fem::ConstraintSelfWeight")
|
||||
gravObj = self.getSingleMember("Fem::ConstraintSelfWeight")
|
||||
if gravObj is not None:
|
||||
density_needed = True
|
||||
# temperature
|
||||
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
tempObj = self.getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if tempObj is not None:
|
||||
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
refTemp = self.getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
for name in bodies:
|
||||
self.material(name, "Reference Temperature", refTemp)
|
||||
# get the material data for all bodies
|
||||
@@ -809,7 +808,7 @@ class Writer(object):
|
||||
if density_needed is True:
|
||||
self.material(
|
||||
name, "Density",
|
||||
self._getDensity(m)
|
||||
self.getDensity(m)
|
||||
)
|
||||
self.material(
|
||||
name, "Youngs Modulus",
|
||||
@@ -825,12 +824,6 @@ class Writer(object):
|
||||
self.convert(m["ThermalExpansionCoefficient"], "O^-1")
|
||||
)
|
||||
|
||||
def _getDensity(self, m):
|
||||
density = self.convert(m["Density"], "M/L^3")
|
||||
if self._getMeshDimension() == 2:
|
||||
density *= 1e3
|
||||
return density
|
||||
|
||||
def _getYoungsModulus(self, m):
|
||||
youngsModulus = self.convert(m["YoungsModulus"], "M/(L*T^2)")
|
||||
if self._getMeshDimension() == 2:
|
||||
@@ -929,7 +922,7 @@ class Writer(object):
|
||||
# check if we need to update the equation
|
||||
self._updateFlowSolver(equation)
|
||||
# output the equation parameters
|
||||
s = self._createNonlinearSolver(equation)
|
||||
s = self.createNonlinearSolver(equation)
|
||||
s["Equation"] = "Navier-Stokes"
|
||||
s["Procedure"] = sifio.FileAttr("FlowSolve/FlowSolver")
|
||||
if equation.DivDiscretization is True:
|
||||
@@ -1009,9 +1002,9 @@ class Writer(object):
|
||||
equation.Variable = "Flow Solution[Velocity:3 Pressure:1]"
|
||||
|
||||
def _handleFlowMaterial(self, bodies):
|
||||
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
tempObj = self.getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if tempObj is not None:
|
||||
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
refTemp = self.getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
for name in bodies:
|
||||
self.material(name, "Reference Temperature", refTemp)
|
||||
for obj in self.getMember("App::MaterialObject"):
|
||||
@@ -1025,7 +1018,7 @@ class Writer(object):
|
||||
if "Density" in m:
|
||||
self.material(
|
||||
name, "Density",
|
||||
self._getDensity(m)
|
||||
self.getDensity(m)
|
||||
)
|
||||
if "ThermalConductivity" in m:
|
||||
self.material(
|
||||
@@ -1033,7 +1026,7 @@ class Writer(object):
|
||||
self.convert(m["ThermalConductivity"], "M*L/(T^3*O)")
|
||||
)
|
||||
if "KinematicViscosity" in m:
|
||||
density = self._getDensity(m)
|
||||
density = self.getDensity(m)
|
||||
kViscosity = self.convert(m["KinematicViscosity"], "L^2/T")
|
||||
self.material(
|
||||
name, "Viscosity", kViscosity * density)
|
||||
@@ -1059,7 +1052,7 @@ class Writer(object):
|
||||
# initial pressure only makes sense for fluid material
|
||||
if self._isBodyMaterialFluid(name):
|
||||
pressure = float(obj.Pressure.getValueAs("Pa"))
|
||||
self._initial(name, "Pressure", pressure)
|
||||
self.initial(name, "Pressure", pressure)
|
||||
|
||||
def _handleFlowInitialPressure(self, bodies):
|
||||
initialPressures = self.getMember("Fem::ConstraintInitialPressure")
|
||||
@@ -1085,14 +1078,14 @@ class Writer(object):
|
||||
# flow only makes sense for fluid material
|
||||
if self._isBodyMaterialFluid(name):
|
||||
if obj.VelocityXEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
self._initial(name, "Velocity 1", velocity)
|
||||
velocity = self.getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
self.initial(name, "Velocity 1", velocity)
|
||||
if obj.VelocityYEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
self._initial(name, "Velocity 2", velocity)
|
||||
velocity = self.getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
self.initial(name, "Velocity 2", velocity)
|
||||
if obj.VelocityZEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
self._initial(name, "Velocity 3", velocity)
|
||||
velocity = self.getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
self.initial(name, "Velocity 3", velocity)
|
||||
|
||||
def _handleFlowInitialVelocity(self, bodies):
|
||||
initialVelocities = self.getMember("Fem::ConstraintInitialFlowVelocity")
|
||||
@@ -1119,13 +1112,13 @@ class Writer(object):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.VelocityXEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
velocity = self.getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
self.boundary(name, "Velocity 1", velocity)
|
||||
if obj.VelocityYEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
velocity = self.getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
self.boundary(name, "Velocity 2", velocity)
|
||||
if obj.VelocityZEnabled:
|
||||
velocity = self._getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
velocity = self.getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
self.boundary(name, "Velocity 3", velocity)
|
||||
if obj.NormalToBoundary:
|
||||
self.boundary(name, "Normal-Tangential Velocity", True)
|
||||
@@ -1133,7 +1126,7 @@ class Writer(object):
|
||||
for obj in self.getMember("Fem::ConstraintPressure"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
pressure = self._getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
|
||||
pressure = self.getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
|
||||
if obj.Reversed:
|
||||
pressure *= -1
|
||||
self.boundary(name, "External Pressure", pressure)
|
||||
@@ -1293,6 +1286,7 @@ class Writer(object):
|
||||
# Heat
|
||||
|
||||
def _handleHeat(self):
|
||||
HeatW = heat_writer.Heatwriter(self, self.solver)
|
||||
activeIn = []
|
||||
for equation in self.solver.Group:
|
||||
if femutils.is_of_type(equation, "Fem::EquationElmerHeat"):
|
||||
@@ -1300,172 +1294,16 @@ class Writer(object):
|
||||
activeIn = equation.References[0][1]
|
||||
else:
|
||||
activeIn = self.getAllBodies()
|
||||
solverSection = self._getHeatSolver(equation)
|
||||
solverSection = HeatW.getHeatSolver(equation)
|
||||
for body in activeIn:
|
||||
self._addSolver(body, solverSection)
|
||||
self._handleHeatEquation(activeIn, equation)
|
||||
HeatW.handleHeatEquation(activeIn, equation)
|
||||
if activeIn:
|
||||
self._handleHeatConstants()
|
||||
self._handleHeatBndConditions()
|
||||
self._handleHeatInitial(activeIn)
|
||||
self._handleHeatBodyForces(activeIn)
|
||||
self._handleHeatMaterial(activeIn)
|
||||
|
||||
def _getHeatSolver(self, equation):
|
||||
# check if we need to update the equation
|
||||
self._updateHeatSolver(equation)
|
||||
# output the equation parameters
|
||||
s = self._createNonlinearSolver(equation)
|
||||
s["Equation"] = equation.Name
|
||||
s["Procedure"] = sifio.FileAttr("HeatSolve/HeatSolver")
|
||||
s["Bubbles"] = equation.Bubbles
|
||||
s["Exec Solver"] = "Always"
|
||||
s["Optimize Bandwidth"] = True
|
||||
s["Stabilize"] = equation.Stabilize
|
||||
s["Variable"] = self.getUniqueVarName("Temperature")
|
||||
return s
|
||||
|
||||
def _handleHeatConstants(self):
|
||||
self.constant(
|
||||
"Stefan Boltzmann",
|
||||
self.convert(self.constsdef["StefanBoltzmann"], "M/(O^4*T^3)")
|
||||
)
|
||||
|
||||
def _handleHeatEquation(self, bodies, equation):
|
||||
for b in bodies:
|
||||
if equation.Convection != "None":
|
||||
self._equation(b, "Convection", equation.Convection)
|
||||
if equation.PhaseChangeModel != "None":
|
||||
self._equation(b, "Phase Change Model", equation.PhaseChangeModel)
|
||||
|
||||
def _updateHeatSolver(self, equation):
|
||||
# updates older Heat equations
|
||||
if not hasattr(equation, "Convection"):
|
||||
equation.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Convection",
|
||||
"Equation",
|
||||
"Type of convection to be used"
|
||||
)
|
||||
equation.Convection = heat.CONVECTION_TYPE
|
||||
equation.Convection = "None"
|
||||
if not hasattr(equation, "PhaseChangeModel"):
|
||||
equation.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"PhaseChangeModel",
|
||||
"Equation",
|
||||
"Model for phase change"
|
||||
)
|
||||
equation.PhaseChangeModel = heat.PHASE_CHANGE_MODEL
|
||||
equation.PhaseChangeModel = "None"
|
||||
|
||||
def _handleHeatBndConditions(self):
|
||||
i = -1
|
||||
for obj in self.getMember("Fem::ConstraintTemperature"):
|
||||
i = i + 1
|
||||
femobjects = membertools.get_several_member(self.analysis, "Fem::ConstraintTemperature")
|
||||
femobjects[i]["Nodes"] = meshtools.get_femnodes_by_femobj_with_references(
|
||||
self._getSingleMember("Fem::FemMeshObject").FemMesh,
|
||||
femobjects[i]
|
||||
)
|
||||
NumberOfNodes = len(femobjects[i]["Nodes"])
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.ConstraintType == "Temperature":
|
||||
temp = self._getFromUi(obj.Temperature, "K", "O")
|
||||
self.boundary(name, "Temperature", temp)
|
||||
elif obj.ConstraintType == "CFlux":
|
||||
# the CFLUX property stores the value in µW
|
||||
# but the unit system is not aware of µW, only of mW
|
||||
flux = 0.001 * self._getFromUi(obj.CFlux, "mW", "M*L^2*T^-3")
|
||||
# CFLUX is the flux per mesh node
|
||||
flux = flux / NumberOfNodes
|
||||
self.boundary(name, "Temperature Load", flux)
|
||||
self.handled(obj)
|
||||
for obj in self.getMember("Fem::ConstraintHeatflux"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.ConstraintType == "Convection":
|
||||
film = self._getFromUi(obj.FilmCoef, "W/(m^2*K)", "M/(T^3*O)")
|
||||
temp = self._getFromUi(obj.AmbientTemp, "K", "O")
|
||||
self.boundary(name, "Heat Transfer Coefficient", film)
|
||||
self.boundary(name, "External Temperature", temp)
|
||||
elif obj.ConstraintType == "DFlux":
|
||||
flux = self._getFromUi(obj.DFlux, "W/m^2", "M*T^-3")
|
||||
self.boundary(name, "Heat Flux BC", True)
|
||||
self.boundary(name, "Heat Flux", flux)
|
||||
self.handled(obj)
|
||||
|
||||
def _handleHeatInitial(self, bodies):
|
||||
obj = self._getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if obj is not None:
|
||||
for name in bodies:
|
||||
temp = self._getFromUi(obj.initialTemperature, "K", "O")
|
||||
self._initial(name, "Temperature", temp)
|
||||
self.handled(obj)
|
||||
|
||||
def _outputHeatBodyForce(self, obj, name):
|
||||
heatSource = self._getFromUi(obj.HeatSource, "W/kg", "L^2*T^-3")
|
||||
if heatSource == 0.0:
|
||||
# a zero heat would break Elmer (division by zero)
|
||||
raise WriteError("The body heat source must not be zero!")
|
||||
self._bodyForce(name, "Heat Source", heatSource)
|
||||
|
||||
def _handleHeatBodyForces(self, bodies):
|
||||
bodyHeats = self.getMember("Fem::ConstraintBodyHeatSource")
|
||||
for obj in bodyHeats:
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
self._outputHeatBodyForce(obj, name)
|
||||
self.handled(obj)
|
||||
else:
|
||||
# if there is only one body heat without a reference
|
||||
# add it to all bodies
|
||||
if len(bodyHeats) == 1:
|
||||
for name in bodies:
|
||||
self._outputHeatBodyForce(obj, name)
|
||||
else:
|
||||
raise WriteError(
|
||||
"Several body heat constraints found without reference to a body.\n"
|
||||
"Please set a body for each body heat constraint."
|
||||
)
|
||||
self.handled(obj)
|
||||
|
||||
def _handleHeatMaterial(self, bodies):
|
||||
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
|
||||
if tempObj is not None:
|
||||
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
|
||||
for name in bodies:
|
||||
self.material(name, "Reference Temperature", refTemp)
|
||||
for obj in self.getMember("App::MaterialObject"):
|
||||
m = obj.Material
|
||||
refs = (
|
||||
obj.References[0][1]
|
||||
if obj.References
|
||||
else self.getAllBodies())
|
||||
for name in (n for n in refs if n in bodies):
|
||||
if "Density" not in m:
|
||||
raise WriteError(
|
||||
"Used material does not specify the necessary 'Density'."
|
||||
)
|
||||
self.material(name, "Name", m["Name"])
|
||||
self.material(
|
||||
name, "Density",
|
||||
self._getDensity(m))
|
||||
if "ThermalConductivity" not in m:
|
||||
raise WriteError(
|
||||
"Used material does not specify the necessary 'Thermal Conductivity'."
|
||||
)
|
||||
self.material(
|
||||
name, "Heat Conductivity",
|
||||
self.convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
|
||||
if "SpecificHeat" not in m:
|
||||
raise WriteError(
|
||||
"Used material does not specify the necessary 'Specific Heat'."
|
||||
)
|
||||
self.material(
|
||||
name, "Heat Capacity",
|
||||
self.convert(m["SpecificHeat"], "L^2/(T^2*O)"))
|
||||
HeatW.handleHeatConstants()
|
||||
HeatW.handleHeatBndConditions()
|
||||
HeatW.handleHeatInitial(activeIn)
|
||||
HeatW.handleHeatBodyForces(activeIn)
|
||||
HeatW.handleHeatMaterial(activeIn)
|
||||
|
||||
#-------------------------------------------------------------------------------------------
|
||||
# Solver handling
|
||||
@@ -1544,7 +1382,7 @@ class Writer(object):
|
||||
str(equation.NonlinearNewtonAfterTolerance)
|
||||
)
|
||||
|
||||
def _createNonlinearSolver(self, equation):
|
||||
def createNonlinearSolver(self, equation):
|
||||
# first check if we have to update
|
||||
self._updateNonlinearSolver(equation)
|
||||
# write the linear solver
|
||||
@@ -1589,6 +1427,12 @@ class Writer(object):
|
||||
return "KinematicViscosity" in m
|
||||
return False
|
||||
|
||||
def getDensity(self, m):
|
||||
density = self.convert(m["Density"], "M/L^3")
|
||||
if self._getMeshDimension() == 2:
|
||||
density *= 1e3
|
||||
return density
|
||||
|
||||
def _hasExpression(self, equation):
|
||||
for (obj, exp) in equation.ExpressionEngine:
|
||||
if obj == equation:
|
||||
@@ -1606,7 +1450,7 @@ class Writer(object):
|
||||
return varName
|
||||
|
||||
def getAllBodies(self):
|
||||
obj = self._getSingleMember("Fem::FemMeshObject")
|
||||
obj = self.getSingleMember("Fem::FemMeshObject")
|
||||
bodyCount = 0
|
||||
prefix = ""
|
||||
if obj.Part.Shape.Solids:
|
||||
@@ -1621,7 +1465,7 @@ class Writer(object):
|
||||
return [prefix + str(i + 1) for i in range(bodyCount)]
|
||||
|
||||
def _getMeshDimension(self):
|
||||
obj = self._getSingleMember("Fem::FemMeshObject")
|
||||
obj = self.getSingleMember("Fem::FemMeshObject")
|
||||
if obj.Part.Shape.Solids:
|
||||
return 3
|
||||
if obj.Part.Shape.Faces:
|
||||
@@ -1665,7 +1509,7 @@ class Writer(object):
|
||||
def constant(self, key, attr):
|
||||
self._builder.constant(key, attr)
|
||||
|
||||
def _initial(self, body, key, attr):
|
||||
def initial(self, body, key, attr):
|
||||
self._builder.initial(body, key, attr)
|
||||
|
||||
def material(self, body, key, attr):
|
||||
@@ -1689,7 +1533,7 @@ class Writer(object):
|
||||
def getMember(self, t):
|
||||
return membertools.get_member(self.analysis, t)
|
||||
|
||||
def _getSingleMember(self, t):
|
||||
def getSingleMember(self, t):
|
||||
return membertools.get_single_member(self.analysis, t)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user