- Notifications
You must be signed in to change notification settings - Fork 536
Fixing the base interface _run_interface function for NiftyRegCommand #2005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… for setting the OMP default value.
nipype/interfaces/niftyreg/base.py Outdated
if not isdefined(self.inputs.environ['OMP_NUM_THREADS']): | ||
self.inputs.environ['OMP_NUM_THREADS'] = self.num_threads | ||
omp_key = 'OMP_NUM_THREADS' | ||
if omp_key in os.environ: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest this instead of the if .. else
:
self.inputs.environ[omp_key] = os.getenv(omp_key, '%d' % self.num_threads)
This PR is probably not the right place for it, but the |
I was coming back to this and, correct me if I'm wrong, but this PR does not actually fix the issue: If So it is not really necessary to make sure of setting it again, it will be already in the environment of the call to subprocess. Then, I've noticed several issues in the code (sorry I reviewed the PR too quickly before):
def _run_interface(self, runtime): if isdefined(self.inputs.omp_core_val): self.inputs.environ['OMP_NUM_THREADS'] = '%d' % self.input.omp_core_val return super(NiftyRegCommand, self)._run_interface(runtime) Then, the format_arg function would go away. If OMP_NUM_THREADS is not specified (not defined in the environment and omp_core_val is not defined), then I think nifty* should handle that situation. (meaning, I don't think it is a good idea to define a default value here). |
@effigies just reminded me that BaseInterface has a def _run_interface(self, runtime): if isdefined(self.inputs.omp_core_val): self.inputs.environ['OMP_NUM_THREADS'] = '%d' % self.input.omp_core_val if self.inputs.environ.get('OMP_NUM_THREADS') is not None: self.num_threads = int(self.inputs.environ.get('OMP_NUM_THREADS')) return super(NiftyRegCommand, self)._run_interface(runtime) |
@oesteban - i don't think we should modify also self.num_threads should be set whenever when the multiproc plugin executes, it will want to know how many threads to use, essentially here are the scenarios:
|
hmm so how do you want me to proceed? I can also remove the option from the interface and let nipype take care of this and the nifty executables are going to use the OMP_NUM_THREADS value from the env variables. WDYT? |
As @chrisfilo said and @satra confirmed, I think we should give this a general solution. |
@oesteban - i really would like to release 0.13 before the general solution. can you see if the solution below works? @byvernault - so for these interfaces i would suggest the following, and remove overloading
|
updated the |
@satra I could be wrong, but I think your solution will always set diff --git a/nipype/interfaces/niftyreg/base.py b/nipype/interfaces/niftyreg/base.py index a7672166f..0c7e22d60 100644 --- a/nipype/interfaces/niftyreg/base.py +++ b/nipype/interfaces/niftyreg/base.py @@ -47,8 +47,8 @@ def no_nifty_package(cmd='reg_f3d'): class NiftyRegCommandInputSpec(CommandLineInputSpec): """Input Spec for niftyreg interfaces.""" # Set the number of omp thread to use - omp_core_val = traits.Int(desc='Number of openmp thread to use', - argstr='-omp %i') + omp_core_val = traits.Int(1, desc='Number of openmp thread to use', + argstr='-omp %i', usedefault=True) class NiftyRegCommand(CommandLine): @@ -73,6 +73,15 @@ class NiftyRegCommand(CommandLine): msg = 'The version of NiftyReg differs from the required' msg += '(%s != %s)' warn(msg % (_version, self.required_version)) + self._omp_update() + self.inputs.on_trait_change(self._omp_update, 'omp_core_val') + + def _omp_update(self): + if self.inputs.omp_core_val: + self.inputs.environ['OMP_NUM_THREADS'] = self.inputs.omp_core_val + self.num_threads = self.inputs.omp_core_val + else: + self.num_threads = 1 Also modified your environment update line, as it currently clobbers any user-set WDYT? (And what should happen to |
Sure, I will edit the code for this PR tomorrow morning. All the tests will need to be updated with the default value. I remember you wanted to do a release so I was trying to fix this so those interfaces are working properly. Thank you for your help guys. |
@effigies and @byvernault - here is an update. if this passes muster, @byvernault - you should be able to patch this in: diff --git a/nipype/interfaces/niftyreg/base.py b/nipype/interfaces/niftyreg/base.py index a7672166f..fca2a1b97 100644 --- a/nipype/interfaces/niftyreg/base.py +++ b/nipype/interfaces/niftyreg/base.py @@ -26,7 +26,8 @@ import shutil import subprocess from warnings import warn -from ..base import CommandLine, isdefined, CommandLineInputSpec, traits +from ..base import (CommandLine, isdefined, CommandLineInputSpec, traits, + Undefined) from ...utils.filemanip import split_filename @@ -47,8 +48,8 @@ def no_nifty_package(cmd='reg_f3d'): class NiftyRegCommandInputSpec(CommandLineInputSpec): """Input Spec for niftyreg interfaces.""" # Set the number of omp thread to use - omp_core_val = traits.Int(desc='Number of openmp thread to use', - argstr='-omp %i') + omp_core_val = traits.Int(1, desc='Number of openmp thread to use', + argstr='-omp %i', usedefault=True) class NiftyRegCommand(CommandLine): @@ -58,7 +59,10 @@ class NiftyRegCommand(CommandLine): _suffix = '_nr' _min_version = '1.5.30' + input_spec = NiftyRegCommandInputSpec + def __init__(self, required_version=None, **inputs): + self.num_threads = 1 super(NiftyRegCommand, self).__init__(**inputs) self.required_version = required_version _version = self.get_version() @@ -73,6 +77,26 @@ class NiftyRegCommand(CommandLine): msg = 'The version of NiftyReg differs from the required' msg += '(%s != %s)' warn(msg % (_version, self.required_version)) + self.inputs.on_trait_change(self._omp_update, 'omp_core_val') + self.inputs.on_trait_change(self._environ_update, 'environ') + + def _omp_update(self): + if self.inputs.omp_core_val: + self.inputs.environ['OMP_NUM_THREADS'] = \ + str(self.inputs.omp_core_val) + self.num_threads = self.inputs.omp_core_val + else: + if 'OMP_NUM_THREADS' in self.inputs.environ: + del self.inputs.environ['OMP_NUM_THREADS'] + self.num_threads = 1 + + def _environ_update(self): + if self.inputs.environ: + if 'OMP_NUM_THREADS' in self.inputs.environ: + self.inputs.omp_core_val = \ + int(self.inputs.environ['OMP_NUM_THREADS']) + else: + self.inputs.omp_core_val = Undefined def check_version(self): _version = self.get_version() |
one extra else: + def _environ_update(self): + if self.inputs.environ: + if 'OMP_NUM_THREADS' in self.inputs.environ: + self.inputs.omp_core_val = \ + int(self.inputs.environ['OMP_NUM_THREADS']) + else: + self.inputs.omp_core_val = Undefined + else: + self.inputs.omp_core_val = Undefined |
here is the full thing (also added an initial sync with a call to diff --git a/nipype/interfaces/niftyreg/base.py b/nipype/interfaces/niftyreg/base.py index a7672166f..ca0e2e634 100644 --- a/nipype/interfaces/niftyreg/base.py +++ b/nipype/interfaces/niftyreg/base.py @@ -26,7 +26,8 @@ import shutil import subprocess from warnings import warn -from ..base import CommandLine, isdefined, CommandLineInputSpec, traits +from ..base import (CommandLine, isdefined, CommandLineInputSpec, traits, + Undefined) from ...utils.filemanip import split_filename @@ -47,8 +48,8 @@ def no_nifty_package(cmd='reg_f3d'): class NiftyRegCommandInputSpec(CommandLineInputSpec): """Input Spec for niftyreg interfaces.""" # Set the number of omp thread to use - omp_core_val = traits.Int(desc='Number of openmp thread to use', - argstr='-omp %i') + omp_core_val = traits.Int(1, desc='Number of openmp thread to use', + argstr='-omp %i', usedefault=True) class NiftyRegCommand(CommandLine): @@ -58,7 +59,10 @@ class NiftyRegCommand(CommandLine): _suffix = '_nr' _min_version = '1.5.30' + input_spec = NiftyRegCommandInputSpec + def __init__(self, required_version=None, **inputs): + self.num_threads = 1 super(NiftyRegCommand, self).__init__(**inputs) self.required_version = required_version _version = self.get_version() @@ -73,6 +77,29 @@ class NiftyRegCommand(CommandLine): msg = 'The version of NiftyReg differs from the required' msg += '(%s != %s)' warn(msg % (_version, self.required_version)) + self.inputs.on_trait_change(self._omp_update, 'omp_core_val') + self.inputs.on_trait_change(self._environ_update, 'environ') + self._omp_update() + + def _omp_update(self): + if self.inputs.omp_core_val: + self.inputs.environ['OMP_NUM_THREADS'] = \ + str(self.inputs.omp_core_val) + self.num_threads = self.inputs.omp_core_val + else: + if 'OMP_NUM_THREADS' in self.inputs.environ: + del self.inputs.environ['OMP_NUM_THREADS'] + self.num_threads = 1 + + def _environ_update(self): + if self.inputs.environ: + if 'OMP_NUM_THREADS' in self.inputs.environ: + self.inputs.omp_core_val = \ + int(self.inputs.environ['OMP_NUM_THREADS']) + else: + self.inputs.omp_core_val = Undefined + else: + self.inputs.omp_core_val = Undefined def check_version(self): _version = self.get_version() |
this tries to keep |
@satra @byvernault I want to preface this by saying that I think that patch is fine as it stands. Two comments, though.
The default behavior in this patch is to set x = pe.Node(Interface(), name='x') x.inputs.omp_core_val = int(os.get_env('OMP_NUM_THREADS', 1)) Just want to make sure nobody's surprised. |
@effigies - i set the default for soon after this release we should generalize the thread pieces across interfaces and clearly indicate preference order. |
My intent was that if the user wants to increase the omp_core_val for this node, he can do it by using the options omp_core_val (it's an option in the niftyseg executables). If it's not set, then I want the interface to use OMP_NUM_THREADS. If Nothing is specify or set, then we use 1. As it is right now, it worked when I run the interface. The number of core used by the executables is 1 if OMP_NUM_THREADS not present in env variable, OMP_NUM_THREADS value if set and omp_core_val if the user use the options. With the changes from @satra, even if OMP_NUM_THREADS is set to 2 and the user doesn't specify omp_core_val, the executable is using 1 since we have the options -omp 1. It's the reason I was avoiding setting the omp_core_val to 1 as default. I will prefer to set it to OMP_NUM_THREADS by default and 1 of not set in the env. See example below: I am running this:
Where we have OMP_NUM_THREADS at 2 but reg_aladin used only 1 threads since -omp 1 was set. |
What I can do is all the changes from @satra but set the default value of omp_core_val like below:
WDYT? |
Codecov Report
@@ Coverage Diff @@ ## master #2005 +/- ## ========================================== - Coverage 72.19% 72.17% -0.03% ========================================== Files 1132 1130 -2 Lines 56988 56963 -25 Branches 8159 8156 -3 ========================================== - Hits 41144 41112 -32 - Misses 14560 14567 +7 Partials 1284 1284
Continue to review full report at Codecov.
|
I added the changes from @satra with my last message for the default value. Is that ok? I can edit again if needed. I also changed the NiftySegCommand to be a children class of NiftyFitCommand instead of NiftyRegCommand since there are no omp_core_val. Cheers, |
Dear Nipype Team,
In our previous release of the niftyreg interfaces, int the base interface, we added some line of code to set at least by default the number of thread use by niftyreg tools.
I just realized this morning that the code I used isn't correct since I am not checking if the key exists in the dictionary. I edited with @effigies advice from the issue #2004 .
Basically, I am setting the self.inputs.environ['OMP_NUM_THREADS'] to the environ variable if it exists, else to the num_threads if not found (default 1).
I ran a node using the interface RegAladin and it worked. I tested as well one interface for NiftySeg and it worked as well.
Let me know if you think I should change something from those few lines.
Kind Regards,