[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:
Uwe
2022-07-10 17:25:19 +02:00
parent 74e77f0191
commit eec258ab37
11 changed files with 401 additions and 231 deletions

View File

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

View File

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

View File

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