[FEM] Elmer: add support for 2D magnetodynamics
- adds the corresponding Elmer equation (it is now possible to do Elmer's tutorial example no. 16)
This commit is contained in:
@@ -265,6 +265,8 @@ SET(FemSolverElmerEquations_SRCS
|
||||
femsolver/elmer/equations/heat.py
|
||||
femsolver/elmer/equations/heat_writer.py
|
||||
femsolver/elmer/equations/linear.py
|
||||
femsolver/elmer/equations/magnetodynamic2D.py
|
||||
femsolver/elmer/equations/magnetodynamic2D_writer.py
|
||||
femsolver/elmer/equations/nonlinear.py
|
||||
)
|
||||
|
||||
|
||||
@@ -1344,6 +1344,136 @@ bool CmdFemCompEmConstraints::isActive()
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
// FEM_CompEmEquations (dropdown toolbar button for Electromagnetic equations)
|
||||
//===========================================================================
|
||||
|
||||
DEF_STD_CMD_ACL(CmdFEMCompEmEquations)
|
||||
|
||||
CmdFEMCompEmEquations::CmdFEMCompEmEquations()
|
||||
: Command("FEM_CompEmEquations")
|
||||
{
|
||||
sAppModule = "Fem";
|
||||
sGroup = QT_TR_NOOP("Fem");
|
||||
sMenuText = QT_TR_NOOP("Electromagnetic equations...");
|
||||
sToolTipText = QT_TR_NOOP(
|
||||
"Electromagnetic equations for the Elmer solver");
|
||||
sWhatsThis = "FEM_CompEmEquations";
|
||||
sStatusTip = sToolTipText;
|
||||
}
|
||||
|
||||
void CmdFEMCompEmEquations::activated(int iMsg)
|
||||
{
|
||||
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
|
||||
if (iMsg == 0)
|
||||
rcCmdMgr.runCommandByName("FEM_EquationElectricforce");
|
||||
else if (iMsg == 1)
|
||||
rcCmdMgr.runCommandByName("FEM_EquationElectrostatic");
|
||||
else if (iMsg == 2)
|
||||
rcCmdMgr.runCommandByName("FEM_EquationMagnetodynamic2D");
|
||||
else
|
||||
return;
|
||||
|
||||
// Since the default icon is reset when enabling/disabling the command we have
|
||||
// to explicitly set the icon of the used command.
|
||||
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
|
||||
assert(iMsg < a.size());
|
||||
pcAction->setIcon(a[iMsg]->icon());
|
||||
}
|
||||
|
||||
Gui::Action* CmdFEMCompEmEquations::createAction()
|
||||
{
|
||||
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
|
||||
pcAction->setDropDownMenu(true);
|
||||
applyCommandData(this->className(), pcAction);
|
||||
|
||||
QAction* cmd0 = pcAction->addAction(QString());
|
||||
cmd0->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_EquationElectricforce"));
|
||||
QAction* cmd1 = pcAction->addAction(QString());
|
||||
cmd1->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_EquationElectrostatic"));
|
||||
QAction* cmd2 = pcAction->addAction(QString());
|
||||
cmd2->setIcon(Gui::BitmapFactory().iconFromTheme("FEM_EquationMagnetodynamic2D"));
|
||||
|
||||
_pcAction = pcAction;
|
||||
languageChange();
|
||||
|
||||
pcAction->setIcon(cmd0->icon());
|
||||
int defaultId = 0;
|
||||
pcAction->setProperty("defaultAction", QVariant(defaultId));
|
||||
|
||||
return pcAction;
|
||||
}
|
||||
|
||||
void CmdFEMCompEmEquations::languageChange()
|
||||
{
|
||||
Command::languageChange();
|
||||
|
||||
if (!_pcAction)
|
||||
return;
|
||||
|
||||
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
|
||||
|
||||
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
|
||||
Gui::Command* EquationElectricforce = rcCmdMgr.getCommandByName("FEM_EquationElectricforce");
|
||||
if (EquationElectricforce) {
|
||||
QAction* cmd0 = a[0];
|
||||
cmd0->setText(QApplication::translate("FEM_EquationElectricforce",
|
||||
EquationElectricforce->getMenuText()));
|
||||
cmd0->setToolTip(QApplication::translate("FEM_EquationElectricforce",
|
||||
EquationElectricforce->getToolTipText()));
|
||||
cmd0->setStatusTip(QApplication::translate("FEM_EquationElectricforce",
|
||||
EquationElectricforce->getStatusTip()));
|
||||
}
|
||||
|
||||
Gui::Command* EquationElectrostatic = rcCmdMgr.getCommandByName("FEM_EquationElectrostatic");
|
||||
if (EquationElectrostatic) {
|
||||
QAction* cmd1 = a[1];
|
||||
cmd1->setText(QApplication::translate("FEM_EquationElectrostatic",
|
||||
EquationElectrostatic->getMenuText()));
|
||||
cmd1->setToolTip(QApplication::translate("FEM_EquationElectrostatic",
|
||||
EquationElectrostatic->getToolTipText()));
|
||||
cmd1->setStatusTip(QApplication::translate("FEM_EquationElectrostatic",
|
||||
EquationElectrostatic->getStatusTip()));
|
||||
}
|
||||
|
||||
Gui::Command* EquationMagnetodynamic2D =
|
||||
rcCmdMgr.getCommandByName("FEM_EquationMagnetodynamic2D");
|
||||
if (EquationMagnetodynamic2D) {
|
||||
QAction* cmd2 = a[2];
|
||||
cmd2->setText(QApplication::translate("FEM_EquationMagnetodynamic2D",
|
||||
EquationMagnetodynamic2D->getMenuText()));
|
||||
cmd2->setToolTip(QApplication::translate("FEM_EquationMagnetodynamic2D",
|
||||
EquationMagnetodynamic2D->getToolTipText()));
|
||||
cmd2->setStatusTip(QApplication::translate("FEM_EquationMagnetodynamic2D",
|
||||
EquationMagnetodynamic2D->getStatusTip()));
|
||||
}
|
||||
}
|
||||
|
||||
bool CmdFEMCompEmEquations::isActive()
|
||||
{
|
||||
// only if there is an active analysis
|
||||
Fem::FemAnalysis* ActiveAnalysis =
|
||||
FemGui::ActiveAnalysisObserver::instance()->getActiveObject();
|
||||
if (!ActiveAnalysis
|
||||
|| !ActiveAnalysis->getTypeId().isDerivedFrom(Fem::FemAnalysis::getClassTypeId()))
|
||||
return false;
|
||||
|
||||
// only activate if a single Elmer object is selected
|
||||
auto results = getSelection().getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), Gui::ResolveMode::FollowLink); //.getObjectsOfType<femsolver.elmer.solver.Proxy>();
|
||||
if (results.size() == 1) {
|
||||
auto object = results.begin()->getObject();
|
||||
// FIXME: this is not unique since the Ccx solver object has the same type
|
||||
std::string Type = "Fem::FemSolverObjectPython";
|
||||
if (Type.compare(object->getTypeId().getName()) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
//================================================================================================
|
||||
// commands vtk post processing
|
||||
@@ -2203,6 +2333,9 @@ void CreateFemCommands()
|
||||
rcCmdMgr.addCommand(new CmdFemCreateNodesSet());
|
||||
rcCmdMgr.addCommand(new CmdFemDefineNodesSet());
|
||||
|
||||
// equations
|
||||
rcCmdMgr.addCommand(new CmdFEMCompEmEquations());
|
||||
|
||||
// vtk post processing
|
||||
#ifdef FC_USE_VTK
|
||||
rcCmdMgr.addCommand(new CmdFemPostClipFilter);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<file>icons/FEM_EquationFlow.svg</file>
|
||||
<file>icons/FEM_EquationFlux.svg</file>
|
||||
<file>icons/FEM_EquationHeat.svg</file>
|
||||
<file>icons/FEM_EquationMagnetodynamic2D.svg</file>
|
||||
|
||||
<!-- gui command icons: meshes -->
|
||||
<file>icons/FEM_CreateNodesSet.svg</file>
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg version="1.1" id="svg2" height="64" width="64" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs id="defs4">
|
||||
<linearGradient id="linearGradient959">
|
||||
<stop style="stop-color:#cc0000;stop-opacity:1" offset="0" id="stop957" />
|
||||
<stop id="stop955" offset="1" style="stop-color:#729fcf;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient940">
|
||||
<stop style="stop-color:#000000;stop-opacity:1" offset="0" id="stop936" />
|
||||
<stop style="stop-color:#000000;stop-opacity:0" offset="1" id="stop938" />
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient882">
|
||||
<stop id="stop876" offset="0" style="stop-color:#73d216;stop-opacity:1" />
|
||||
<stop style="stop-color:#729fcf;stop-opacity:1" offset="1" id="stop878" />
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient3802">
|
||||
<stop id="stop3804" offset="0" style="stop-color:#2e3436;stop-opacity:1" />
|
||||
<stop id="stop3806" offset="1" style="stop-color:#555753;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" y2="42" x2="47" y1="58" x1="49" id="linearGradient3808" xlink:href="#linearGradient3802" />
|
||||
<linearGradient id="linearGradient3767">
|
||||
<stop style="stop-color:#edd400;stop-opacity:1" offset="0" id="stop3769" />
|
||||
<stop style="stop-color:#fce94f;stop-opacity:1" offset="1" id="stop3771" />
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#linearGradient3767" id="linearGradient985" gradientUnits="userSpaceOnUse" gradientTransform="translate(0,-4)" x1="32.567314" y1="41.431564" x2="5.3436856" y2="34.638504" />
|
||||
<linearGradient gradientTransform="translate(-48.762712,7.1864407)" xlink:href="#linearGradient882" id="linearGradient4039" x1="89.610168" y1="46.389832" x2="91.338982" y2="32.542374" gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient id="linearGradient4033">
|
||||
<stop style="stop-color:#73d216;stop-opacity:1" offset="0" id="stop4035" />
|
||||
<stop id="stop4041" offset="0.5" style="stop-color:#729fcf;stop-opacity:1" />
|
||||
<stop style="stop-color:#cc0000;stop-opacity:1" offset="1" id="stop4037" />
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#linearGradient940" id="linearGradient942" x1="14.474576" y1="24.519833" x2="32" y2="28.99441" gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient xlink:href="#linearGradient959" id="linearGradient951" gradientUnits="userSpaceOnUse" x1="14.474576" y1="22.892714" x2="23.237288" y2="26.757122" />
|
||||
<linearGradient xlink:href="#linearGradient959" id="linearGradient968" gradientUnits="userSpaceOnUse" x1="12.863206" y1="12.118106" x2="12.863206" y2="46.133854" gradientTransform="matrix(0.94458924,0,0,0.94225828,21.548537,4.555805)" />
|
||||
</defs>
|
||||
<metadata id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[Alexander Gryson]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2017-03-11</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path style="fill:url(#linearGradient968);fill-opacity:1;stroke:#302b00;stroke-width:2.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" d="M 47.979387,15.97419 H 15.848478 V 48.025811 H 47.979387 Z" id="path987" />
|
||||
<g id="g1227" transform="matrix(0.94458924,0,0,0.94225826,1.5680741,1.7290257)" style="stroke-width:1.05998">
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 30.23622,15.11811 C 30.23622,15.11811 14.253491,-15.11811 0,-15.11811 C -14.253491,-15.11811 -24.781645,1.9496015 -30.23622,15.11811 C -34.575308,25.593594 -34.575308,38.658374 -30.23622,49.133858 C -24.781645,62.302367 -14.253491,79.370079 0,79.370079 C 14.253491,79.370079 30.23622,49.133858 30.23622,49.133858" id="path556" />
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 22.677165,15.11811 C 22.677165,15.11811 20.752297,0 15.11811,0 C 3.8497361,0 0.25590134,19.929541 0,31.195009 C -0.28178213,43.599819 2.7132999,63.970187 15.11811,64.251969 C 20.750844,64.37992 22.677165,49.133858 22.677165,49.133858" id="path621" />
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 15.11811,15.11811 C 15.11811,15.11811 7.7932043,25.51775 7.5590551,31.147069 C 7.2771021,37.925667 15.11811,49.133858 15.11811,49.133858" id="path623" />
|
||||
</g>
|
||||
<g id="g1227-3" transform="matrix(-0.94458924,0,0,-0.94225826,62.259791,62.270969)" style="stroke-width:1.05998">
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 30.23622,15.11811 C 30.23622,15.11811 14.253491,-15.11811 0,-15.11811 C -14.253491,-15.11811 -24.781645,1.9496015 -30.23622,15.11811 C -34.575308,25.593594 -34.575308,38.658374 -30.23622,49.133858 C -24.781645,62.302367 -14.253491,79.370079 0,79.370079 C 14.253491,79.370079 30.23622,49.133858 30.23622,49.133858" id="path556-9" />
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 22.677165,15.11811 C 22.677165,15.11811 20.752297,0 15.11811,0 C 3.8497361,0 0.25590134,19.929541 0,31.195009 C -0.28178213,43.599819 2.7132999,63.970187 15.11811,64.251969 C 20.750844,64.37992 22.677165,49.133858 22.677165,49.133858" id="path621-3" />
|
||||
<path style="fill:none;stroke:black;stroke-width:1.05998px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 15.11811,15.11811 C 15.11811,15.11811 7.7932043,25.51775 7.5590551,31.147069 C 7.2771021,37.925667 15.11811,49.133858 15.11811,49.133858" id="path623-3" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@@ -180,8 +180,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
<< "FEM_SolverZ88"
|
||||
<< "Separator"
|
||||
<< "FEM_EquationElasticity"
|
||||
<< "FEM_EquationElectricforce"
|
||||
<< "FEM_EquationElectrostatic"
|
||||
<< "FEM_CompEmEquations"
|
||||
<< "FEM_EquationFlow"
|
||||
<< "FEM_EquationFlux"
|
||||
<< "FEM_EquationHeat"
|
||||
@@ -347,8 +346,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
<< "FEM_SolverZ88"
|
||||
<< "Separator"
|
||||
<< "FEM_EquationElasticity"
|
||||
<< "FEM_EquationElectricforce"
|
||||
<< "FEM_EquationElectrostatic"
|
||||
<< "FEM_CompEmEquations"
|
||||
<< "FEM_EquationFlow"
|
||||
<< "FEM_EquationFlux"
|
||||
<< "FEM_EquationHeat"
|
||||
|
||||
@@ -802,6 +802,20 @@ def makeEquationHeat(
|
||||
return obj
|
||||
|
||||
|
||||
def makeEquationMagnetodynamic2D(
|
||||
doc,
|
||||
base_solver=None,
|
||||
name="Magnetodynamic2D"
|
||||
):
|
||||
"""makeEquationMagnetodynamic2D(document, [base_solver], [name]):
|
||||
creates a FEM magnetodynamic2D equation for a solver"""
|
||||
from femsolver.elmer.equations import magnetodynamic2D
|
||||
obj = magnetodynamic2D.create(doc, name)
|
||||
if base_solver:
|
||||
base_solver.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def makeSolverCalculixCcxTools(
|
||||
doc,
|
||||
name="SolverCcxTools"
|
||||
|
||||
@@ -516,6 +516,23 @@ class _EquationHeat(CommandManager):
|
||||
self.do_activated = "add_obj_on_gui_selobj_noset_edit"
|
||||
|
||||
|
||||
class _EquationMagnetodynamic2D(CommandManager):
|
||||
"The FEM_EquationMagnetodynamic2D command definition"
|
||||
|
||||
def __init__(self):
|
||||
super(_EquationMagnetodynamic2D, self).__init__()
|
||||
self.menutext = Qt.QT_TRANSLATE_NOOP(
|
||||
"FEM_EquationMagnetodynamic2D",
|
||||
"Magnetodynamic2D equation"
|
||||
)
|
||||
self.tooltip = Qt.QT_TRANSLATE_NOOP(
|
||||
"FEM_EquationMagnetodynamic2D",
|
||||
"Creates a FEM equation for\n2D magentodynamic forces"
|
||||
)
|
||||
self.is_active = "with_solver_elmer"
|
||||
self.do_activated = "add_obj_on_gui_selobj_noset_edit"
|
||||
|
||||
|
||||
class _Examples(CommandManager):
|
||||
"The FEM_Examples command definition"
|
||||
|
||||
@@ -1201,6 +1218,10 @@ FreeCADGui.addCommand(
|
||||
"FEM_EquationHeat",
|
||||
_EquationHeat()
|
||||
)
|
||||
FreeCADGui.addCommand(
|
||||
"FEM_EquationMagnetodynamic2D",
|
||||
_EquationMagnetodynamic2D()
|
||||
)
|
||||
FreeCADGui.addCommand(
|
||||
"FEM_Examples",
|
||||
_Examples()
|
||||
|
||||
141
src/Mod/Fem/femsolver/elmer/equations/magnetodynamic2D.py
Normal file
141
src/Mod/Fem/femsolver/elmer/equations/magnetodynamic2D.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# ***************************************************************************
|
||||
# * 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 solver Elmer equation object Magnetodynamic2D"
|
||||
__author__ = "Uwe Stöhr"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
#
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
from femtools import femutils
|
||||
from . import nonlinear
|
||||
from ... import equationbase
|
||||
|
||||
def create(doc, name="Magnetodynamic2D"):
|
||||
return femutils.createObject(
|
||||
doc, name, Proxy, ViewProxy)
|
||||
|
||||
|
||||
class Proxy(nonlinear.Proxy, equationbase.Magnetodynamic2DProxy):
|
||||
|
||||
Type = "Fem::EquationElmerMagnetodynamic2D"
|
||||
|
||||
def __init__(self, obj):
|
||||
super(Proxy, self).__init__(obj)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"IsHarmonic",
|
||||
"Magnetodynamic2D",
|
||||
"If the magnetic source is harmonically driven"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyFrequency",
|
||||
"AngularFrequency",
|
||||
"Magnetodynamic2D",
|
||||
"Frequency of the driving current"
|
||||
)
|
||||
obj.IsHarmonic = False
|
||||
obj.AngularFrequency = 0
|
||||
obj.Priority = 10
|
||||
|
||||
# the post processor options
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateCurrentDensity",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateElectricField",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateElementalFields",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateHarmonicLoss",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateJouleHeating",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateMagneticFieldStrength",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateMaxwellStress",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateNodalFields",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateNodalForces",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"CalculateNodalHeating",
|
||||
"Magnetodynamic2D",
|
||||
""
|
||||
)
|
||||
obj.CalculateCurrentDensity = False
|
||||
obj.CalculateElectricField = False
|
||||
# FIXME: at the moment FreeCAD's post processor cannot display elementary field
|
||||
# results, therefore disable despite this is by default on in Elmer
|
||||
obj.CalculateElementalFields = False
|
||||
obj.CalculateHarmonicLoss = False
|
||||
obj.CalculateJouleHeating = False
|
||||
obj.CalculateMagneticFieldStrength = False
|
||||
obj.CalculateMaxwellStress = False
|
||||
obj.CalculateNodalFields = True
|
||||
obj.CalculateNodalForces = False
|
||||
obj.CalculateNodalHeating = False
|
||||
|
||||
|
||||
class ViewProxy(nonlinear.ViewProxy, equationbase.Magnetodynamic2DViewProxy):
|
||||
pass
|
||||
|
||||
## @}
|
||||
208
src/Mod/Fem/femsolver/elmer/equations/magnetodynamic2D_writer.py
Normal file
208
src/Mod/Fem/femsolver/elmer/equations/magnetodynamic2D_writer.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# ***************************************************************************
|
||||
# * 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 Magnetodynamics2D Elmer writer"
|
||||
__author__ = "Uwe Stöhr"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
from FreeCAD import Console
|
||||
from FreeCAD import Units
|
||||
|
||||
from .. import sifio
|
||||
from .. import writer as general_writer
|
||||
|
||||
class MgDyn2Dwriter:
|
||||
|
||||
def __init__(self, writer, solver):
|
||||
self.write = writer
|
||||
self.solver = solver
|
||||
|
||||
def getMagnetodynamic2DSolver(self, equation):
|
||||
# output the equation parameters
|
||||
s = self.write.createNonlinearSolver(equation)
|
||||
if not equation.IsHarmonic:
|
||||
s["Equation"] = "MgDyn2D"
|
||||
s["Procedure"] = sifio.FileAttr("MagnetoDynamics2D/MagnetoDynamics2D")
|
||||
s["Variable"] = "Potential"
|
||||
else:
|
||||
s["Equation"] = "MgDyn2DHarmonic"
|
||||
s["Procedure"] = sifio.FileAttr("MagnetoDynamics2D/MagnetoDynamics2DHarmonic")
|
||||
s["Variable"] = "Potential[Potential Re:1 Potential Im:1]"
|
||||
s["Exec Solver"] = "Always"
|
||||
s["Optimize Bandwidth"] = True
|
||||
s["Stabilize"] = equation.Stabilize
|
||||
return s
|
||||
|
||||
def getMagnetodynamic2DSolverPost(self, equation):
|
||||
# output the equation parameters
|
||||
s = self.write.createNonlinearSolver(equation)
|
||||
s["Equation"] = "MgDyn2DPost"
|
||||
s["Exec Solver"] = "Always"
|
||||
s["Procedure"] = sifio.FileAttr("MagnetoDynamics/MagnetoDynamicsCalcFields")
|
||||
if equation.IsHarmonic:
|
||||
s["Angular Frequency"] = float(Units.Quantity(equation.AngularFrequency).Value)
|
||||
s["Potential Variable"] = "Potential"
|
||||
if equation.CalculateCurrentDensity is True:
|
||||
s["Calculate Current Density"] = True
|
||||
if equation.CalculateElectricField is True:
|
||||
s["Calculate Electric Field"] = True
|
||||
if equation.CalculateElementalFields is False:
|
||||
s["Calculate Elemental Fields"] = False
|
||||
if equation.CalculateHarmonicLoss is True:
|
||||
s["Calculate Harmonic Loss"] = True
|
||||
if equation.CalculateJouleHeating is True:
|
||||
s["Calculate Joule Heating"] = True
|
||||
if equation.CalculateMagneticFieldStrength is True:
|
||||
s["Calculate Magnetic Field Strength"] = True
|
||||
if equation.CalculateMaxwellStress is True:
|
||||
s["Calculate Maxwell Stress"] = True
|
||||
if equation.CalculateNodalFields is False:
|
||||
s["Calculate Nodal Fields"] = False
|
||||
if equation.CalculateNodalForces is True:
|
||||
s["Calculate Nodal Forces"] = True
|
||||
if equation.CalculateNodalHeating is True:
|
||||
s["Calculate Nodal Heating"] = True
|
||||
s["Optimize Bandwidth"] = True
|
||||
s["Stabilize"] = equation.Stabilize
|
||||
return s
|
||||
|
||||
def handleMagnetodynamic2DConstants(self):
|
||||
permeability = self.write.convert(
|
||||
self.write.constsdef["PermeabilityOfVacuum"],
|
||||
"M*L/(T^2*I^2)"
|
||||
)
|
||||
permeability = round(permeability, 20) # to get rid of numerical artifacts
|
||||
self.write.constant("Permeability Of Vacuum", permeability)
|
||||
|
||||
permittivity = self.write.convert(
|
||||
self.write.constsdef["PermittivityOfVacuum"],
|
||||
"T^4*I^2/(L^3*M)"
|
||||
)
|
||||
permittivity = round(permittivity, 20) # to get rid of numerical artifacts
|
||||
self.write.constant("Permittivity Of Vacuum", permittivity)
|
||||
|
||||
def handleMagnetodynamic2DMaterial(self, bodies):
|
||||
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 "ElectricalConductivity" not in m:
|
||||
Console.PrintMessage("m: {}\n".format(m))
|
||||
raise general_writer.WriteError(
|
||||
"The electrical conductivity must be specified for all materials.\n\n"
|
||||
)
|
||||
if "RelativePermeability" not in m:
|
||||
Console.PrintMessage("m: {}\n".format(m))
|
||||
raise general_writer.WriteError(
|
||||
"The relative permeability must be specified for all materials.\n\n"
|
||||
)
|
||||
self.write.material(name, "Name", m["Name"])
|
||||
conductivity = self.write.convert(m["ElectricalConductivity"], "T^3*I^2/(L^3*M)")
|
||||
conductivity = round(conductivity, 10) # to get rid of numerical artifacts
|
||||
self.write.material(
|
||||
name, "Electric Conductivity",
|
||||
conductivity
|
||||
)
|
||||
self.write.material(
|
||||
name, "Relative Permeability",
|
||||
float(m["RelativePermeability"])
|
||||
)
|
||||
# permittivity might be necessary for the post processor
|
||||
if "RelativePermittivity" in m:
|
||||
self.write.material(
|
||||
name, "Relative Permittivity",
|
||||
float(m["RelativePermittivity"])
|
||||
)
|
||||
|
||||
def _outputMagnetodynamic2DBodyForce(self, obj, name):
|
||||
currentDensity = self.write.getFromUi(
|
||||
obj.CurrentDensity.getValueAs("A/m^2"), "A/m^2", "I/L^2"
|
||||
)
|
||||
currentDensity = round(currentDensity, 10) # to get rid of numerical artifacts
|
||||
if currentDensity == 0.0:
|
||||
# a zero density would break Elmer
|
||||
raise general_writer.WriteError("The current density must not be zero!")
|
||||
self.write.bodyForce(name, "Current Density", currentDensity)
|
||||
|
||||
def handleMagnetodynamic2DBodyForces(self, bodies):
|
||||
currentDensities = self.write.getMember("Fem::ConstraintCurrentDensity")
|
||||
if len(currentDensities) == 0:
|
||||
raise general_writer.WriteError(
|
||||
"The Magnetodynamic2D equation needs at least one CurrentDensity constraint."
|
||||
)
|
||||
# check that all bodies have a set material
|
||||
for name in bodies:
|
||||
if self.write.getBodyMaterial(name) == None:
|
||||
raise general_writer.WriteError(
|
||||
"The body {} is not referenced in any material.\n\n".format(name)
|
||||
)
|
||||
for obj in currentDensities:
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
self._outputMagnetodynamic2DBodyForce(obj, name)
|
||||
self.write.handled(obj)
|
||||
else:
|
||||
# if there is only one current density without a reference,
|
||||
# add it to all bodies
|
||||
if len(currentDensities) == 1:
|
||||
for name in bodies:
|
||||
self._outputMagnetodynamic2DBodyForce(obj, name)
|
||||
else:
|
||||
raise general_writer.WriteError(
|
||||
"Several current density constraints found without reference to a body.\n"
|
||||
"Please set a body for each current density constraint."
|
||||
)
|
||||
self.write.handled(obj)
|
||||
|
||||
def handleMagnetodynamic2DBndConditions(self):
|
||||
for obj in self.write.getMember("Fem::ConstraintElectrostaticPotential"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
# output the FreeCAD label as comment
|
||||
if obj.Label:
|
||||
self.write.boundary(name, "! FreeCAD Name", obj.Label)
|
||||
if obj.PotentialEnabled:
|
||||
if hasattr(obj, "Potential"):
|
||||
potential = float(obj.Potential.getValueAs("V"))
|
||||
self.write.boundary(name, "Potential", potential)
|
||||
if obj.ElectricInfinity:
|
||||
self.write.boundary(name, "Infinity BC", True)
|
||||
self.write.handled(obj)
|
||||
|
||||
def handleMagnetodynamic2DEquation(self, bodies, equation):
|
||||
for b in bodies:
|
||||
if equation.IsHarmonic and (equation.AngularFrequency == 0):
|
||||
raise general_writer.WriteError(
|
||||
"The angular frequency must not be zero.\n\n"
|
||||
)
|
||||
self.write.equation(b, "Name", equation.Name)
|
||||
frequency = Units.Quantity(equation.AngularFrequency).Value
|
||||
self.write.equation(b, "Angular Frequency", float(frequency))
|
||||
|
||||
## @}
|
||||
@@ -40,6 +40,7 @@ from .equations import electrostatic
|
||||
from .equations import flow
|
||||
from .equations import flux
|
||||
from .equations import heat
|
||||
from .equations import magnetodynamic2D
|
||||
from .. import run
|
||||
from .. import solverbase
|
||||
from femtools import femutils
|
||||
@@ -71,6 +72,7 @@ class Proxy(solverbase.Proxy):
|
||||
"Flux": flux,
|
||||
"Electricforce": electricforce,
|
||||
"Flow": flow,
|
||||
"Magnetodynamic2D": magnetodynamic2D,
|
||||
}
|
||||
|
||||
def __init__(self, obj):
|
||||
|
||||
@@ -54,12 +54,15 @@ from .equations import electrostatic_writer as ES_writer
|
||||
from .equations import flow_writer
|
||||
from .equations import flux_writer
|
||||
from .equations import heat_writer
|
||||
from .equations import magnetodynamic2D_writer as MgDyn2D_writer
|
||||
|
||||
|
||||
_STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
|
||||
_SIF_NAME = "case.sif"
|
||||
_ELMERGRID_IFORMAT = "8"
|
||||
_ELMERGRID_OFORMAT = "2"
|
||||
_NON_CARTESIAN_CS = ["Polar 2D", "Polar 3D",
|
||||
"Cylindric", "Cylindric Symmetric"]
|
||||
|
||||
|
||||
def _getAllSubObjects(obj):
|
||||
@@ -96,6 +99,7 @@ class Writer(object):
|
||||
self._handleHeat()
|
||||
self._handleFlow()
|
||||
self._handleFlux()
|
||||
self._handleMagnetodynamic2D()
|
||||
self._addOutputSolver()
|
||||
|
||||
self._writeSif()
|
||||
@@ -530,6 +534,39 @@ class Writer(object):
|
||||
HeatW.handleHeatBodyForces(activeIn)
|
||||
HeatW.handleHeatMaterial(activeIn)
|
||||
|
||||
#-------------------------------------------------------------------------------------------
|
||||
# Magnetodynamic2D
|
||||
|
||||
def _handleMagnetodynamic2D(self):
|
||||
MgDyn2D = MgDyn2D_writer.MgDyn2Dwriter(self, self.solver)
|
||||
activeIn = []
|
||||
for equation in self.solver.Group:
|
||||
if femutils.is_of_type(equation, "Fem::EquationElmerMagnetodynamic2D"):
|
||||
if equation.References:
|
||||
activeIn = equation.References[0][1]
|
||||
else:
|
||||
activeIn = self.getAllBodies()
|
||||
# Magnetodynamic2D cannot handle all coordinate sysytems
|
||||
if self.solver.CoordinateSystem in _NON_CARTESIAN_CS:
|
||||
raise WriteError(
|
||||
"The coordinate setting '{}'\n is not "
|
||||
"supported by the equation 'Magnetodynamic2D'.\n\n"
|
||||
"The possible settings are:\n'Cartesian 2D',\n"
|
||||
"'Cartesian 3D',\nor 'Axi Symmetric'".format(self.solver.CoordinateSystem)
|
||||
)
|
||||
|
||||
solverSection = MgDyn2D.getMagnetodynamic2DSolver(equation)
|
||||
solverPostSection = MgDyn2D.getMagnetodynamic2DSolverPost(equation)
|
||||
for body in activeIn:
|
||||
self._addSolver(body, solverSection)
|
||||
self._addSolver(body, solverPostSection)
|
||||
MgDyn2D.handleMagnetodynamic2DEquation(activeIn, equation)
|
||||
if activeIn:
|
||||
MgDyn2D.handleMagnetodynamic2DConstants()
|
||||
MgDyn2D.handleMagnetodynamic2DBndConditions()
|
||||
MgDyn2D.handleMagnetodynamic2DBodyForces(activeIn)
|
||||
MgDyn2D.handleMagnetodynamic2DMaterial(activeIn)
|
||||
|
||||
#-------------------------------------------------------------------------------------------
|
||||
# Solver handling
|
||||
|
||||
@@ -753,7 +790,7 @@ class Writer(object):
|
||||
def equation(self, body, key, attr):
|
||||
self._builder.equation(body, key, attr)
|
||||
|
||||
def _bodyForce(self, body, key, attr):
|
||||
def bodyForce(self, body, key, attr):
|
||||
self._builder.bodyForce(body, key, attr)
|
||||
|
||||
def _addSolver(self, body, solverSection):
|
||||
|
||||
@@ -129,4 +129,14 @@ class HeatViewProxy(BaseViewProxy):
|
||||
return ":/icons/FEM_EquationHeat.svg"
|
||||
|
||||
|
||||
class Magnetodynamic2DProxy(BaseProxy):
|
||||
pass
|
||||
|
||||
|
||||
class Magnetodynamic2DViewProxy(BaseViewProxy):
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_EquationMagnetodynamic2D.svg"
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -84,14 +84,14 @@ class TestObjectCreate(unittest.TestCase):
|
||||
# thus they are not added to the analysis group ATM
|
||||
# https://forum.freecadweb.org/viewtopic.php?t=25283
|
||||
# thus they should not be counted
|
||||
# solver children: equations --> 6
|
||||
# solver children: equations --> 7
|
||||
# gmsh mesh children: group, region, boundary layer --> 3
|
||||
# resule children: mesh result --> 1
|
||||
# post pipeline children: region, scalar, cut, wrap --> 4
|
||||
# analysis itself is not in analysis group --> 1
|
||||
# thus: -14
|
||||
# thus: -16
|
||||
|
||||
self.assertEqual(len(doc.Analysis.Group), count_defmake - 15)
|
||||
self.assertEqual(len(doc.Analysis.Group), count_defmake - 16)
|
||||
self.assertEqual(len(doc.Objects), count_defmake)
|
||||
|
||||
fcc_print("doc objects count: {}, method: {}".format(
|
||||
@@ -366,6 +366,10 @@ class TestObjectType(unittest.TestCase):
|
||||
"Fem::EquationElmerHeat",
|
||||
type_of_obj(ObjectsFem.makeEquationHeat(doc, solverelmer))
|
||||
)
|
||||
self.assertEqual(
|
||||
"Fem::EquationElmerMagnetodynamic2D",
|
||||
type_of_obj(ObjectsFem.makeEquationMagnetodynamic2D(doc, solverelmer))
|
||||
)
|
||||
|
||||
fcc_print("doc objects count: {}, method: {}".format(
|
||||
len(doc.Objects),
|
||||
@@ -597,6 +601,10 @@ class TestObjectType(unittest.TestCase):
|
||||
ObjectsFem.makeEquationHeat(doc, solverelmer),
|
||||
"Fem::EquationElmerHeat"
|
||||
))
|
||||
self.assertTrue(is_of_type(
|
||||
ObjectsFem.makeEquationMagnetodynamic2D(doc, solverelmer),
|
||||
"Fem::EquationElmerMagnetodynamic2D"
|
||||
))
|
||||
|
||||
fcc_print("doc objects count: {}, method: {}".format(
|
||||
len(doc.Objects),
|
||||
@@ -1422,6 +1430,21 @@ class TestObjectType(unittest.TestCase):
|
||||
"Fem::EquationElmerHeat"
|
||||
))
|
||||
|
||||
# EquationElmerMagnetodynamic2D
|
||||
equation_magnetodynamic2D = ObjectsFem.makeEquationMagnetodynamic2D(doc, solver_elmer)
|
||||
self.assertTrue(is_derived_from(
|
||||
equation_magnetodynamic2D,
|
||||
"App::DocumentObject"
|
||||
))
|
||||
self.assertTrue(is_derived_from(
|
||||
equation_magnetodynamic2D,
|
||||
"App::FeaturePython"
|
||||
))
|
||||
self.assertTrue(is_derived_from(
|
||||
equation_magnetodynamic2D,
|
||||
"Fem::EquationElmerMagnetodynamic2D"
|
||||
))
|
||||
|
||||
fcc_print("doc objects count: {}, method: {}".format(
|
||||
len(doc.Objects),
|
||||
sys._getframe().f_code.co_name)
|
||||
@@ -1706,6 +1729,12 @@ class TestObjectType(unittest.TestCase):
|
||||
solverelmer
|
||||
).isDerivedFrom("App::FeaturePython")
|
||||
)
|
||||
self.assertTrue(
|
||||
ObjectsFem.makeEquationMagnetodynamic2D(
|
||||
doc,
|
||||
solverelmer
|
||||
).isDerivedFrom("App::FeaturePython")
|
||||
)
|
||||
|
||||
fcc_print("doc objects count: {}, method: {}".format(
|
||||
len(doc.Objects),
|
||||
@@ -1786,6 +1815,7 @@ def create_all_fem_objects_doc(
|
||||
ObjectsFem.makeEquationFlow(doc, sol)
|
||||
ObjectsFem.makeEquationFlux(doc, sol)
|
||||
ObjectsFem.makeEquationHeat(doc, sol)
|
||||
ObjectsFem.makeEquationMagnetodynamic2D(doc, sol)
|
||||
|
||||
doc.recompute()
|
||||
|
||||
|
||||
@@ -353,6 +353,11 @@ class TestObjectOpen(unittest.TestCase):
|
||||
doc.Heat.Proxy.__class__
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
"Fem::EquationElmerMagnetodynamic2D",
|
||||
type_of_obj(ObjectsFem.makeEquationMagnetodynamic2D(doc))
|
||||
)
|
||||
|
||||
|
||||
"""
|
||||
# code was generated by the following code from a document with all objects
|
||||
|
||||
@@ -328,6 +328,11 @@ class TestObjectOpen(unittest.TestCase):
|
||||
doc.Heat.ViewObject.Proxy.__class__
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
"Fem::EquationElmerMagnetodynamic2D",
|
||||
type_of_obj(ObjectsFem.makeEquationMagnetodynamic2D(doc))
|
||||
)
|
||||
|
||||
|
||||
"""
|
||||
# code was generated by the following code from a document with all objects
|
||||
|
||||
Reference in New Issue
Block a user