diff --git a/src/Base/UnitsSchemaInternal.cpp b/src/Base/UnitsSchemaInternal.cpp
index 4b141fd247..c17cbbe99e 100644
--- a/src/Base/UnitsSchemaInternal.cpp
+++ b/src/Base/UnitsSchemaInternal.cpp
@@ -551,7 +551,7 @@ UnitsSchemaInternal::schemaTranslate(const Quantity& quant, double& factor, QStr
}
}
else if (unit == Unit::DissipationRate) {
- unitString = QString::fromLatin1("m^2/s^3");
+ unitString = QString::fromLatin1("W/kg");
factor = 1e6;
}
else if (unit == Unit::InverseLength) {
diff --git a/src/Base/UnitsSchemaMKS.cpp b/src/Base/UnitsSchemaMKS.cpp
index 96034a9495..5d921e8166 100644
--- a/src/Base/UnitsSchemaMKS.cpp
+++ b/src/Base/UnitsSchemaMKS.cpp
@@ -538,7 +538,7 @@ QString UnitsSchemaMKS::schemaTranslate(const Quantity& quant, double& factor, Q
}
}
else if (unit == Unit::DissipationRate) {
- unitString = QString::fromLatin1("m^2/s^3");
+ unitString = QString::fromLatin1("W/kg");
factor = 1e6;
}
else if (unit == Unit::InverseLength) {
diff --git a/src/Mod/Fem/Gui/Resources/ui/BodyHeatSource.ui b/src/Mod/Fem/Gui/Resources/ui/BodyHeatSource.ui
index e263d118a6..50a3fd7461 100644
--- a/src/Mod/Fem/Gui/Resources/ui/BodyHeatSource.ui
+++ b/src/Mod/Fem/Gui/Resources/ui/BodyHeatSource.ui
@@ -13,56 +13,115 @@
Analysis feature properties
-
+
-
-
-
- Body heat in W/kg:
+
+
+
+ 0
+ 0
+
-
-
- -
-
-
- Qt::Horizontal
+
+
-
-
- 130
- 19
-
-
-
-
- -
-
-
- true
-
-
-
- 100
- 20
-
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- true
-
-
- 0.000000000000000
-
-
- 1000000000000000000000.000000000000000
-
-
- 50.000000000000000
-
-
- 0.000000000000000
+
+ Heat Source
+
+
-
+
+
+ Mode:
+
+
+
+ -
+
+
+ -
+
+
+ Total Power:
+
+
+
+ -
+
+
+ true
+
+
+ W
+
+
+
+ 100
+ 20
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ true
+
+
+ 0.000000000000000
+
+
+ 1000000000000000000000.000000000000000
+
+
+ 50.000000000000000
+
+
+ 0.000000000000000
+
+
+
+ -
+
+
+ Dissipation Rate:
+
+
+
+ -
+
+
+ true
+
+
+ W/kg
+
+
+
+ 100
+ 20
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ true
+
+
+ 0.000000000000000
+
+
+ 1000000000000000000000.000000000000000
+
+
+ 50.000000000000000
+
+
+ 0.000000000000000
+
+
+
+
diff --git a/src/Mod/Fem/femobjects/constraint_bodyheatsource.py b/src/Mod/Fem/femobjects/constraint_bodyheatsource.py
index 92c9cc0aac..ac1f4d23ac 100644
--- a/src/Mod/Fem/femobjects/constraint_bodyheatsource.py
+++ b/src/Mod/Fem/femobjects/constraint_bodyheatsource.py
@@ -1,6 +1,7 @@
# ***************************************************************************
# * Copyright (c) 2017 Markus Hovorka *
# * Copyright (c) 2020 Bernd Hahnebach *
+# * Copyright (c) 2024 Mario Passaglia *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -23,15 +24,19 @@
# ***************************************************************************
__title__ = "FreeCAD FEM constraint body heat source document object"
-__author__ = "Markus Hovorka, Bernd Hahnebach"
+__author__ = "Markus Hovorka, Bernd Hahnebach, Mario Passaglia"
__url__ = "https://www.freecad.org"
## @package constraint_bodyheatsource
# \ingroup FEM
# \brief constraint body heat source object
+import FreeCAD
+
from . import base_fempythonobject
+_PropHelper = base_fempythonobject._PropHelper
+
class ConstraintBodyHeatSource(base_fempythonobject.BaseFemPythonObject):
@@ -39,18 +44,56 @@ class ConstraintBodyHeatSource(base_fempythonobject.BaseFemPythonObject):
def __init__(self, obj):
super(ConstraintBodyHeatSource, self).__init__(obj)
- self.add_properties(obj)
+
+ for prop in self._get_properties():
+ prop.add_to_object(obj)
+
+
+ def _get_properties(self):
+ prop = []
+
+ prop.append(_PropHelper(
+ type = "App::PropertyDissipationRate",
+ name = "DissipationRate",
+ group = "Constraint Body Heat Source",
+ doc = "Power dissipated per unit mass",
+ value = "0 W/kg"
+ )
+ )
+ prop.append(_PropHelper(
+ type = "App::PropertyPower",
+ name = "TotalPower",
+ group = "Constraint Body Heat Source",
+ doc = "Total power dissipated",
+ value = "0 W"
+ )
+ )
+ prop.append(_PropHelper(
+ type = "App::PropertyEnumeration",
+ name = "Mode",
+ group = "Constraint Body Heat Source",
+ doc = "Switch quantity input mode",
+ value = ["Dissipation Rate", "Total Power"]
+ )
+ )
+
+ return prop
+
def onDocumentRestored(self, obj):
- self.add_properties(obj)
+ # update old project with new properties
+ for prop in self._get_properties():
+ try:
+ obj.getPropertyByName(prop.name)
+ except:
+ prop.add_to_object(obj)
- def add_properties(self, obj):
- if not hasattr(obj, "HeatSource"):
- obj.addProperty(
- "App::PropertyFloat",
- "HeatSource",
- "Base",
- "Body heat source"
- )
- obj.setPropertyStatus("HeatSource", "LockDynamic")
- obj.HeatSource = 0.0
+ # migrate old HeatSource property
+ try:
+ value = obj.getPropertyByName("HeatSource")
+ obj.DissipationRate = FreeCAD.Units.Quantity(value, "W/kg")
+ obj.Mode = "Dissipation Rate"
+ obj.setPropertyStatus("HeatSource", "-LockDynamic")
+ obj.removeProperty("HeatSource")
+ except:
+ pass
diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_bodyheatsource.py b/src/Mod/Fem/femsolver/calculix/write_constraint_bodyheatsource.py
index 075209de84..16232acc55 100644
--- a/src/Mod/Fem/femsolver/calculix/write_constraint_bodyheatsource.py
+++ b/src/Mod/Fem/femsolver/calculix/write_constraint_bodyheatsource.py
@@ -25,8 +25,8 @@ __title__ = "FreeCAD FEM calculix constraint body heat source"
__author__ = "Mario Passaglia"
__url__ = "https://www.freecad.org"
-
import FreeCAD
+import itertools
def get_analysis_types():
@@ -70,22 +70,29 @@ def write_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
# floats read from ccx should use {:.13G}, see comment in writer module
# search referenced material
- ref = bodyheatsource_obj.References
+ ref = bodyheatsource_obj.References[0]
+ ref_feat = ref[0]
+ ref_sub_obj = ref[1][0]
density = None
for mat in ccxwriter.member.mats_linear:
- for mat_ref in mat["Object"].References:
- if mat_ref[0] == ref[0][0]:
- density = FreeCAD.Units.Quantity(mat["Object"].Material["Density"])
- break
+ mat_ref = [*itertools.chain(*[itertools.product([i[0]],i[1]) for i in mat["Object"].References])]
+ if (ref_feat, ref_sub_obj) in mat_ref:
+ density = FreeCAD.Units.Quantity(mat["Object"].Material["Density"])
+ break
if not density:
# search material without references
for mat in ccxwriter.member.mats_linear:
if not mat["Object"].References:
density = FreeCAD.Units.Quantity(mat["Object"].Material["Density"])
+ break
- # get some data from the bodyheatsource_obj (is in power per unit mass)
- heat = FreeCAD.Units.Quantity(bodyheatsource_obj.HeatSource, "m^2/s^3") * density
+ # get data from the bodyheatsource_obj (DissipationRate is in power per unit mass)
+ if bodyheatsource_obj.Mode == "Dissipation Rate":
+ heat = bodyheatsource_obj.DissipationRate * density
+ elif bodyheatsource_obj.Mode == "Total Power":
+ volume = ref_feat.getSubObject(ref_sub_obj).Volume
+ heat = bodyheatsource_obj.TotalPower / FreeCAD.Units.Quantity(volume, "mm^3")
# write to file
f.write("*DFLUX\n")
f.write(
diff --git a/src/Mod/Fem/femsolver/elmer/equations/heat_writer.py b/src/Mod/Fem/femsolver/elmer/equations/heat_writer.py
index 6478971840..7fa9231269 100644
--- a/src/Mod/Fem/femsolver/elmer/equations/heat_writer.py
+++ b/src/Mod/Fem/femsolver/elmer/equations/heat_writer.py
@@ -30,6 +30,9 @@ __url__ = "https://www.freecad.org"
## \addtogroup FEM
# @{
+import itertools
+import FreeCAD
+
from .. import sifio
from .. import writer as general_writer
from femtools import membertools
@@ -138,7 +141,29 @@ class Heatwriter:
self.write.handled(tempObj)
def _outputHeatBodyForce(self, obj, name):
- heatSource = self.write.getFromUi(obj.HeatSource, "W/kg", "L^2*T^-3")
+ if obj.Mode == "Dissipation Rate":
+ heatSource = obj.DissipationRate.getValueAs("W/kg").Value
+
+ elif obj.Mode == "Total Power":
+ ref = obj.References[0]
+ ref_feat = ref[0]
+ ref_sub_obj = ref[1][0]
+ density = None
+ for mat in self.write.getMember("App::MaterialObject"):
+ mat_ref = [*itertools.chain(*[itertools.product([i[0]],i[1]) for i in mat.References])]
+ if (ref_feat, ref_sub_obj) in mat_ref:
+ density = FreeCAD.Units.Quantity(mat.Material["Density"])
+ break
+
+ if not density:
+ # search material without references
+ for mat in self.write.getMember("App::MaterialObject"):
+ if not mat.References:
+ density = FreeCAD.Units.Quantity(mat.Material["Density"])
+ break
+ volume = ref_feat.getSubObject(ref_sub_obj).Volume
+ heatSource = (obj.TotalPower / (density*FreeCAD.Units.Quantity(volume, "mm^3"))).getValueAs("W/kg").Value
+
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!")
diff --git a/src/Mod/Fem/femtaskpanels/task_constraint_bodyheatsource.py b/src/Mod/Fem/femtaskpanels/task_constraint_bodyheatsource.py
index 9809c57b85..2893073b40 100644
--- a/src/Mod/Fem/femtaskpanels/task_constraint_bodyheatsource.py
+++ b/src/Mod/Fem/femtaskpanels/task_constraint_bodyheatsource.py
@@ -29,6 +29,8 @@ __url__ = "https://www.freecad.org"
# \ingroup FEM
# \brief task panel for constraint bodyheatsource object
+from PySide import QtCore
+
import FreeCAD
import FreeCADGui
@@ -41,15 +43,35 @@ from femtools import membertools
class _TaskPanel(object):
def __init__(self, obj):
- self._obj = obj
+ self.obj = obj
- self._paramWidget = FreeCADGui.PySideUic.loadUi(
+ self.parameter_widget = FreeCADGui.PySideUic.loadUi(
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/BodyHeatSource.ui")
- self._initParamWidget()
+
+ self.init_parameter_widget()
+
+ QtCore.QObject.connect(
+ self.parameter_widget.qsb_dissipation_rate,
+ QtCore.SIGNAL("valueChanged(Base::Quantity)"),
+ self.dissipation_rate_changed
+ )
+
+ QtCore.QObject.connect(
+ self.parameter_widget.qsb_total_power,
+ QtCore.SIGNAL("valueChanged(Base::Quantity)"),
+ self.total_power_changed
+ )
+
+ QtCore.QObject.connect(
+ self.parameter_widget.cb_mode,
+ QtCore.SIGNAL("currentIndexChanged(int)"),
+ self.mode_changed
+ )
+
# geometry selection widget
# start with Solid in list!
- self._selectionWidget = selection_widgets.GeometryElementsSelection(
+ self.selection_widget = selection_widgets.GeometryElementsSelection(
obj.References,
["Solid", "Face"],
True,
@@ -57,7 +79,7 @@ class _TaskPanel(object):
)
# form made from param and selection widget
- self.form = [self._paramWidget, self._selectionWidget]
+ self.form = [self.selection_widget, self.parameter_widget]
analysis = obj.getParentGroup()
self._mesh = None
@@ -77,20 +99,22 @@ class _TaskPanel(object):
self._part.ViewObject.show()
def reject(self):
- self._restoreVisibility()
+ self.restore_visibility()
FreeCADGui.ActiveDocument.resetEdit()
return True
def accept(self):
- if self._obj.References != self._selectionWidget.references:
- self._obj.References = self._selectionWidget.references
- self._applyWidgetChanges()
- self._obj.Document.recompute()
+ self.obj.References = self.selection_widget.references
+ self.obj.DissipationRate = self.dissipation_rate
+ self.obj.TotalPower = self.total_power
+ self.obj.Mode = self.mode
+
+ self.obj.Document.recompute()
FreeCADGui.ActiveDocument.resetEdit()
- self._restoreVisibility()
+ self.restore_visibility()
return True
- def _restoreVisibility(self):
+ def restore_visibility(self):
if self._mesh is not None and self._part is not None:
if self._meshVisible:
self._mesh.ViewObject.show()
@@ -101,21 +125,36 @@ class _TaskPanel(object):
else:
self._part.ViewObject.hide()
- def _initParamWidget(self):
- self._paramWidget.bodyheatQSB.setProperty(
- 'value', self._obj.HeatSource)
- self._paramWidget.bodyheatQSB.setProperty("unit", "W/kg")
- FreeCADGui.ExpressionBinding(self._paramWidget.bodyheatQSB).bind(self._obj, "HeatSource")
- def _applyWidgetChanges(self):
- bodyheat = None
- try:
- bodyheat = self._paramWidget.bodyheatQSB.property('value').getValueAs("W/kg")
- except ValueError:
- FreeCAD.Console.PrintMessage(
- "Wrong input. Not recognised input: '{}' "
- "Body heat has not been set.\n"
- .format(self._paramWidget.bodyheatQSB.text())
- )
- if bodyheat is not None:
- self._obj.HeatSource = float(bodyheat)
+ def init_parameter_widget(self):
+ self.dissipation_rate = self.obj.DissipationRate
+ self.total_power = self.obj.TotalPower
+ FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_dissipation_rate)\
+ .bind(self.obj, "DissipationRate")
+ self.parameter_widget.qsb_dissipation_rate.setProperty("value", self.dissipation_rate)
+
+ FreeCADGui.ExpressionBinding(self.parameter_widget.qsb_total_power)\
+ .bind(self.obj, "TotalPower")
+ self.parameter_widget.qsb_total_power.setProperty("value", self.total_power)
+
+ self.mode = self.obj.Mode
+ self.mode_enum = self.obj.getEnumerationsOfProperty("Mode")
+ self.parameter_widget.cb_mode.addItems(self.mode_enum)
+ index = self.mode_enum.index(self.mode)
+ self.parameter_widget.cb_mode.setCurrentIndex(index)
+ self.mode_changed(index)
+
+ def dissipation_rate_changed(self, base_quantity_value):
+ self.dissipation_rate = base_quantity_value
+
+ def total_power_changed(self, base_quantity_value):
+ self.total_power = base_quantity_value
+
+ def mode_changed(self, index):
+ self.mode = self.mode_enum[index]
+ if self.mode == "Dissipation Rate":
+ self.parameter_widget.qsb_dissipation_rate.setEnabled(True)
+ self.parameter_widget.qsb_total_power.setEnabled(False)
+ elif self.mode == "Total Power":
+ self.parameter_widget.qsb_dissipation_rate.setEnabled(False)
+ self.parameter_widget.qsb_total_power.setEnabled(True)