FEM: elmer, add it to the new solver framework and add equation object for elmer

This commit is contained in:
Markus Hovorka
2017-12-01 19:43:45 +01:00
committed by wmayer
parent 42b3c3353b
commit 412df6f4c1
31 changed files with 3097 additions and 0 deletions

View File

@@ -120,6 +120,7 @@ SET(FemObjectsScripts_SRCS
SET(FemSolver_SRCS
femsolver/__init__.py
femsolver/solverbase.py
femsolver/equationbase.py
femsolver/report.py
femsolver/reportdialog.py
femsolver/settings.py
@@ -128,6 +129,24 @@ SET(FemSolver_SRCS
femsolver/signal.py
)
SET(FemElmer_SRCS
femsolver/elmer/__init__.py
femsolver/elmer/sifio.py
femsolver/elmer/solver.py
femsolver/elmer/tasks.py
femsolver/elmer/writer.py
)
SET(FemEquationsElmer_SRCS
femsolver/elmer/equations/__init__.py
femsolver/elmer/equations/equation.py
femsolver/elmer/equations/linear.py
femsolver/elmer/equations/nonlinear.py
femsolver/elmer/equations/elasticity.py
femsolver/elmer/equations/heat.py
femsolver/elmer/equations/flow.py
)
SET(FemCalculix_SRCS
femsolver/calculix/__init__.py
femsolver/calculix/solver.py
@@ -169,6 +188,8 @@ SET(FemGuiScripts_SRCS
PyGui/_CommandFemResultShow.py
PyGui/_CommandFemResultsPurge.py
PyGui/_CommandFemSolverCalculix.py
PyGui/_CommandFemSolverElmer.py
PyGui/_CommandFemEquation.py
PyGui/_CommandFemSolverControl.py
PyGui/_CommandFemSolverRun.py
PyGui/_CommandFemSolverZ88.py
@@ -353,8 +374,10 @@ fc_target_copy_resource(Fem
${FemGuiScripts_SRCS}
${FemTests_SRCS}
${FemSolver_SRCS}
${FemElmer_SRCS}
${FemCalculix_SRCS}
${FemZ88_SRCS}
${FemEquationsElmer_SRCS}
)
SET_BIN_DIR(Fem Fem /Mod/Fem)

View File

@@ -73,6 +73,7 @@ INSTALL(
FILES
femsolver/__init__.py
femsolver/solverbase.py
femsolver/equationbase.py
femsolver/report.py
femsolver/reportdialog.py
femsolver/settings.py
@@ -83,6 +84,30 @@ INSTALL(
Mod/Fem/femsolver
)
INSTALL(
FILES
femsolver/elmer/__init__.py
femsolver/elmer/sifio.py
femsolver/elmer/solver.py
femsolver/elmer/tasks.py
femsolver/elmer/writer.py
DESTINATION
Mod/Fem/femsolver/elmer
)
INSTALL(
FILES
femsolver/elmer/equations/__init__.py
femsolver/elmer/equations/equation.py
femsolver/elmer/equations/linear.py
femsolver/elmer/equations/nonlinear.py
femsolver/elmer/equations/elasticity.py
femsolver/elmer/equations/heat.py
femsolver/elmer/equations/flow.py
DESTINATION
Mod/Fem/femsolver/elmer/equations
)
INSTALL(
FILES
femsolver/calculix/__init__.py
@@ -109,6 +134,8 @@ INSTALL(
PyGui/FemSelectionObserver.py
PyGui/FemSelectionWidgets.py
PyGui/__init__.py
PyGui/_CommandFemSolverElmer.py
PyGui/_CommandFemEquation.py
PyGui/_CommandFemConstraintBodyHeatSource.py
PyGui/_CommandFemConstraintFlowVelocity.py
PyGui/_CommandFemConstraintInitialFlowVelocity.py

View File

@@ -38,6 +38,7 @@
#include "DlgSettingsFemExportAbaqusImp.h"
#include "DlgSettingsFemGmshImp.h"
#include "DlgSettingsFemZ88Imp.h"
#include "DlgSettingsFemElmerImp.h"
#include "ViewProviderFemMesh.h"
#include "ViewProviderFemMeshShape.h"
#include "ViewProviderFemMeshShapeNetgen.h"
@@ -160,6 +161,7 @@ PyMOD_INIT_FUNC(FemGui)
new Gui::PrefPageProducer<FemGui::DlgSettingsFemCcxImp> (QT_TRANSLATE_NOOP("QObject","FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemGmshImp> (QT_TRANSLATE_NOOP("QObject","FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemZ88Imp> (QT_TRANSLATE_NOOP("QObject","FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemElmerImp> (QT_TRANSLATE_NOOP("QObject","FEM"));
// register preferences pages on Import-Export
new Gui::PrefPageProducer<FemGui::DlgSettingsFemExportAbaqusImp> (QT_TRANSLATE_NOOP("QObject","Import-Export"));

View File

@@ -46,6 +46,7 @@ set(FemGui_MOC_HDRS
DlgSettingsFemGeneralImp.h
DlgSettingsFemGmshImp.h
DlgSettingsFemZ88Imp.h
DlgSettingsFemElmerImp.h
PropertyFemMeshItem.h
TaskObjectName.h
TaskCreateNodeSet.h
@@ -87,6 +88,7 @@ set(FemGui_UIC_SRCS
DlgSettingsFemGeneral.ui
DlgSettingsFemGmsh.ui
DlgSettingsFemZ88.ui
DlgSettingsFemElmer.ui
TaskCreateNodeSet.ui
TaskObjectName.ui
TaskFemConstraint.ui
@@ -143,6 +145,9 @@ SET(FemGui_DLG_SRCS
DlgSettingsFemZ88.ui
DlgSettingsFemZ88Imp.cpp
DlgSettingsFemZ88Imp.h
DlgSettingsFemElmer.ui
DlgSettingsFemElmerImp.cpp
DlgSettingsFemElmerImp.h
TaskFemConstraint.ui
TaskFemConstraint.cpp
TaskFemConstraint.h

View File

@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FemGui::DlgSettingsFemElmerImp</class>
<widget class="QWidget" name="FemGui::DlgSettingsFemElmerImp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>377</width>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string>Elmer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="gb_elmer_param">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Binaries</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="gl_elmer">
<item row="0" column="0">
<widget class="QLabel" name="l_elmer_binary_std">
<property name="text">
<string>ElmerSolver:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefFileChooser" name="fc_elmer_binary_path" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Leave blank to use default Elmer elmer binary file</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>elmerBinaryPath</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Fem/Elmer</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::PrefCheckBox" name="cb_elmer_binary_std">
<property name="text">
<string>use standard path</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>UseStandardElmerLocation</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Fem/Elmer</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="l_grid_binary_std">
<property name="text">
<string>ElmerGrid:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::PrefFileChooser" name="fc_grid_binary_path" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Leave blank to use default ElmerGrid binary file</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>gridBinaryPath</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Fem/Grid</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::PrefCheckBox" name="cb_grid_binary_std">
<property name="text">
<string>use standard path</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>UseStandardGridLocation</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Fem/Grid</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::FileChooser</class>
<extends>QWidget</extends>
<header>Gui/FileDialog.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>Gui::FileChooser</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefRadioButton</class>
<extends>QRadioButton</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefLineEdit</class>
<extends>QLineEdit</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="Resources/Fem.qrc"/>
</resources>
<connections>
<connection>
<sender>cb_elmer_binary_std</sender>
<signal>toggled(bool)</signal>
<receiver>fc_elmer_binary_path</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>cb_elmer_binary_std</sender>
<signal>toggled(bool)</signal>
<receiver>fc_elmer_binary_path</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>cb_grid_binary_std</sender>
<signal>toggled(bool)</signal>
<receiver>fc_grid_binary_path</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>cb_grid_binary_std</sender>
<signal>toggled(bool)</signal>
<receiver>fc_grid_binary_path</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>cb_wd_custom</sender>
<signal>toggled(bool)</signal>
<receiver>le_wd_custom</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>188</x>
<y>314</y>
</hint>
<hint type="destinationlabel">
<x>137</x>
<y>372</y>
</hint>
</hints>
</connection>
<connection>
<sender>cb_wd_custom</sender>
<signal>toggled(bool)</signal>
<receiver>le_wd_custom</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>188</x>
<y>314</y>
</hint>
<hint type="destinationlabel">
<x>137</x>
<y>372</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (c) 2015 FreeCAD Developers *
* Author: Bernd Hahnebach <bernd@bimstatik.ch> *
* Based on src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#include "Gui/Application.h"
#include "DlgSettingsFemElmerImp.h"
#include <Gui/PrefWidgets.h>
using namespace FemGui;
DlgSettingsFemElmerImp::DlgSettingsFemElmerImp( QWidget* parent )
: PreferencePage( parent )
{
this->setupUi(this);
}
DlgSettingsFemElmerImp::~DlgSettingsFemElmerImp()
{
// no need to delete child widgets, Qt does it all for us
}
void DlgSettingsFemElmerImp::saveSettings()
{
cb_elmer_binary_std->onSave();
fc_elmer_binary_path->onSave();
cb_grid_binary_std->onSave();
fc_grid_binary_path->onSave();
}
void DlgSettingsFemElmerImp::loadSettings()
{
cb_elmer_binary_std->onRestore();
fc_elmer_binary_path->onRestore();
cb_grid_binary_std->onRestore();
fc_grid_binary_path->onRestore();
}
/**
* Sets the strings of the subwidgets using the current language.
*/
void DlgSettingsFemElmerImp::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
}
else {
QWidget::changeEvent(e);
}
}
#include "moc_DlgSettingsFemElmerImp.cpp"

View File

@@ -0,0 +1,50 @@
/**************************************************************************
* Copyright (c) 2016 FreeCAD Developers *
* Author: Bernd Hahnebach <bernd@bimstatik.ch> *
* Based on src/Mod/Fem/Gui/DlgSettingsFemCcx.h *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef FEMGUI_DLGSETTINGSFEMELMERIMP_H
#define FEMGUI_DLGSETTINGSFEMELMERIMP_H
#include "ui_DlgSettingsFemElmer.h"
#include <Gui/PropertyPage.h>
namespace FemGui {
class DlgSettingsFemElmerImp : public Gui::Dialog::PreferencePage, public Ui_DlgSettingsFemElmerImp
{
Q_OBJECT
public:
DlgSettingsFemElmerImp( QWidget* parent = 0 );
~DlgSettingsFemElmerImp();
protected:
void saveSettings();
void loadSettings();
void changeEvent(QEvent *e);
};
} // namespace FemGui
#endif // FEMGUI_DLGSETTINGSFEMELMERIMP_H

View File

@@ -34,6 +34,10 @@
<file>icons/fem-cylinder.svg</file>
<file>icons/fem-DataAlongLine.svg</file>
<file>icons/fem-data.svg</file>
<file>icons/fem-elmer.png</file>
<file>icons/fem-equation-elasticity.svg</file>
<file>icons/fem-equation-flow.svg</file>
<file>icons/fem-equation-heat.svg</file>
<file>icons/fem-femmesh-boundary-layer.svg</file>
<file>icons/fem-femmesh-clear-mesh.svg</file>
<file>icons/fem-femmesh-create-node-by-poly.svg</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="svg2"
height="64"
width="64">
<defs
id="defs4">
<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" />
</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:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[Alexander Gryson]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>fem-warp</dc:title>
<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>
<g
transform="translate(0,-988.36218)"
id="layer1">
<rect
transform="translate(0,988.36218)"
y="39"
x="3"
height="22"
width="58"
id="rect2987"
style="fill:url(#linearGradient3808);stroke:#172a04;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:9.59999999999999964;fill-opacity:1" />
<rect
y="1029.3622"
x="5"
height="18"
width="54"
id="rect2987-6"
style="fill:none;stroke:#555753;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:9.6;stroke-opacity:1" />
<path
id="rect2987-3"
d="m 3,1027.3622 c 18,0 24,-20 30,-32.00002 l 20,10.00002 c -4,8 -18,44 -50,44 z"
style="fill:#888a85;stroke:#172a04;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:9.6;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,258 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2860"
sodipodi:version="0.32"
inkscape:version="0.92.1 r"
sodipodi:docname="fem-equation-flow.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2862">
<inkscape:path-effect
effect="spiro"
id="path-effect3038"
is_visible="true" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3692"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3703"
gradientUnits="userSpaceOnUse"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436"
gradientTransform="matrix(0.97435,0.2250379,-0.4623105,2.0016728,48.487554,-127.99883)" />
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#faff2b;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#ffaa00;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient3705"
gradientUnits="userSpaceOnUse"
cx="148.88333"
cy="81.869568"
fx="148.88333"
fy="81.869568"
r="19.467436"
gradientTransform="matrix(1.3852588,-5.1367833e-2,3.7056289e-2,0.9993132,-60.392403,7.7040438)" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2868" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3809-5"
id="linearGradient3815-7"
x1="43"
y1="58"
x2="42"
y2="48"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient3809-5">
<stop
style="stop-color:#3465a4;stop-opacity:1;"
offset="0"
id="stop3811-3" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop3813-5" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.8890873"
inkscape:cx="37.49133"
inkscape:cy="21.628156"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="956"
inkscape:window-height="1155"
inkscape:window-x="2880"
inkscape:window-y="43"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid3003"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2865">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>[qingfengxia]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>fem-constraint-fluid-boundary</dc:title>
<dc:date>2016-08-10</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>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g2689"
transform="translate(1.4142133,-1.5706127)">
<g
transform="matrix(0.94877631,0,0,0.94877631,0.22494458,3.1949428)"
id="g2671">
<path
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 9.466838,26.650801 14.178715,32 9.418501,37.380527"
id="path3907-3-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 56.058735,32 H 7.9412655"
id="path3881"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 56.058735,32 H 7.9412655"
id="path3881-0"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 9.466838,26.650801 14.178715,32 9.418501,37.380527"
id="path3907-9-6-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
</g>
<g
transform="matrix(0.94877631,0,0,0.94877631,0.22494458,3.5249658)"
id="g2665">
<g
id="g2643"
transform="translate(0,2.7272727)">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3907-3"
d="m 7.9412655,45.365964 h 7.1285135 l 0.891065,7.128514"
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3861-1"
d="M 56.058735,40.019578 H 27.544679 c -10.692771,0 -15.148092,8.019578 -19.6034135,16.039157"
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3861-1-7"
d="M 56.058735,40.019578 H 27.544679 c -10.692771,0 -15.148092,8.019578 -19.6034135,16.039157"
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3907-9-6"
d="m 7.9412655,45.365964 h 7.1285135 l 0.891065,7.128514"
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="matrix(1,0,0,-1,0,60.608303)"
id="g2653">
<path
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7.9412655,45.365964 h 7.1285135 l 0.891065,7.128514"
id="path2645"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 56.058735,40.019578 H 27.544679 c -10.692771,0 -15.148092,8.019578 -19.6034135,16.039157"
id="path2647"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 56.058735,40.019578 H 27.544679 c -10.692771,0 -15.148092,8.019578 -19.6034135,16.039157"
id="path2649"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#729fcf;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7.9412655,45.365964 h 7.1285135 l 0.891065,7.128514"
id="path2651"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="svg2860"
height="64px"
width="64px">
<defs
id="defs2862">
<linearGradient
id="linearGradient3929">
<stop
id="stop3931"
offset="0"
style="stop-color:#a40000;stop-opacity:1" />
<stop
id="stop3933"
offset="1"
style="stop-color:#ef2929;stop-opacity:1" />
</linearGradient>
<radialGradient
gradientTransform="matrix(0.97435,0.2250379,-0.4623105,2.0016728,48.487554,-127.99883)"
r="19.467436"
fy="97.369568"
fx="135.38333"
cy="97.369568"
cx="135.38333"
gradientUnits="userSpaceOnUse"
id="radialGradient3703"
xlink:href="#linearGradient3377" />
<linearGradient
id="linearGradient3377">
<stop
style="stop-color:#faff2b;stop-opacity:1;"
offset="0"
id="stop3379" />
<stop
style="stop-color:#ffaa00;stop-opacity:1;"
offset="1"
id="stop3381" />
</linearGradient>
<radialGradient
gradientTransform="matrix(1.3852588,-0.05136783,0.03705629,0.9993132,-60.392403,7.7040438)"
r="19.467436"
fy="81.869568"
fx="148.88333"
cy="81.869568"
cx="148.88333"
gradientUnits="userSpaceOnUse"
id="radialGradient3705"
xlink:href="#linearGradient3377" />
<radialGradient
gradientTransform="matrix(0.97435,0.2250379,-0.4623105,2.0016728,48.487554,-127.99883)"
r="19.467436"
fy="97.369568"
fx="135.38333"
cy="97.369568"
cx="135.38333"
gradientUnits="userSpaceOnUse"
id="radialGradient3703-8"
xlink:href="#linearGradient3377-4" />
<linearGradient
id="linearGradient3377-4">
<stop
style="stop-color:#faff2b;stop-opacity:1;"
offset="0"
id="stop3379-3" />
<stop
style="stop-color:#ffaa00;stop-opacity:1;"
offset="1"
id="stop3381-0" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="51.200001"
x2="4.8000002"
y1="60.799999"
x1="9.6000004"
id="linearGradient3935"
xlink:href="#linearGradient3929" />
</defs>
<metadata
id="metadata2865">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[vdwalts]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>fem-constraint-temperature</dc:title>
<dc:date>2016-08-01</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>
<g
id="layer1">
<g
transform="matrix(1.569104,0,0,1.569104,16.30896,-37.780432)"
id="g6350">
<path
style="fill:none;stroke:#280000;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 10,31 V 57"
id="path3083" />
<circle
r="8"
cy="56"
cx="8"
style="fill:#cc0000;stroke:#280000;stroke-width:2.28571439;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none"
id="path3909"
transform="matrix(0.875,0,0,0.875,3,5)" />
<path
style="fill:none;stroke:#d3d7cf;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 10,31 V 57"
id="path3083-3" />
<path
style="fill:none;stroke:#cc0000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 10,40 V 56"
id="path3083-3-6" />
<path
style="fill:none;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 9,40 V 50"
id="path3083-3-6-7" />
<circle
r="8"
cy="56"
cx="8"
style="fill:url(#linearGradient3935);fill-opacity:1;stroke:#ef2929;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none"
id="path3909-5"
transform="matrix(0.625,0,0,0.625,5,19)" />
<path
style="fill:none;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="m 11,33 h 2"
id="path3937" />
<path
style="fill:none;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="m 11,39 h 2"
id="path3937-3" />
<path
style="fill:none;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="m 11,45 h 2"
id="path3937-5" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -112,6 +112,11 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
Gui::ToolBarItem* solve = new Gui::ToolBarItem(root);
solve->setCommand("Solve");
*solve << "FEM_SolverCalculix"
<< "FEM_AddSolverElmer"
<< "Separator"
<< "FEM_AddEquationHeat"
<< "FEM_AddEquationElasticity"
<< "FEM_AddEquationFlow"
<< "Separator"
<< "FEM_SolverControl"
<< "FEM_SolverRun";
@@ -209,6 +214,11 @@ Gui::MenuItem* Workbench::setupMenuBar() const
solve->setCommand("&Solve");
*solve << "FEM_SolverCalculix"
<< "FEM_SolverZ88"
<< "FEM_AddSolverElmer"
<< "Separator"
<< "FEM_AddEquationHeat"
<< "FEM_AddEquationElasticity"
<< "FEM_AddEquationFlow"
<< "Separator"
<< "FEM_SolverControl"
<< "FEM_SolverRun";

View File

@@ -67,6 +67,8 @@ class FemWorkbench (Workbench):
import PyGui._CommandFemResultShow
import PyGui._CommandFemResultsPurge
import PyGui._CommandFemSolverCalculix
import PyGui._CommandFemSolverElmer
import PyGui._CommandFemEquation
import PyGui._CommandFemSolverControl
import PyGui._CommandFemSolverRun
import PyGui._CommandFemSolverZ88

View File

@@ -361,6 +361,13 @@ def makeSolverCalculix(doc, name="SolverCalculiX"):
return obj
def makeSolverElmer(doc, name="SolverElmer"):
'''makeSolverElmer(document, [name]): makes a Elmer solver object'''
import femsolver.elmer.solver
obj = femsolver.elmer.solver.create(doc, name)
return obj
def makeSolverZ88(doc, name="SolverZ88"):
'''makeSolverZ88(document, [name]): makes a Z88 solver object'''
import femsolver.z88.solver

View File

@@ -0,0 +1,102 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "_CommandFemEquation"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from PySide import QtCore
import FreeCAD as App
import FreeCADGui as Gui
import FemUtils
class _Base(QtCore.QObject):
def getSpecifier(self):
raise NotImplementedError()
def Activated(self):
s = Gui.Selection.getSelection()
if len(s) == 1 and FemUtils.isDerivedFrom(s[0], "Fem::FemSolverObject"):
App.ActiveDocument.openTransaction(
"Add %s equation to %s"
% (self.getSpecifier(), s[0].Label))
Gui.doCommand(
"App.ActiveDocument.%(obj)s.Proxy.addEquation("
"App.ActiveDocument.%(obj)s, '%(name)s')"
% {"obj": s[0].Name, "name": self.getSpecifier()})
App.ActiveDocument.commitTransaction()
App.ActiveDocument.recompute()
def IsActive(self):
s = Gui.Selection.getSelection()
if len(s) == 1 and FemUtils.isDerivedFrom(s[0], "Fem::FemSolverObject"):
return s[0].Proxy.isSupported(self.getSpecifier())
return False
class Heat(_Base):
def getSpecifier(self):
return "Heat"
def GetResources(self):
return {
'Pixmap': 'fem-equation-heat',
'MenuText': "Heat Equation",
'ToolTip': "Creates a FEM constraint body heat flux"
}
class Elasticity(_Base):
def getSpecifier(self):
return "Elasticity"
def GetResources(self):
return {
'Pixmap': 'fem-equation-elasticity',
'MenuText': "Elasticity Equation",
'ToolTip': "Creates a FEM constraint for elasticity"
}
class Flow(_Base):
def getSpecifier(self):
return "Flow"
def GetResources(self):
return {
'Pixmap': 'fem-equation-flow',
'MenuText': "Flow Equation",
'ToolTip': "Creates a FEM constraint body heat flux"
}
Gui.addCommand('FEM_AddEquationHeat', Heat())
Gui.addCommand('FEM_AddEquationElasticity', Elasticity())
Gui.addCommand('FEM_AddEquationFlow', Flow())

View File

@@ -0,0 +1,63 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Elmer"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from PySide import QtCore
import FreeCAD as App
import FreeCADGui as Gui
import FemGui
class Command(QtCore.QObject):
def Activated(self):
analysis = FemGui.getActiveAnalysis()
App.ActiveDocument.openTransaction("Create Elmer solver object")
Gui.addModule("femsolver.elmer.solver")
Gui.doCommand(
"App.ActiveDocument.%s.Member += "
"[femsolver.elmer.solver.create(App.ActiveDocument)]"
% analysis.Name)
App.ActiveDocument.commitTransaction()
App.ActiveDocument.recompute()
def GetResources(self):
return {
'Pixmap': 'fem-elmer',
'MenuText': "Solver Elmer",
'Accel': "S, E",
'ToolTip': "Creates a FEM solver Elmer"
}
def IsActive(self):
analysis = FemGui.getActiveAnalysis()
return (analysis is not None
and analysis.Document == App.ActiveDocument)
Gui.addCommand('FEM_AddSolverElmer', Command())

View File

@@ -54,6 +54,8 @@ class _CommandFemSolverRun(FemCommands):
self.solver = FreeCADGui.Selection.getSelection()[0] # see 'with_solver' in FemCommands for selection check
if FemUtils.isDerivedFrom(self.solver, "Fem::FemSolverObjectZ88"):
self._newActivated()
elif FemUtils.isDerivedFrom(self.solver, "Fem::FemSolverObjectElmer"):
self._newActivated()
elif FemUtils.isDerivedFrom(self.solver, "Fem::FemSolverObjectCalculix"):
self._newActivated()
elif self.solver.SolverType == "FemSolverCalculix":

View File

View File

@@ -0,0 +1,68 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Elasticity"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from ... import equationbase
from . import linear
def create(doc, name="Elasticity"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.ElasticityProxy):
Type = "Fem::FemEquationElmerElasticity"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyBool", "DoFrequencyAnalysis",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "EigenmodesCount",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculateStrains",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculateStresses",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculatePrincipal",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculatePangle",
"Elasticity", "Select type of solver for linear system")
obj.EigenmodesCount = 5
obj.Priority = 10
class ViewProxy(linear.ViewProxy, equationbase.ElasticityViewProxy):
pass

View File

@@ -0,0 +1,111 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Equation"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FreeCAD as App
from ... import equationbase
import FemUtils
if App.GuiUp:
import FreeCADGui as Gui
import PyGui.FemSelectionWidgets as FemSelectionWidgets
class Proxy(equationbase.BaseProxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyInteger", "Priority",
"Base", "Select type of solver for linear system")
class ViewProxy(equationbase.BaseViewProxy):
def setEdit(self, vobj, mode=0):
task = _TaskPanel(vobj.Object)
Gui.Control.showDialog(task)
def unsetEdit(self, vobj, mode=0):
Gui.Control.closeDialog()
def doubleClicked(self, vobj):
if Gui.Control.activeDialog():
Gui.Control.closeDialog()
Gui.ActiveDocument.setEdit(vobj.Object.Name)
return True
def getTaskWidget(self, vobj):
return None
class _TaskPanel(object):
def __init__(self, obj):
self._obj = obj
self._refWidget = FemSelectionWidgets.SolidSelector()
self._refWidget.setReferences(obj.References)
propWidget = obj.ViewObject.Proxy.getTaskWidget(
obj.ViewObject)
if propWidget is None:
self.form = self._refWidget
else:
self.form = [self.refWidget, propWidget]
analysis = FemUtils.findAnalysisOfMember(obj)
self._mesh = FemUtils.getSingleMember(analysis, "Fem::FemMeshObject")
self._part = self._mesh.Part if self._mesh is not None else None
self._partVisible = None
self._meshVisible = None
def open(self):
if self._mesh is not None and self._part is not None:
self._meshVisible = self._mesh.ViewObject.isVisible()
self._partVisible = self._part.ViewObject.isVisible()
self._mesh.ViewObject.hide()
self._part.ViewObject.show()
def reject(self):
self._restoreVisibility()
return True
def accept(self):
if self._obj.References != self._refWidget.references():
self._obj.References = self._refWidget.references()
self._obj.Document.recompute()
self._restoreVisibility()
return True
def _restoreVisibility(self):
if self._mesh is not None and self._part is not None:
if self._meshVisible:
self._mesh.ViewObject.show()
else:
self._mesh.ViewObject.hide()
if self._partVisible:
self._part.ViewObject.show()
else:
self._part.ViewObject.hide()

View File

@@ -0,0 +1,49 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Flow"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from . import nonlinear
from ... import equationbase
def create(doc, name="Flow"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
Type = "Fem::FemEquationElmerFlow"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.Priority = 10
class ViewProxy(nonlinear.ViewProxy, equationbase.FlowViewProxy):
pass

View File

@@ -0,0 +1,49 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Heat"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from . import nonlinear
from ... import equationbase
def create(doc, name="Heat"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.HeatProxy):
Type = "Fem::FemEquationElmerHeat"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.Priority = 20
class ViewProxy(nonlinear.ViewProxy, equationbase.HeatViewProxy):
pass

View File

@@ -0,0 +1,103 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "_Linear"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from . import equation
LINEAR_SOLVER = ["Direct", "Iterative"]
LINEAR_DIRECT = ["Banded", "umfpack"]
LINEAR_ITERATIVE = [
"CG",
"CGS",
"BiCGStab",
"BiCGStabl",
"TFQMR",
"GMRES",
"GCR",
]
LINEAR_PRECONDITIONING = [
"None",
"Diagonal",
"ILU0",
"ILU1",
"ILU2",
"ILU3",
"ILU4",
]
class Proxy(equation.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyEnumeration", "LinearSolverType",
"Linear System", "Select type of solver for linear system")
obj.LinearSolverType = LINEAR_SOLVER
obj.LinearSolverType = "Iterative"
obj.addProperty(
"App::PropertyEnumeration", "LinearDirectMethod",
"Linear System", "Select type of solver for linear system")
obj.LinearDirectMethod = LINEAR_DIRECT
obj.addProperty(
"App::PropertyEnumeration", "LinearIterativeMethod",
"Linear System", "Select type of solver for linear system")
obj.LinearIterativeMethod = LINEAR_ITERATIVE
obj.LinearIterativeMethod = "BiCGStab"
obj.addProperty(
"App::PropertyInteger", "BiCGstablDegree",
"Linear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyEnumeration", "LinearPreconditioning",
"Linear System", "Select type of solver for linear system")
obj.LinearPreconditioning = LINEAR_PRECONDITIONING
obj.LinearPreconditioning = "ILU0"
obj.addProperty(
"App::PropertyFloat", "LinearTolerance",
"Linear System", "Select type of solver for linear system")
obj.LinearTolerance = 1e-8
obj.addProperty(
"App::PropertyInteger", "LinearIterations",
"Linear System", "Select type of solver for linear system")
obj.LinearIterations = 500
obj.addProperty(
"App::PropertyFloat", "SteadyStateTolerance",
"Steady State", "Select type of solver for linear system")
obj.SteadyStateTolerance = 1e-5
obj.addProperty(
"App::PropertyBool", "Stabilize",
"Base", "Select type of solver for linear system")
obj.Stabilize = True
obj.addProperty(
"App::PropertyBool", "Bubbles",
"Base", "Select type of solver for linear system")
obj.Bubbles = False
class ViewProxy(equation.ViewProxy):
pass

View File

@@ -0,0 +1,59 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "_NonLinear"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from . import linear
class Proxy(linear.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyFloat", "NonlinearTolerance",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "NonlinearIterations",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyFloat", "RelaxationFactor",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "NonlinearNewtonAfterIterations",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyFloat", "NonlinearNewtonAfterTolerance",
"Nonlinear System", "Select type of solver for linear system")
obj.NonlinearTolerance = 1e-8
obj.NonlinearIterations = 500
obj.RelaxationFactor = 1
obj.NonlinearNewtonAfterIterations = 3
obj.NonlinearNewtonAfterTolerance = 1e-3
class ViewProxy(linear.ViewProxy):
pass

View File

@@ -0,0 +1,429 @@
# ***************************************************************************
# * *
# * Copyright (c) 2016 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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 *
# * *
# ***************************************************************************
import collections
SIMULATION = "Simulation"
CONSTANTS = "Constants"
BODY = "Body"
MATERIAL = "Material"
BODY_FORCE = "Body Force"
EQUATION = "Equation"
SOLVER = "Solver"
BOUNDARY_CONDITION = "Boundary Condition"
INITIAL_CONDITION = "Initial Condition"
COMPONENT = "Component"
_VALID_SECTIONS = (
SIMULATION,
CONSTANTS,
BODY,
MATERIAL,
BODY_FORCE,
EQUATION,
SOLVER,
BOUNDARY_CONDITION,
INITIAL_CONDITION,
COMPONENT,
)
_NUMBERED_SECTIONS = (
BODY,
MATERIAL,
BODY_FORCE,
EQUATION,
SOLVER,
BOUNDARY_CONDITION,
INITIAL_CONDITION,
COMPONENT,
)
_SECTION_DELIM = "End"
_WHITESPACE = " "
_INDENT = " " * 2
_NEWLINE = "\n"
_TYPE_REAL = "Real"
_TYPE_INTEGER = "Integer"
_TYPE_LOGICAL = "Logical"
_TYPE_STRING = "String"
_TYPE_FILE = "File"
WARN = "Warn"
IGNORE = "Ignore"
ABORT = "Abort"
SILENT = "Silent"
def createSection(name):
section = Section(name)
if not isValid(section):
raise ValueError("Invalid section name: %s" % name)
return section
def writeSections(sections, stream):
ids = _IdManager()
_Writer(ids, sections, stream).write()
def isNumbered(section):
return section.name in _NUMBERED_SECTIONS
def isValid(section):
return section.name in _VALID_SECTIONS
class Builder(object):
_ACTIVE_SOLVERS = "Active Solvers"
def __init__(self):
self._customSections = set()
self._simulation = createSection(SIMULATION)
self._constants = createSection(CONSTANTS)
self._bodies = {}
self._boundaries = {}
def getBoundaryNames(self):
return self._boundaries.keys()
def getBodyNames(self):
return self._bodies.keys()
def simulation(self, key, attr):
self._simulation[key] = attr
def constant(self, key, attr):
self._constants[key] = attr
def initial(self, body, key, attr):
section = self._getFromBody(body, INITIAL_CONDITION)
section[key] = attr
def material(self, body, key, attr):
section = self._getFromBody(body, MATERIAL)
section[key] = attr
def equation(self, body, key, attr):
section = self._getFromBody(body, EQUATION)
section[key] = attr
def bodyForce(self, body, key, attr):
section = self._getFromBody(body, BODY_FORCE)
section[key] = attr
def addSolver(self, body, solverSection):
section = self._getFromBody(body, EQUATION)
if self._ACTIVE_SOLVERS not in section:
section[self._ACTIVE_SOLVERS] = []
section[self._ACTIVE_SOLVERS].append(solverSection)
def boundary(self, boundary, key, attr):
if boundary not in self._boundaries:
self._boundaries[boundary] = createSection(BOUNDARY_CONDITION)
self._boundaries[boundary][key] = attr
def addSection(self, section):
self._customSections.add(section)
def _getFromBody(self, body, key):
if body not in self._bodies:
self._bodies[body] = createSection(BODY)
if key not in self._bodies[body]:
self._bodies[body][key] = createSection(key)
return self._bodies[body][key]
def __iter__(self):
allSections = set(self._customSections)
allSections.add(self._simulation)
allSections.add(self._constants)
for name, section in self._bodies.iteritems():
section["Name"] = name
allSections.add(section)
if MATERIAL in section:
allSections.add(section[MATERIAL])
if EQUATION in section:
eqSection = section[EQUATION]
allSections.add(eqSection)
if self._ACTIVE_SOLVERS in eqSection:
for solverSection in eqSection[self._ACTIVE_SOLVERS]:
allSections.add(solverSection)
if BODY_FORCE in section:
allSections.add(section[BODY_FORCE])
if INITIAL_CONDITION in section:
allSections.add(section[INITIAL_CONDITION])
for name, section in self._boundaries.iteritems():
section["Name"] = name
allSections.add(section)
return iter(allSections)
class Sif(object):
_CHECK_KEYWORDS = "Check Keywords"
_HEADER = "Header"
_MESHDB_ATTR = "Mesh DB"
_INCLUDE_ATTR = "Include Path"
_RESULT_ATTR = "Results Directory"
def __init__(self, sections=[], meshLocation="."):
self.sections = sections
self.meshPath = meshLocation
self.checkKeywords = WARN
self.incPath = ""
self.resPath = ""
def write(self, stream):
self._writeCheckKeywords(stream)
stream.write(_NEWLINE * 2)
self._writeHeader(stream)
stream.write(_NEWLINE * 2)
writeSections(self.sections, stream)
def _writeCheckKeywords(self, stream):
stream.write(self._CHECK_KEYWORDS)
stream.write(_WHITESPACE)
stream.write(self.checkKeywords)
def _writeHeader(self, stream):
stream.write(self._HEADER)
stream.write(_NEWLINE)
self._writeAttr(self._MESHDB_ATTR, self.meshPath, stream)
stream.write(_NEWLINE)
if self.incPath:
self._writeAttr(self._INCLUDE_ATTR, self.incPath, stream)
stream.write(_NEWLINE)
if self.resPath:
self._writeAttr(self._RESULT_ATTR, self.resPath, stream)
stream.write(_NEWLINE)
stream.write(_SECTION_DELIM)
def _writeAttr(self, name, value, stream):
stream.write(_INDENT)
stream.write(name)
stream.write(_WHITESPACE)
stream.write('"%s"' % value)
class Section(object):
def __init__(self, name):
self.name = name
self.priority = 0
self._attrs = dict()
def __setitem__(self, key, value):
self._attrs[key] = value
def __getitem__(self, key):
return self._attrs[key]
def __delitem__(self, key):
del self._attrs[key]
def __iter__(self):
return self._attrs.iteritems()
def __contains__(self, item):
return item in self._attrs
def __str__(self):
return str(self._attrs)
def __repr__(self):
return self.name
class FileAttr(str):
pass
class _Writer(object):
def __init__(self, idManager, sections, stream):
self._idMgr = idManager
self._sections = sections
self._stream = stream
def write(self):
sortedSections = sorted(
self._sections, key=lambda s: s.priority, reverse=True)
for s in sortedSections:
self._writeSection(s)
self._stream.write(_NEWLINE)
def _writeSection(self, s):
self._writeSectionHeader(s)
self._writeSectionBody(s)
self._writeSectionFooter(s)
self._stream.write(_NEWLINE)
def _writeSectionHeader(self, s):
self._stream.write(s.name)
self._stream.write(_WHITESPACE)
if isNumbered(s):
self._stream.write(str(self._idMgr.getId(s)))
def _writeSectionFooter(self, s):
self._stream.write(_NEWLINE)
self._stream.write(_SECTION_DELIM)
def _writeSectionBody(self, s):
for key, data in s:
self._writeAttribute(key, data)
def _writeAttribute(self, key, data):
if isinstance(data, Section):
self._stream.write(_NEWLINE)
self._writeScalarAttr(key, data)
elif isinstance(data, FileAttr):
self._stream.write(_NEWLINE)
self._writeFileAttr(key, data)
elif self._isCollection(data):
if len(data) == 1:
scalarData = self._getOnlyElement(data)
self._stream.write(_NEWLINE)
self._writeScalarAttr(key, scalarData)
elif len(data) > 1:
self._stream.write(_NEWLINE)
self._writeArrAttr(key, data)
else:
self._stream.write(_NEWLINE)
self._writeScalarAttr(key, data)
def _getOnlyElement(self, collection):
it = iter(collection)
return it.next()
def _isCollection(self, data):
return (not isinstance(data, basestring)
and isinstance(data, collections.Iterable))
def _checkScalar(self, dataType):
if issubclass(dataType, int):
return self._genIntAttr
if issubclass(dataType, float):
return self._genFloatAttr
if issubclass(dataType, bool):
return self._genBoolAttr
if issubclass(dataType, basestring):
return self._genStrAttr
return None
def _writeScalarAttr(self, key, data):
attrType = self._getAttrTypeScalar(data)
if attrType is None:
raise ValueError("Unsupported data type: %s" % type(data))
self._stream.write(_INDENT)
self._stream.write(key)
self._stream.write(_WHITESPACE)
self._stream.write("=")
self._stream.write(_WHITESPACE)
self._stream.write(attrType)
self._stream.write(_WHITESPACE)
self._stream.write(self._preprocess(data, type(data)))
def _writeArrAttr(self, key, data):
attrType = self._getAttrTypeArr(data)
self._stream.write(_INDENT)
self._stream.write(key)
self._stream.write("(%d)" % len(data))
self._stream.write(_WHITESPACE)
self._stream.write("=")
self._stream.write(_WHITESPACE)
self._stream.write(attrType)
for val in data:
self._stream.write(_WHITESPACE)
self._stream.write(self._preprocess(val, type(val)))
def _writeFileAttr(self, key, data):
self._stream.write(_INDENT)
self._stream.write(key)
self._stream.write(_WHITESPACE)
self._stream.write("=")
self._stream.write(_WHITESPACE)
self._stream.write(_TYPE_FILE)
for val in data.split("/"):
if val:
self._stream.write(_WHITESPACE)
self._stream.write('"%s"' % val)
def _getSifDataType(self, dataType):
if issubclass(dataType, Section):
return _TYPE_INTEGER
if issubclass(dataType, bool):
return _TYPE_LOGICAL
if issubclass(dataType, int):
return _TYPE_INTEGER
if issubclass(dataType, float):
return _TYPE_REAL
if issubclass(dataType, basestring):
return _TYPE_STRING
raise ValueError("Unsupported data type: %s" % dataType)
def _preprocess(self, data, dataType):
if issubclass(dataType, Section):
return str(self._idMgr.getId(data))
if issubclass(dataType, basestring):
return '"%s"' % data
return str(data)
def _getAttrTypeScalar(self, data):
return self._getSifDataType(type(data))
def _getAttrTypeArr(self, data):
if not data:
raise ValueError("Collections must not be empty.")
it = iter(data)
dataType = type(it.next())
for entry in it:
if not isinstance(entry, dataType):
raise ValueError("Collection must be homogenueous")
return self._getSifDataType(dataType)
class _IdManager(object):
def __init__(self, firstId=1):
self._pool = dict()
self._ids = dict()
self.firstId = firstId
def setId(self, section):
if section.name not in self._pool:
self._pool[section.name] = self.firstId
self._ids[section] = self._pool[section.name]
self._pool[section.name] += 1
def getId(self, section):
if section not in self._ids:
self.setId(section)
return self._ids[section]

View File

@@ -0,0 +1,93 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "Elmer"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from .. import run
from .. import solverbase
from . import tasks
from .equations import heat
from .equations import elasticity
from .equations import flow
def create(doc, name="ElmerSolver"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(solverbase.Proxy):
"""Proxy for FemSolverElmers Document Object."""
Type = "Fem::FemSolverObjectElmer"
_EQUATIONS = {
"Heat": heat,
"Elasticity": elasticity,
"Flow": flow,
}
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyInteger", "SteadyStateMaxIterations",
"Steady State", "")
obj.addProperty(
"App::PropertyInteger", "SteadyStateMinIterations",
"Steady State", "")
obj.addProperty(
"App::PropertyLink", "ElmerResult",
"Base", "", 4 | 8)
obj.addProperty(
"App::PropertyLink", "ElmerOutput",
"Base", "", 4 | 8)
obj.SteadyStateMaxIterations = 1
obj.SteadyStateMinIterations = 0
def createMachine(self, obj, directory):
return run.Machine(
solver=obj, directory=directory,
check=tasks.Check(),
prepare=tasks.Prepare(),
solve=tasks.Solve(),
results=tasks.Results())
def createEquation(self, doc, eqId):
return self._EQUATIONS[eqId].create(doc)
def isSupported(self, eqId):
return eqId in self._EQUATIONS
class ViewProxy(solverbase.ViewProxy):
"""Proxy for FemSolverElmers View Provider."""
def getIcon(self):
return ":/icons/fem-elmer.png"

View File

@@ -0,0 +1,148 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "FemElmerTasks"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import subprocess
import os.path
import FemUtils
from .. import run
from .. import settings
from . import writer
class Check(run.Check):
def run(self):
self.pushStatus("Checking analysis...\n")
if (self.checkMesh()):
self.checkMeshType()
self.checkMaterial()
self.checkEquations()
def checkMeshType(self):
mesh = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
if not FemUtils.isOfType(mesh, "FemMeshGmsh"):
self.report.error(
"Unsupported type of mesh. "
"Mesh must be created with gmsh.")
self.fail()
return False
return True
def checkEquations(self):
equations = self.solver.Group
if not equations:
self.report.error(
"Solver has no equations. "
"Add at least one equation.")
self.fail()
class Prepare(run.Prepare):
def run(self):
self.pushStatus("Preparing input files...\n")
w = writer.Writer(self.solver, self.directory)
try:
w.write()
self.checkHandled(w)
except writer.WriteError as e:
self.report.error(str(e))
self.fail()
except IOError as e:
self.report.error("Can't access working directory.")
self.fail()
def checkHandled(self, w):
handled = w.getHandledConstraints()
allConstraints = FemUtils.getMember(self.analysis, "Fem::Constraint")
for obj in set(allConstraints) - handled:
self.report.warning("Ignored constraint %s." % obj.Label)
class Solve(run.Solve):
def run(self):
self.pushStatus("Executing solver...\n")
binary = settings.getBinary("ElmerSolver")
if binary is not None:
self._process = subprocess.Popen(
[binary], cwd=self.directory,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
self.signalAbort.add(self._process.terminate)
output = self._observeSolver(self._process)
self._process.communicate()
self.signalAbort.remove(self._process.terminate)
if not self.aborted:
self._updateOutput(output)
else:
self.report.error("ElmerSolver executable not found.")
self.fail()
def _observeSolver(self, process):
output = ""
line = process.stdout.readline()
self.pushStatus(line)
output += line
line = process.stdout.readline()
while line:
line = "\n%s" % line.rstrip()
self.pushStatus(line)
output += line
line = process.stdout.readline()
return output
def _updateOutput(self, output):
if self.solver.ElmerOutput is None:
self._createOutput()
self.solver.ElmerOutput.Text = output
def _createOutput(self):
self.solver.ElmerOutput = self.analysis.Document.addObject(
"App::TextDocument", self.solver.Name + "Output")
self.solver.ElmerOutput.Label = self.solver.Label + "Output"
self.solver.ElmerOutput.ReadOnly = True
self.analysis.Member += [self.solver.ElmerOutput]
class Results(run.Results):
def run(self):
if self.solver.ElmerResult is None:
self._createResults()
postPath = os.path.join(self.directory, "case0001.vtu")
self.solver.ElmerResult.read(postPath)
self.solver.ElmerResult.getLastPostObject().touch()
self.solver.Document.recompute()
def _createResults(self):
self.solver.ElmerResult = self.analysis.Document.addObject(
"Fem::FemPostPipeline", self.solver.Name + "Result")
self.solver.ElmerResult.Label = self.solver.Label + "Result"
self.analysis.Member += [self.solver.ElmerResult]

View File

@@ -0,0 +1,682 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "FemWriterElmer"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import os
import os.path
import subprocess
import tempfile
from FreeCAD import Units
import Fem
import FemUtils
import FemGmshTools
from .. import settings
from . import sifio
_STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
_SIF_NAME = "case.sif"
_ELMERGRID_IFORMAT = "8"
_ELMERGRID_OFORMAT = "2"
_SOLID_PREFIX = "Solid"
UNITS = {
"L": "mm",
"M": "kg",
"T": "s",
"I": "A",
"O": "K",
"N": "mol",
"J": "cd",
}
CONSTS_DEF = {
"Gravity": "9.82 m/s^2",
"StefanBoltzmann": "5.67e-8 W/(m^2*K^4)",
"PermittivityOfVacuum": "8.8542e-12 s^4*A^2/(m*kg)",
"BoltzmannConstant": "1.3807e-23 J/K",
}
def getFromUi(value, unit, outputDim):
quantity = Units.Quantity(str(value) + str(unit))
return convert(quantity, outputDim)
def convert(quantityStr, unit):
quantity = Units.Quantity(quantityStr)
for key, setting in UNITS.iteritems():
unit = unit.replace(key, setting)
return float(quantity.getValueAs(unit))
def _getAllSubObjects(obj):
s = ["Solid" + str(i + 1) for i in range(len(obj.Shape.Solids))]
s.extend(("Face" + str(i + 1) for i in range(len(obj.Shape.Faces))))
s.extend(("Edge" + str(i + 1) for i in range(len(obj.Shape.Edges))))
s.extend(("Vertex" + str(i + 1) for i in range(len(obj.Shape.Vertexes))))
return s
def getConstant(name, dimension):
return convert(CONSTS_DEF[name], dimension)
class Writer(object):
def __init__(self, solver, directory):
self.analysis = FemUtils.findAnalysisOfMember(solver)
self.solver = solver
self.directory = directory
self._usedVarNames = set()
self._builder = sifio.Builder()
self._handledObjects = set()
def getHandledConstraints(self):
return self._handledObjects
def write(self):
self._handleSimulation()
self._handleHeat()
self._handleElasticity()
self._handleFlow()
self._addOutputSolver()
self._writeSif()
self._writeMesh()
self._writeStartinfo()
def _writeMesh(self):
mesh = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
unvPath = os.path.join(self.directory, "mesh.unv")
groups = []
groups.extend(self._builder.getBodyNames())
groups.extend(self._builder.getBoundaryNames())
self._exportToUnv(groups, mesh, unvPath)
binary = settings.getBinary("ElmerGrid")
if binary is None:
raise WriteError("Couldn't find ElmerGrid binary.")
args = [binary,
_ELMERGRID_IFORMAT,
_ELMERGRID_OFORMAT,
unvPath,
"-out", self.directory]
subprocess.call(args)
def _writeStartinfo(self):
path = os.path.join(self.directory, _STARTINFO_NAME)
with open(path, 'w') as f:
f.write(_SIF_NAME)
def _exportToUnv(self, groups, mesh, meshPath):
unvGmshFd, unvGmshPath = tempfile.mkstemp(suffix=".unv")
brepFd, brepPath = tempfile.mkstemp(suffix=".brep")
geoFd, geoPath = tempfile.mkstemp(suffix=".geo")
os.close(brepFd)
os.close(geoFd)
os.close(unvGmshFd)
tools = FemGmshTools.FemGmshTools(mesh)
tools.group_elements = {g: [g] for g in groups}
tools.ele_length_map = {}
tools.temp_file_geometry = brepPath
tools.temp_file_geo = geoPath
tools.temp_file_mesh = unvGmshPath
tools.get_dimension()
tools.get_gmsh_command()
tools.get_region_data()
tools.get_boundary_layer_data()
tools.write_part_file()
tools.write_geo()
tools.run_gmsh_with_geo()
ioMesh = Fem.FemMesh()
ioMesh.read(unvGmshPath)
ioMesh.write(meshPath)
os.remove(brepPath)
os.remove(geoPath)
os.remove(unvGmshPath)
def _handleSimulation(self):
self._simulation("Coordinate System", "Cartesian 3D")
self._simulation("Coordinate Mapping", (1, 2, 3))
self._simulation("Simulation Type", "Steady state")
self._simulation("Steady State Max Iterations", 1)
self._simulation("Output Intervals", 1)
self._simulation("Timestepping Method", "BDF")
self._simulation("BDF Order", 1)
self._simulation("Use Mesh Names", True)
self._simulation(
"Steady State Max Iterations",
self.solver.SteadyStateMaxIterations)
self._simulation(
"Steady State Min Iterations",
self.solver.SteadyStateMinIterations)
def _handleHeat(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerHeat"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getHeatSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleHeatConstants()
self._handleHeatBndConditions()
self._handleHeatInitial(activeIn)
self._handleHeatBodyForces(activeIn)
self._handleHeatMaterial(activeIn)
def _getHeatSolver(self, equation):
s = self._createNonlinearSolver(equation)
s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("HeatSolve/HeatSolver")
s["Variable"] = self._getUniqueVarName("Temperature")
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleHeatConstants(self):
self._constant(
"Stefan Boltzmann",
getConstant("StefanBoltzmann", "M/(O^4*T^3)"))
def _handleHeatBndConditions(self):
for obj in self._getMember("Fem::ConstraintTemperature"):
if obj.References:
for name in obj.References[0][1]:
if obj.ConstraintType == "Temperature":
temp = getFromUi(obj.Temperature, "K", "O")
self._boundary(name, "Temperature", temp)
elif obj.ConstraintType == "CFlux":
flux = getFromUi(obj.CFlux, "kg*mm^2*s^-3", "M*L^2*T^-3")
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 = getFromUi(obj.FilmCoef, "W/(m^2*K)", "M/(T^3*O)")
temp = getFromUi(obj.AmbientTemp, "K", "O")
self._boundary(name, "Heat Transfer Coefficient", film)
self._boundary(name, "External Temperature", temp)
elif obj.ConstraintType == "DFlux":
flux = 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 = getFromUi(obj.initialTemperature, "K", "O")
self._initial(name, "Temperature", temp)
self._handled(obj)
def _handleHeatBodyForces(self, bodies):
obj = self._getSingleMember("Fem::FemConstraintBodyHeatSource")
if obj is not None:
for name in bodies:
heatSource = getFromUi(obj.HeatFlux, "W/kg", "L^2*T^-3")
self._bodyForce(name, "Heat Source", heatSource)
self._handled(obj)
def _handleHeatMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = 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):
self._material(
name, "Density",
self._getDensity(m))
self._material(
name, "Heat Conductivity",
convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
self._material(
name, "Heat Capacity",
convert(m["SpecificHeat"], "L^2/(T^2*O)"))
def _handleElasticity(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerElasticity"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getElasticitySolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleElasticityConstants()
self._handleElasticityBndConditions()
self._handleElasticityInitial(activeIn)
self._handleElasticityBodyForces(activeIn)
self._handleElasticityMaterial(activeIn)
def _getElasticitySolver(self, equation):
s = self._createLinearSolver(equation)
s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("StressSolve/StressSolver")
s["Variable"] = self._getUniqueVarName("Displacement")
s["Variable DOFs"] = 3
s["Eigen Analysis"] = equation.DoFrequencyAnalysis
s["Eigen System Values"] = equation.EigenmodesCount
s["Calculate Strains"] = equation.CalculateStrains
s["Calculate Stresses"] = equation.CalculateStresses
s["Calculate Principal"] = equation.CalculatePrincipal
s["Calculate Pangle"] = equation.CalculatePangle
s["Displace mesh"] = False
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleElasticityConstants(self):
pass
def _handleElasticityBndConditions(self):
for obj in self._getMember("Fem::ConstraintPressure"):
if obj.References:
for name in obj.References[0][1]:
pressure = getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
if not obj.Reversed:
pressure *= -1
self._boundary(name, "Normal Force", pressure)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintFixed"):
if obj.References:
for name in obj.References[0][1]:
self._boundary(name, "Displacement 1", 0.0)
self._boundary(name, "Displacement 2", 0.0)
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintForce"):
if obj.References:
for name in obj.References[0][1]:
force = 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)
self._boundary(name, "Force 1 Normalize by Area", True)
self._boundary(name, "Force 2 Normalize by Area", True)
self._boundary(name, "Force 3 Normalize by Area", True)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintDisplacement"):
if obj.References:
for name in obj.References[0][1]:
if not obj.xFree:
self._boundary(
name, "Displacement 1", obj.xDisplacement * 0.001)
elif obj.xFix:
self._boundary(name, "Displacement 1", 0.0)
if not obj.yFree:
self._boundary(
name, "Displacement 2", obj.yDisplacement * 0.001)
elif obj.yFix:
self._boundary(name, "Displacement 2", 0.0)
if not obj.zFree:
self._boundary(
name, "Displacement 3", obj.zDisplacement * 0.001)
elif obj.zFix:
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
def _handleElasticityInitial(self, bodies):
pass
def _handleElasticityBodyForces(self, bodies):
obj = self._getSingleMember("FemConstraintSelfWeight")
if obj is not None:
for name in bodies:
gravity = getConstant("Gravity", "L/T^2")
m = self._getBodyMaterial(name).Material
densityQuantity = Units.Quantity(m["Density"])
dimension = "M/L^3"
if name.startswith("Edge"):
density.Unit = Units.Unit(-2, 1)
dimension = "M/L^2"
density = convert(densityQuantity, dimension)
force1 = gravity * obj.Gravity_x * density
force2 = gravity * obj.Gravity_y * density
force3 = gravity * obj.Gravity_z * density
self._bodyForce(name, "Stress Bodyforce 1", force1)
self._bodyForce(name, "Stress Bodyforce 2", force2)
self._bodyForce(name, "Stress Bodyforce 3", force3)
self._handled(obj)
def _getBodyMaterial(self, name):
for obj in self._getMember("App::MaterialObject"):
if not obj.References or name in obj.References[0][1]:
return obj
return None
def _handleElasticityMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = 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):
self._material(
name, "Density",
self._getDensity(m))
self._material(
name, "Youngs Modulus",
self._getYoungsModulus(m))
self._material(
name, "Poisson ratio",
float(m["PoissonRatio"]))
self._material(
name, "Heat expansion Coefficient",
convert(m["ThermalExpansionCoefficient"], "O^-1"))
def _getDensity(self, m):
density = convert(m["Density"], "M/L^3")
if self._getMeshDimension() == 2:
density *= 1e3
return density
def _getYoungsModulus(self, m):
youngsModulus = convert(m["YoungsModulus"], "M/(L*T^2)")
if self._getMeshDimension() == 2:
youngsModulus *= 1e3
return youngsModulus
def _handleFlow(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerFlow"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getFlowSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleFlowConstants()
self._handleFlowBndConditions()
self._handleFlowInitialVelocity(activeIn)
#self._handleFlowInitial(activeIn)
#self._handleFlowBodyForces(activeIn)
self._handleFlowMaterial(activeIn)
self._handleFlowEquation(activeIn)
def _getFlowSolver(self, equation):
s = self._createNonlinearSolver(equation)
s["Equation"] = "Navier-Stokes"
#s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("FlowSolve/FlowSolver")
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleFlowConstants(self):
gravity = getConstant("Gravity", "L/T^2")
self._constant("Gravity", (0.0, -1.0, 0.0, gravity))
def _handleFlowMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = 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" in m:
self._material(
name, "Density",
self._getDensity(m))
if "ThermalConductivity" in m:
self._material(
name, "Heat Conductivity",
convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
if "KinematicViscosity" in m:
density = self._getDensity(m)
kViscosity = convert(m["KinematicViscosity"], "L^2/T")
self._material(
name, "Viscosity", kViscosity * density)
if "ThermalExpansionCoefficient" in m:
value = convert(m["ThermalExpansionCoefficient"], "O^-1")
if value > 0:
self._material(
name, "Heat expansion Coefficient", value)
if "ReferencePressure" in m:
pressure = convert(m["ReferencePressure"], "M/(L*T^2)")
self._material(name, "Reference Pressure", pressure)
if "SpecificHeatRatio" in m:
self._material(
name, "Specific Heat Ratio",
float(m["SpecificHeatRatio"]))
if "CompressibilityModel" in m:
self._material(
name, "Compressibility Model",
m["CompressibilityModel"])
def _handleFlowInitialVelocity(self, bodies):
obj = self._getSingleMember("Fem::ConstraintInitialFlowVelocity")
if obj is not None:
for name in bodies:
if obj.VelocityXEnabled:
velocity = getFromUi(obj.VelocityX, "m/s", "L/T")
self._initial(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = getFromUi(obj.VelocityY, "m/s", "L/T")
self._initial(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = getFromUi(obj.VelocityZ, "m/s", "L/T")
self._initial(name, "Velocity 3", velocity)
self._handled(obj)
def _handleFlowBndConditions(self):
for obj in self._getMember("Fem::ConstraintFlowVelocity"):
if obj.References:
for name in obj.References[0][1]:
if obj.VelocityXEnabled:
velocity = getFromUi(obj.VelocityX, "m/s", "L/T")
self._boundary(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = getFromUi(obj.VelocityY, "m/s", "L/T")
self._boundary(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = getFromUi(obj.VelocityZ, "m/s", "L/T")
self._boundary(name, "Velocity 3", velocity)
if obj.NormalToBoundary:
self._boundary(name, "Normal-Tangential Velocity", True)
self._handled(obj)
def _handleFlowEquation(self, bodies):
for b in bodies:
self._equation(b, "Convection", "Computed")
def _createLinearSolver(self, equation):
s = sifio.createSection(sifio.SOLVER)
s.priority = equation.Priority
s["Linear System Solver"] = equation.LinearSolverType
if equation.LinearSolverType == "Direct":
s["Linear System Direct Method"] = \
equation.LinearDirectMethod
elif equation.LinearSolverType == "Iterative":
s["Linear System Iterative Method"] = \
equation.LinearIterativeMethod
if equation.LinearIterativeMethod == "BiCGStabl":
s["BiCGstabl polynomial degree"] = \
equation.BiCGstablDegree
s["Linear System Max Iterations"] = \
equation.LinearIterations
s["Linear System Convergence Tolerance"] = \
equation.LinearTolerance
s["Linear System Preconditioning"] = \
equation.LinearPreconditioning
s["Steady State Convergence Tolerance"] = \
equation.SteadyStateTolerance
s["Linear System Abort Not Converged"] = False
s["Linear System Residual Output"] = 1
s["Linear System Precondition Recompute"] = 1
return s
def _createNonlinearSolver(self, equation):
s = self._createLinearSolver(equation)
s["Nonlinear System Max Iterations"] = \
equation.NonlinearIterations
s["Nonlinear System Convergence Tolerance"] = \
equation.NonlinearTolerance
s["Nonlinear System Relaxation Factor"] = \
equation.RelaxationFactor
s["Nonlinear System Newton After Iterations"] = \
equation.NonlinearNewtonAfterIterations
s["Nonlinear System Newton After Tolerance"] = \
equation.NonlinearNewtonAfterTolerance
return s
def _getUniqueVarName(self, varName):
postfix = 1
if varName in self._usedVarNames:
varName += "%2d" % postfix
while varName in self._usedVarNames:
postfix += 1
varName = varName[:-2] + "%2d" % postfix
self._usedVarNames.add(varName)
return varName
def _getAllBodies(self):
obj = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
bodyCount = 0
prefix = ""
if obj.Part.Shape.Solids:
prefix = "Solid"
bodyCount = len(obj.Part.Shape.Solids)
elif obj.Part.Shape.Faces:
prefix = "Face"
bodyCount = len(obj.Part.Shape.Faces)
elif obj.Part.Shape.Edges:
prefix = "Edge"
bodyCount = len(obj.Part.Shape.Edges)
return [prefix + str(i + 1) for i in range(bodyCount)]
def _getMeshDimension(self):
obj = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
if obj.Part.Shape.Solids:
return 3
elif obj.Part.Shape.Faces:
return 2
elif obj.Part.Shape.Edges:
return 1
return None
def _addOutputSolver(self):
s = sifio.createSection(sifio.SOLVER)
s["Equation"] = "ResultOutput"
s["Exec Solver"] = "After simulation"
s["Procedure"] = sifio.FileAttr("ResultOutputSolve/ResultOutputSolver")
s["Output File Name"] = sifio.FileAttr("case")
s["Vtu Format"] = True
for name in self._getAllBodies():
self._addSolver(name, s)
def _writeSif(self):
sifPath = os.path.join(self.directory, _SIF_NAME)
with open(sifPath, 'w') as fstream:
sif = sifio.Sif(self._builder)
sif.write(fstream)
def _handled(self, obj):
self._handledObjects.add(obj)
def _simulation(self, key, attr):
self._builder.simulation(key, attr)
def _constant(self, key, attr):
self._builder.constant(key, attr)
def _initial(self, body, key, attr):
self._builder.initial(body, key, attr)
def _material(self, body, key, attr):
self._builder.material(body, key, attr)
def _equation(self, body, key, attr):
self._builder.equation(body, key, attr)
def _bodyForce(self, body, key, attr):
self._builder.bodyForce(body, key, attr)
def _addSolver(self, body, solverSection):
self._builder.addSolver(body, solverSection)
def _boundary(self, boundary, key, attr):
self._builder.boundary(boundary, key, attr)
def _addSection(self, section):
self._builder.addSection(section)
def _getMember(self, t):
return FemUtils.getMember(self.analysis, t)
def _getSingleMember(self, t):
return FemUtils.getSingleMember(self.analysis, t)
class WriteError(Exception):
pass

View File

@@ -0,0 +1,96 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Markus Hovorka <m.hovorka@live.de> *
# * *
# * 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__ = "_Base"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FreeCAD
if FreeCAD.GuiUp:
from pivy import coin
class BaseProxy(object):
BaseType = "App::FeaturePython"
def __init__(self, obj):
obj.Proxy = self
obj.addProperty(
"App::PropertyLinkSubList", "References",
"Base", "")
def execute(self, obj):
return True
class BaseViewProxy(object):
def __init__(self, vobj):
vobj.Proxy = self
def attach(self, vobj):
default = coin.SoGroup()
vobj.addDisplayMode(default, "Default")
def getDisplayModes(self, obj):
"Return a list of display modes."
modes = ["Default"]
return modes
def getDefaultDisplayMode(self):
return "Default"
def setDisplayMode(self, mode):
return mode
class HeatProxy(BaseProxy):
pass
class HeatViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-heat.svg"
class ElasticityProxy(BaseProxy):
pass
class ElasticityViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-elasticity.svg"
class FlowProxy(BaseProxy):
pass
class FlowViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-flow.svg"