Fem: Improve constraint BodyHeatSource

This commit is contained in:
marioalexis
2024-06-02 17:06:21 -03:00
parent d8f1ade17d
commit 108687b938
7 changed files with 272 additions and 99 deletions

View File

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

View File

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

View File

@@ -13,56 +13,115 @@
<property name="windowTitle">
<string>Analysis feature properties</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="pressureLbl">
<property name="text">
<string>Body heat in W/kg:</string>
<widget class="QGroupBox" name="gpb_heat_source">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<property name="toolTip">
<string/>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>130</width>
<height>19</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="bodyheatQSB">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>20</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="keyboardTracking">
<bool>true</bool>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000000000000000000000.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
<property name="title">
<string>Heat Source</string>
</property>
<layout class="QGridLayout" name="g_layout_heat_source">
<item row="0" column="0">
<widget class="QLabel" name="lbl_dissipation_rate">
<property name="text">
<string>Mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_mode"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_total_power">
<property name="text">
<string>Total Power:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::QuantitySpinBox" name="qsb_total_power">
<property name="enabled">
<bool>true</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">W</string>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>20</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="keyboardTracking">
<bool>true</bool>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000000000000000000000.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_dissipation_rate">
<property name="text">
<string>Dissipation Rate:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::QuantitySpinBox" name="qsb_dissipation_rate">
<property name="enabled">
<bool>true</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">W/kg</string>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>20</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="keyboardTracking">
<bool>true</bool>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000000000000000000000.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>

View File

@@ -1,6 +1,7 @@
# ***************************************************************************
# * Copyright (c) 2017 Markus Hovorka <m.hovorka@live.de> *
# * Copyright (c) 2020 Bernd Hahnebach <bernd@bimstatik.org> *
# * Copyright (c) 2024 Mario Passaglia <mpassaglia@cbc.uba.ar> *
# * *
# * 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

View File

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

View File

@@ -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!")

View File

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