refactor(python): deduplicate run and render implementations - sphere - GPU-based 3D discrete element method algorithm with optional fluid coupling
HTML git clone git://src.adamsgaard.dk/sphere
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
DIR commit 6c9f4c262c98d09947e6c8df549c1fc5ae4d8db2
DIR parent 83cf64c7d715bcc75ebe0f9632ae6f2de0fbbf4f
HTML Author: Anders Damsgaard <anders@adamsgaard.dk>
Date: Sun, 5 Jul 2026 21:49:23 +0200
refactor(python): deduplicate run and render implementations
The module-level run() and render() are now the single implementations,
generalized to the superset of their sim-method counterparts; the methods
delegate. video(), status() and cleanup() methods already delegated.
Behavior change: module-level run() now warns on nonzero exit status.
Diffstat:
M python/sphere.py | 128 +++++++++++++++----------------
1 file changed, 62 insertions(+), 66 deletions(-)
---
DIR diff --git a/python/sphere.py b/python/sphere.py
@@ -4460,38 +4460,8 @@ class sim:
'''
self.writebin(verbose=False)
-
- quiet = ""
- stdout = ""
- dryarg = ""
- fluidarg = ""
- devicearg = ""
- valgrindbin = ""
- cudamemchk = ""
- binary = "sphere"
- if not verbose:
- quiet = "-q "
- if hideinputfile:
- stdout = " > /dev/null"
- if dry:
- dryarg = "--dry "
- if valgrind:
- valgrindbin = "valgrind -q --track-origins=yes "
- if cudamemcheck:
- cudamemchk = "cuda-memcheck --leak-check full "
- if self.fluid:
- fluidarg = "--fluid "
- if device != -1:
- devicearg = "-d " + str(device) + " "
-
- cmd = "cd ..; " + valgrindbin + cudamemchk + "./" + binary + " " \
- + quiet + dryarg + fluidarg + devicearg + \
- "input/" + self.sid + ".bin " + stdout
- #print(cmd)
- status = subprocess.call(cmd, shell=True)
-
- if status != 0:
- print("Warning: the sphere run returned with status " + str(status))
+ run('input/' + self.sid + '.bin', verbose, hideinputfile, dry,
+ valgrind, cudamemcheck, device, self.fluid)
def cleanup(self):
'''
@@ -4600,27 +4570,8 @@ class sim:
'''
print("Rendering {} images with the raytracer".format(self.sid))
-
- quiet = ""
- if not verbose:
- quiet = "-q"
-
- # Render images using sphere raytracer
- if method == "normal":
- subprocess.call("cd ..; for F in `ls output/" + self.sid
- + "*.bin`; do ./sphere " + quiet
- + " --render $F; done", shell=True)
- else:
- subprocess.call("cd ..; for F in `ls output/" + self.sid
- + "*.bin`; do ./sphere " + quiet
- + " --method " + method + " {}".format(max_val)
- + " -l {}".format(lower_cutoff)
- + " --render $F; done", shell=True)
-
- # Convert images to compressed format
- if verbose:
- print('converting images to ' + graphics_format)
- convert(graphics_format=graphics_format)
+ render('output/' + self.sid + '*.bin', method, max_val, lower_cutoff,
+ graphics_format, verbose)
def video(self, out_folder="./", video_format="mp4",
graphics_folder="../img_out/", graphics_format="png", fps=25,
@@ -7374,8 +7325,12 @@ def convert(graphics_format='png', folder='../img_out', remove_ppm=False):
def render(binary, method='pres', max_val=1e3, lower_cutoff=0.0,
graphics_format='png', verbose=True):
'''
- Render target binary using the ``sphere`` raytracer.
+ Render target binary using the ``sphere`` raytracer. The target may be
+ a shell glob matching several binaries.
+ :param binary: Input file(s) for the raytracer, relative to the
+ ``sphere`` root folder. May contain shell wildcards.
+ :type binary: str
:param method: The color visualization method to use for the particles.
Possible values are: 'normal': color all particles with the same
color, 'pres': color by pressure, 'vel': color by translational
@@ -7400,17 +7355,19 @@ def render(binary, method='pres', max_val=1e3, lower_cutoff=0.0,
# Render images using sphere raytracer
if method == 'normal':
- subprocess.call('cd .. ; ./sphere ' + quiet + \
- ' --render ' + binary, shell=True)
+ subprocess.call('cd ..; for F in `ls ' + binary + '`; do '
+ + './sphere ' + quiet
+ + ' --render $F; done', shell=True)
else:
- subprocess.call('cd .. ; ./sphere ' + quiet + \
- ' --method ' + method + ' {}'.format(max_val) + \
- ' -l {}'.format(lower_cutoff) + \
- ' --render ' + binary, shell=True)
+ subprocess.call('cd ..; for F in `ls ' + binary + '`; do '
+ + './sphere ' + quiet
+ + ' --method ' + method + ' {}'.format(max_val)
+ + ' -l {}'.format(lower_cutoff)
+ + ' --render $F; done', shell=True)
# Convert images to compressed format
if verbose:
- print('converting to ' + graphics_format)
+ print('converting images to ' + graphics_format)
convert(graphics_format)
def video(project, out_folder='./', video_format='mp4',
@@ -7504,26 +7461,65 @@ def thinsectionVideo(project, out_folder="./", video_format="mp4", fps=25,
+ out_folder + "/" + project + "-ts-x1x3." + video_format,
shell=True)
-def run(binary, verbose=True, hideinputfile=False):
+def run(binary, verbose=True, hideinputfile=False, dry=False, valgrind=False,
+ cudamemcheck=False, device=-1, fluid=False):
'''
Execute ``sphere`` with target binary file as input.
- :param binary: Input file for ``sphere``
+ :param binary: Input file for ``sphere``, relative to the ``sphere``
+ root folder
:type binary: str
:param verbose: Show ``sphere`` output
:type verbose: bool
:param hideinputfile: Hide the input file
:type hideinputfile: bool
+ :param dry: Perform a dry run. Important parameter values are shown by
+ the ``sphere`` program, and it exits afterwards.
+ :type dry: bool
+ :param valgrind: Run the program with ``valgrind`` in order to check
+ memory leaks in the host code. This causes a significant increase in
+ computational time.
+ :type valgrind: bool
+ :param cudamemcheck: Run the program with ``cudamemcheck`` in order to
+ check for device memory leaks and errors. This causes a significant
+ increase in computational time.
+ :type cudamemcheck: bool
+ :param device: Specify the GPU device to execute the program on.
+ If not specified, sphere will use the device with the most CUDA cores.
+ To see a list of devices, run ``nvidia-smi`` in the system shell.
+ :type device: int
+ :param fluid: Simulate fluid between the particles
+ :type fluid: bool
'''
quiet = ''
stdout = ''
+ dryarg = ''
+ fluidarg = ''
+ devicearg = ''
+ valgrindbin = ''
+ cudamemchk = ''
if not verbose:
- quiet = '-q'
+ quiet = '-q '
if hideinputfile:
stdout = ' > /dev/null'
- subprocess.call('cd ..; ./sphere ' + quiet + ' ' + binary + ' ' + stdout, \
- shell=True)
+ if dry:
+ dryarg = '--dry '
+ if valgrind:
+ valgrindbin = 'valgrind -q --track-origins=yes '
+ if cudamemcheck:
+ cudamemchk = 'cuda-memcheck --leak-check full '
+ if fluid:
+ fluidarg = '--fluid '
+ if device != -1:
+ devicearg = '-d ' + str(device) + ' '
+
+ status = subprocess.call('cd ..; ' + valgrindbin + cudamemchk
+ + './sphere ' + quiet + dryarg + fluidarg
+ + devicearg + binary + ' ' + stdout, shell=True)
+
+ if status != 0:
+ print('Warning: the sphere run returned with status ' + str(status))
def torqueScriptParallel3(obj1, obj2, obj3, email='adc@geo.au.dk',
email_alerts='ae', walltime='24:00:00',