[FEM] enable to run Elmer multi-threaded
- this needs proper testing, especially on a non-Windows system - note that for some tasks multi-threading requires non-standard additional solvers like MUMPS. Ideally the user should be informed about this, depending on the equations he uses. But this should not block this PR, meaning to use multi-threading in general.
This commit is contained in:
@@ -121,10 +121,19 @@ class Solve(run.Solve):
|
||||
if os.path.isdir(solvpath):
|
||||
os.environ["ELMER_HOME"] = solvpath
|
||||
os.environ["LD_LIBRARY_PATH"] = "$LD_LIBRARY_PATH:{}/modules".format(solvpath)
|
||||
# hide the popups on Windows
|
||||
# different call depending if with multithreading or not
|
||||
num_cores = settings.get_cores("ElmerSolver")
|
||||
args = []
|
||||
if int(num_cores) > 1:
|
||||
if system() != "Windows":
|
||||
args.extend(["mpirun"])
|
||||
else:
|
||||
args.extend(["mpiexec"])
|
||||
args.extend(["-np", num_cores])
|
||||
args.extend([binary])
|
||||
if system() == "Windows":
|
||||
self._process = subprocess.Popen(
|
||||
[binary],
|
||||
args,
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
@@ -132,7 +141,7 @@ class Solve(run.Solve):
|
||||
)
|
||||
else:
|
||||
self._process = subprocess.Popen(
|
||||
[binary],
|
||||
args,
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
@@ -190,12 +199,16 @@ class Results(run.Results):
|
||||
# elmer post file path changed with version x.x
|
||||
# see https://forum.freecadweb.org/viewtopic.php?f=18&t=42732
|
||||
# workaround
|
||||
possible_post_file_0 = os.path.join(self.directory, "case0001.vtu")
|
||||
possible_post_file_t = os.path.join(self.directory, "case_t0001.vtu")
|
||||
if os.path.isfile(possible_post_file_0):
|
||||
postPath = possible_post_file_0
|
||||
elif os.path.isfile(possible_post_file_t):
|
||||
postPath = possible_post_file_t
|
||||
possible_post_file_old = os.path.join(self.directory, "case0001.vtu")
|
||||
possible_post_file_single = os.path.join(self.directory, "case_t0001.vtu")
|
||||
possible_post_file_multi = os.path.join(self.directory, "case_t0001.pvtu")
|
||||
# first try the multi-thread result, then single then old name
|
||||
if os.path.isfile(possible_post_file_multi):
|
||||
postPath = possible_post_file_multi
|
||||
elif os.path.isfile(possible_post_file_single):
|
||||
postPath = possible_post_file_single
|
||||
elif os.path.isfile(possible_post_file_old):
|
||||
postPath = possible_post_file_old
|
||||
else:
|
||||
self.report.error("Result file not found.")
|
||||
self.fail()
|
||||
|
||||
@@ -216,23 +216,38 @@ class Writer(object):
|
||||
)
|
||||
else:
|
||||
binary = settings.get_binary("ElmerGrid")
|
||||
num_cores = settings.get_cores("ElmerGrid")
|
||||
if binary is None:
|
||||
raise WriteError("Could not find ElmerGrid binary.")
|
||||
args = [binary,
|
||||
_ELMERGRID_IFORMAT,
|
||||
_ELMERGRID_OFORMAT,
|
||||
unvPath,
|
||||
"-scale", "0.001", "0.001", "0.001",
|
||||
"-out", self.directory]
|
||||
# hide the popups on Windows
|
||||
# for multithreading we first need a normal mesh creation run
|
||||
# then a second to split the mesh into the number of used cores
|
||||
argsBasic = [binary,
|
||||
_ELMERGRID_IFORMAT,
|
||||
_ELMERGRID_OFORMAT,
|
||||
unvPath,
|
||||
"-scale", "0.001", "0.001", "0.001"]
|
||||
args = argsBasic
|
||||
args.extend(["-out", self.directory])
|
||||
if system() == "Windows":
|
||||
subprocess.call(
|
||||
args,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
startupinfo=femutils.startProgramInfo("hide")
|
||||
)
|
||||
else:
|
||||
subprocess.call(args, stdout=subprocess.DEVNULL)
|
||||
if int(num_cores) > 1:
|
||||
args = argsBasic
|
||||
args.extend(["-partdual", "-metiskway", num_cores,
|
||||
"-out", self.directory])
|
||||
if system() == "Windows":
|
||||
subprocess.call(
|
||||
args,
|
||||
stdout=subprocess.DEVNULL,
|
||||
startupinfo=femutils.startProgramInfo("hide")
|
||||
)
|
||||
else:
|
||||
subprocess.call(args, stdout=subprocess.DEVNULL)
|
||||
|
||||
def _writeStartinfo(self):
|
||||
path = os.path.join(self.directory, _STARTINFO_NAME)
|
||||
|
||||
@@ -94,10 +94,7 @@ def get_binary(name):
|
||||
"""
|
||||
if name in _SOLVER_PARAM:
|
||||
binary = _SOLVER_PARAM[name].get_binary()
|
||||
FreeCAD.Console.PrintMessage(
|
||||
'Solver binary path (returned from binary getter): {} \n'
|
||||
.format(binary)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage('Solver binary path: {} \n'.format(binary))
|
||||
return binary
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
@@ -107,6 +104,28 @@ def get_binary(name):
|
||||
)
|
||||
return None
|
||||
|
||||
def get_cores(name):
|
||||
""" Read number of CPU cores for solver *name* honoring user settings.
|
||||
|
||||
Returns number of CPU cores to be used for the solvier run
|
||||
|
||||
:param name: solver id as a ``str`` (see :mod:`femsolver.settings`)
|
||||
"""
|
||||
if name in _SOLVER_PARAM:
|
||||
cores = _SOLVER_PARAM[name].get_cores()
|
||||
FreeCAD.Console.PrintMessage(
|
||||
'Number of CPU cores to be used for the solvier run: {} \n'
|
||||
.format(cores)
|
||||
)
|
||||
return cores
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Settings solver name: {} not found in "
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n"
|
||||
.format(name)
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_write_comments(name):
|
||||
""" Check whether "write_comments" is set for solver.
|
||||
@@ -220,6 +239,10 @@ class _SolverDlg(object):
|
||||
FreeCAD.Console.PrintLog("Solver binary found path: {}\n".format(the_found_binary))
|
||||
return the_found_binary
|
||||
|
||||
def get_cores(self):
|
||||
cores = str(self.param_group.GetInt("UseNumberOfCores"))
|
||||
return cores
|
||||
|
||||
def get_write_comments(self):
|
||||
return self.param_group.GetBool(self.WRITE_COMMENTS_PARAM, True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user