Fem: Enable cancel meshing for Gmsh - fixes #5914
This commit is contained in:
@@ -49,38 +49,35 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::InputField" name="if_max">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0 mm</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000.000000000000000</double>
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_max_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="decimals" stdset="0">
|
||||
<number>2</number>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="value" stdset="0">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -93,38 +90,35 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="Gui::InputField" name="if_min">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0 mm</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000.000000000000000</double>
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_min_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="decimals" stdset="0">
|
||||
<number>2</number>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="value" stdset="0">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -219,9 +213,9 @@
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::InputField</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>Gui/InputField.h</header>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
|
||||
@@ -46,17 +46,25 @@ class GmshError(Exception):
|
||||
|
||||
|
||||
class GmshTools:
|
||||
|
||||
name = "Gmsh"
|
||||
|
||||
def __init__(self, gmsh_mesh_obj, analysis=None):
|
||||
|
||||
# mesh obj
|
||||
self.mesh_obj = gmsh_mesh_obj
|
||||
|
||||
self.process = None
|
||||
# analysis
|
||||
if analysis:
|
||||
self.analysis = analysis
|
||||
else:
|
||||
self.analysis = None
|
||||
|
||||
self.load_properties()
|
||||
self.error = False
|
||||
|
||||
def load_properties(self):
|
||||
# part to mesh
|
||||
self.part_obj = self.mesh_obj.Shape
|
||||
|
||||
@@ -186,7 +194,6 @@ class GmshTools:
|
||||
self.temp_file_geo = ""
|
||||
self.mesh_name = ""
|
||||
self.gmsh_bin = ""
|
||||
self.error = False
|
||||
|
||||
def update_mesh_data(self):
|
||||
self.start_logs()
|
||||
@@ -199,6 +206,27 @@ class GmshTools:
|
||||
self.write_part_file()
|
||||
self.write_geo()
|
||||
|
||||
def compute(self):
|
||||
self.load_properties()
|
||||
self.update_mesh_data()
|
||||
self.get_tmp_file_paths()
|
||||
self.get_gmsh_command()
|
||||
self.write_gmsh_input_files()
|
||||
|
||||
command_list = [self.gmsh_bin, "-", self.temp_file_geo]
|
||||
self.process = subprocess.Popen(
|
||||
command_list, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
out, err = self.process.communicate()
|
||||
if self.process.returncode != 0:
|
||||
raise RuntimeError(err.decode("utf-8"))
|
||||
|
||||
return True
|
||||
|
||||
def update_properties(self):
|
||||
self.mesh_obj.FemMesh = Fem.read(self.temp_file_mesh)
|
||||
|
||||
def create_mesh(self):
|
||||
try:
|
||||
self.update_mesh_data()
|
||||
@@ -399,7 +427,7 @@ class GmshTools:
|
||||
# if self.group_elements:
|
||||
# Console.PrintMessage(" {}\n".format(self.group_elements))
|
||||
|
||||
def get_gmsh_version(self):
|
||||
def version(self):
|
||||
self.get_gmsh_command()
|
||||
if os.path.exists(self.gmsh_bin):
|
||||
found_message = "file found: " + self.gmsh_bin
|
||||
@@ -407,7 +435,7 @@ class GmshTools:
|
||||
else:
|
||||
found_message = "file not found: " + self.gmsh_bin
|
||||
Console.PrintError(found_message + "\n")
|
||||
return (None, None, None), found_message
|
||||
return found_message
|
||||
|
||||
command_list = [self.gmsh_bin, "--info"]
|
||||
try:
|
||||
@@ -420,26 +448,10 @@ class GmshTools:
|
||||
)
|
||||
except Exception as e:
|
||||
Console.PrintMessage(str(e) + "\n")
|
||||
return (None, None, None), found_message + "\n\n" + "Error: " + str(e)
|
||||
return found_message + "\n\n" + "Error: " + str(e)
|
||||
|
||||
gmsh_stdout, gmsh_stderr = p.communicate()
|
||||
Console.PrintMessage("Gmsh: StdOut:\n" + gmsh_stdout + "\n")
|
||||
if gmsh_stderr:
|
||||
Console.PrintError("Gmsh: StdErr:\n" + gmsh_stderr + "\n")
|
||||
|
||||
from re import search
|
||||
|
||||
# use raw string mode to get pep8 quiet
|
||||
# https://stackoverflow.com/q/61497292
|
||||
# https://github.com/MathSci/fecon236/issues/6
|
||||
match = search(r"^Version\s*:\s*(\d+)\.(\d+)\.(\d+)", gmsh_stdout)
|
||||
# return (major, minor, patch), fullmessage
|
||||
if match:
|
||||
mess = found_message + "\n\n" + gmsh_stdout
|
||||
return match.group(1, 2, 3), mess
|
||||
else:
|
||||
mess = found_message + "\n\n" + "Warning: Output not recognized\n\n" + gmsh_stdout
|
||||
return (None, None, None), mess
|
||||
return gmsh_stdout
|
||||
|
||||
def get_region_data(self):
|
||||
# mesh regions
|
||||
|
||||
@@ -29,24 +29,17 @@ __url__ = "https://www.freecad.org"
|
||||
# \ingroup FEM
|
||||
# \brief task panel for mesh gmsh object
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide import QtGui
|
||||
from PySide.QtCore import Qt
|
||||
from PySide.QtGui import QApplication
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import FemGui
|
||||
from femtools.femutils import is_of_type
|
||||
from femtools.femutils import getOutputWinColor
|
||||
from . import base_femtaskpanel
|
||||
from femmesh import gmshtools
|
||||
|
||||
from . import base_femmeshtaskpanel
|
||||
|
||||
|
||||
class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing References property of
|
||||
MeshGmsh objects and creation of new FEM mesh
|
||||
@@ -58,16 +51,14 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/MeshGmsh.ui"
|
||||
)
|
||||
self.Timer = QtCore.QTimer()
|
||||
self.Timer.start(100) # 100 milli seconds
|
||||
self.gmsh_runs = False
|
||||
self.console_message_gmsh = ""
|
||||
|
||||
self.tool = gmshtools.GmshTools(obj)
|
||||
|
||||
QtCore.QObject.connect(
|
||||
self.form.if_max, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.max_changed
|
||||
self.form.qsb_max_size, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.max_changed
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.if_min, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.min_changed
|
||||
self.form.qsb_min_size, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.min_changed
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.cb_dimension, QtCore.SIGNAL("activated(int)"), self.choose_dimension
|
||||
@@ -75,40 +66,16 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
QtCore.QObject.connect(
|
||||
self.form.cb_order, QtCore.SIGNAL("activated(int)"), self.choose_order
|
||||
)
|
||||
QtCore.QObject.connect(self.Timer, QtCore.SIGNAL("timeout()"), self.update_timer_text)
|
||||
QtCore.QObject.connect(
|
||||
self.form.pb_get_gmsh_version, QtCore.SIGNAL("clicked()"), self.get_gmsh_version
|
||||
)
|
||||
|
||||
self.form.cb_dimension.addItems(self.obj.getEnumerationsOfProperty("ElementDimension"))
|
||||
|
||||
self.form.cb_order.addItems(self.obj.getEnumerationsOfProperty("ElementOrder"))
|
||||
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.update_timer_text)
|
||||
QtCore.QObject.connect(
|
||||
self.form.pb_get_gmsh_version, QtCore.SIGNAL("clicked()"), self.get_version
|
||||
)
|
||||
|
||||
self.get_mesh_params()
|
||||
self.get_active_analysis()
|
||||
self.update()
|
||||
|
||||
def getStandardButtons(self):
|
||||
button_value = (
|
||||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel
|
||||
)
|
||||
return button_value
|
||||
# show a OK, a apply and a Cancel button
|
||||
# def reject() is called on Cancel button
|
||||
# def clicked(self, button) is needed, to access the apply button
|
||||
|
||||
def accept(self):
|
||||
self.set_mesh_params()
|
||||
return super().accept()
|
||||
|
||||
def reject(self):
|
||||
self.Timer.stop()
|
||||
return super().reject()
|
||||
|
||||
def clicked(self, button):
|
||||
if button == QtGui.QDialogButtonBox.Apply:
|
||||
self.set_mesh_params()
|
||||
self.run_gmsh()
|
||||
self.set_widgets()
|
||||
|
||||
def get_mesh_params(self):
|
||||
self.clmax = self.obj.CharacteristicLengthMax
|
||||
@@ -122,42 +89,21 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
self.obj.ElementDimension = self.dimension
|
||||
self.obj.ElementOrder = self.order
|
||||
|
||||
def update(self):
|
||||
def set_widgets(self):
|
||||
"fills the widgets"
|
||||
self.form.if_max.setText(self.clmax.UserString)
|
||||
self.form.if_min.setText(self.clmin.UserString)
|
||||
self.form.qsb_max_size.setProperty("value", self.clmax)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_max_size).bind(
|
||||
self.obj, "CharacteristicLengthMax"
|
||||
)
|
||||
self.form.qsb_min_size.setProperty("value", self.clmin)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_min_size).bind(
|
||||
self.obj, "CharacteristicLengthMin"
|
||||
)
|
||||
index_dimension = self.form.cb_dimension.findText(self.dimension)
|
||||
self.form.cb_dimension.setCurrentIndex(index_dimension)
|
||||
index_order = self.form.cb_order.findText(self.order)
|
||||
self.form.cb_order.setCurrentIndex(index_order)
|
||||
|
||||
def console_log(self, message="", outputwin_color_type=None):
|
||||
self.console_message_gmsh = self.console_message_gmsh + (
|
||||
'<font color="{}"><b>{:4.1f}:</b></font> '.format(
|
||||
getOutputWinColor("Logging"), time.time() - self.Start
|
||||
)
|
||||
)
|
||||
if outputwin_color_type:
|
||||
if outputwin_color_type == "#00AA00": # Success is not part of output window parameters
|
||||
self.console_message_gmsh += '<font color="{}">{}</font><br>'.format(
|
||||
outputwin_color_type, message
|
||||
)
|
||||
else:
|
||||
self.console_message_gmsh += '<font color="{}">{}</font><br>'.format(
|
||||
getOutputWinColor(outputwin_color_type), message
|
||||
)
|
||||
else:
|
||||
self.console_message_gmsh += message + "<br>"
|
||||
self.form.te_output.setText(self.console_message_gmsh)
|
||||
self.form.te_output.moveCursor(QtGui.QTextCursor.End)
|
||||
|
||||
def update_timer_text(self):
|
||||
# FreeCAD.Console.PrintMessage("timer1\n")
|
||||
if self.gmsh_runs:
|
||||
FreeCAD.Console.PrintMessage("timer2\n")
|
||||
# FreeCAD.Console.PrintMessage("Time: {0:4.1f}: \n".format(time.time() - self.Start))
|
||||
self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ")
|
||||
|
||||
def max_changed(self, base_quantity_value):
|
||||
self.clmax = base_quantity_value
|
||||
|
||||
@@ -168,81 +114,10 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel):
|
||||
if index < 0:
|
||||
return
|
||||
self.form.cb_dimension.setCurrentIndex(index)
|
||||
self.dimension = str(self.form.cb_dimension.itemText(index)) # form returns unicode
|
||||
self.dimension = self.form.cb_dimension.itemText(index)
|
||||
|
||||
def choose_order(self, index):
|
||||
if index < 0:
|
||||
return
|
||||
self.form.cb_order.setCurrentIndex(index)
|
||||
self.order = str(self.form.cb_order.itemText(index)) # form returns unicode
|
||||
|
||||
def get_gmsh_version(self):
|
||||
from femmesh import gmshtools
|
||||
|
||||
version, full_message = gmshtools.GmshTools(self.obj, self.analysis).get_gmsh_version()
|
||||
if version[0] and version[1] and version[2]:
|
||||
messagebox = QtGui.QMessageBox.information
|
||||
else:
|
||||
messagebox = QtGui.QMessageBox.warning
|
||||
messagebox(None, "Gmsh - Information", full_message)
|
||||
|
||||
def run_gmsh(self):
|
||||
from femmesh import gmshtools
|
||||
|
||||
gmsh_mesh = gmshtools.GmshTools(self.obj, self.analysis)
|
||||
QApplication.setOverrideCursor(Qt.WaitCursor)
|
||||
part = self.obj.Shape
|
||||
if (
|
||||
self.obj.MeshRegionList
|
||||
and part.Shape.ShapeType == "Compound"
|
||||
and (
|
||||
is_of_type(part, "FeatureBooleanFragments")
|
||||
or is_of_type(part, "FeatureSlice")
|
||||
or is_of_type(part, "FeatureXOR")
|
||||
)
|
||||
):
|
||||
gmsh_mesh.outputCompoundWarning()
|
||||
self.Start = time.time()
|
||||
self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ")
|
||||
self.console_message_gmsh = ""
|
||||
self.gmsh_runs = True
|
||||
self.console_log("We are going to start ...")
|
||||
self.get_active_analysis()
|
||||
self.console_log("Start Gmsh ...")
|
||||
error = ""
|
||||
try:
|
||||
error = gmsh_mesh.create_mesh()
|
||||
except Exception:
|
||||
error = sys.exc_info()[1]
|
||||
FreeCAD.Console.PrintError(f"Unexpected error when creating mesh: {error}\n")
|
||||
if error:
|
||||
FreeCAD.Console.PrintWarning("Gmsh had warnings:\n")
|
||||
FreeCAD.Console.PrintWarning(f"{error}\n")
|
||||
self.console_log("Gmsh had warnings ...", "Warning")
|
||||
self.console_log(error, "Error")
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage("Clean run of Gmsh\n")
|
||||
self.console_log("Clean run of Gmsh", "#00AA00")
|
||||
self.console_log("Gmsh done!")
|
||||
self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ")
|
||||
self.Timer.stop()
|
||||
self.update()
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
def get_active_analysis(self):
|
||||
analysis = FemGui.getActiveAnalysis()
|
||||
if not analysis:
|
||||
FreeCAD.Console.PrintLog("No active analysis, means no group meshing.\n")
|
||||
self.analysis = None # no group meshing
|
||||
else:
|
||||
for m in analysis.Group:
|
||||
if m.Name == self.obj.Name:
|
||||
FreeCAD.Console.PrintLog(f"Active analysis found: {analysis.Name}\n")
|
||||
self.analysis = analysis # group meshing
|
||||
break
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"Mesh is not member of active analysis, means no group meshing.\n"
|
||||
)
|
||||
self.analysis = None # no group meshing
|
||||
return
|
||||
self.order = self.form.cb_order.itemText(index)
|
||||
|
||||
Reference in New Issue
Block a user