Skip to content

Commit

Permalink
Merge pull request #288 from populse/add_mrtrix
Browse files Browse the repository at this point in the history
add mrtrix in capsul
  • Loading branch information
manuegrx authored Aug 29, 2023
2 parents af3f393 + 5dc94d4 commit 27a82d4
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 26 deletions.
1 change: 1 addition & 0 deletions capsul/engine/module/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
'ants',
'fsl',
'matlab',
'mrtrix',
'spm']
97 changes: 97 additions & 0 deletions capsul/engine/module/mrtrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os
from capsul import engine
import six


def init_settings(capsul_engine):
with capsul_engine.settings as settings:
settings.ensure_module_fields('mrtrix',
[dict(name='directory',
type='string',
description='Directory where mrtrix is installed')
])

# init a single config
config = settings.config('mrtrix', 'global')
if not config:
settings.new_config('mrtrix', 'global',
{capsul_engine.settings.config_id_field:
'mrtrix'})


def check_notably_invalid_config(conf):
'''
Checks if the given module config is obviously invalid, for instance
if a mandatory path is not filled
Returns
-------
invalid: list
list of invalid config keys
'''
return getattr(conf, 'directory', None)


def activate_configurations():
'''
Activate the mrtrix module (set env variables) from the global
configurations, in order to use them via
:mod:`capsul.in_context.mrtrix` functions
'''
conf = engine.configurations.get('capsul.engine.module.mrtrix', {})
mrtrix_dir = conf.get('directory')
if mrtrix_dir:
os.environ['MRTRIXPATH'] = six.ensure_str(mrtrix_dir)
elif 'MRTRIXPATH' in os.environ:
del os.environ['MRTRIXPATH']


def edition_widget(engine, environment, config_id='mrtrix'):
''' Edition GUI for mrtrix config - see
:class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor`
'''
from soma.qt_gui.controller_widget import ScrollControllerWidget
from soma.controller import Controller
import types
import traits.api as traits

def validate_config(widget):
widget.update_controller()
controller = widget.controller_widget.controller
with widget.engine.settings as session:
conf = session.config(config_id, widget.environment)
values = {'config_id': config_id}
for k in ['directory']:
value = getattr(controller, k)
if value is traits.Undefined:
value = None
values[k] = value
if conf is None:
session.new_config(config_id, widget.environment, values)
else:
for k, value in values.items():
if k == 'config_id':
continue
setattr(conf, k, values[k])

controller = Controller()

controller.add_trait('directory',
traits.Directory(traits.Undefined,
desc='Directory where mrtrix is installed'))

conf = engine.settings.select_configurations(
environment, {'mrtrix': 'any'})
if conf:
fconf = conf.get('capsul.engine.module.mrtrix', {})
controller.directory = fconf.get('directory', traits.Undefined)

widget = ScrollControllerWidget(controller, live=True)
widget.engine = engine
widget.environment = environment
widget.accept = types.MethodType(validate_config, widget)

return widget
4 changes: 2 additions & 2 deletions capsul/engine/module/nipype.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def ensure_config_exists(engine):

def init_settings(capsul_engine):
with capsul_engine.settings as settings:
settings.ensure_module_fields('nipype',[])
settings.ensure_module_fields('nipype', [])
pass

ensure_config_exists(capsul_engine)
Expand All @@ -40,7 +40,7 @@ def activate_configurations():
from capsul.in_context import nipype

# activate optional dependencies first
for module in ('spm', 'fsl', 'freesurfer', 'afni', 'ants'):
for module in ('spm', 'fsl', 'freesurfer', 'afni', 'ants', 'mrtrix'):
module_name = Settings.module_name(module)
mod_conf = engine.configurations.get(module_name)
if mod_conf:
Expand Down
23 changes: 22 additions & 1 deletion capsul/engine/test/test_capsul_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ def test_engine_settings(self):
settings.new_config('ants', 'global', {'directory': '/there',
cif: '235'})

# Create a global mrtrix configuration
config = settings.config('mrtrix', 'global')
if config:
settings.remove_config('mrtrix', 'global',
getattr(config, cif))
settings.new_config('mrtrix', 'global', {'directory': '/there',
cif: '400'})

# Create two global SPM configurations
settings.new_config('spm', 'global', {'version': '8',
'standalone': True,
Expand All @@ -202,7 +210,8 @@ def test_engine_settings(self):
'capsul.engine.module.matlab': 'ALL',
'capsul.engine.module.spm': 'ALL',
'capsul.engine.module.afni': 'ALL',
'capsul.engine.module.ants': 'ALL'}},
'capsul.engine.module.ants': 'ALL',
'capsul.engine.module.mrtrix': 'ALL'}},
'capsul.engine.module.fsl': {'config_environment': 'global',
'directory': '/there',
cif: '5'},
Expand All @@ -212,6 +221,9 @@ def test_engine_settings(self):
'capsul.engine.module.ants': {
'config_environment': 'global', 'directory': '/there',
cif: '235'},
'capsul.engine.module.mrtrix': {
'config_environment': 'global', 'directory': '/there',
cif: '400'},
'capsul.engine.module.spm': {'config_environment': 'my_machine',
'version': '20',
'standalone': True,
Expand Down Expand Up @@ -246,6 +258,15 @@ def test_engine_settings(self):
'capsul_engine':
{'uses': {'capsul.engine.module.ants': 'any'}}})

self.assertEqual(
self.ce.settings.select_configurations('global',
uses={'mrtrix': 'any'}),
{'capsul.engine.module.mrtrix':
{'config_environment': 'global', 'directory': '/there',
cif: '400'},
'capsul_engine':
{'uses': {'capsul.engine.module.mrtrix': 'any'}}})

self.assertEqual(
self.ce.settings.select_configurations('global',
uses={'spm': 'any'}),
Expand Down
127 changes: 127 additions & 0 deletions capsul/in_context/mrtrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
'''
Specific subprocess-like functions to call mrtrix taking into account
configuration stored in ExecutionContext. To functions and class in
this module it is mandatory to activate an ExecutionContext (using a
with statement). For instance::
from capsul.engine import capsul_engine
from capsul.in_context.mrtrix import mrtrix_check_call
ce = capsul_engine()
with ce:
mrtrix_check_call(['mrinfo', '/somewhere/myimage.nii'])
For calling mrtrix command with this module, the first argument of
command line must be the mrtrix executable without any path.
The appropriate path is added from the configuration
of the ExecutionContext.
'''

from __future__ import absolute_import

import os
import os.path as osp
import soma.subprocess
from soma.utils.env import parse_env_lines
import six

mrtrix_runtime_env = None


def mrtrix_command_with_environment(command, use_runtime_env=True):
'''
Given an mrtrix command where first element is a command name without
any path. Returns the appropriate command to call taking into account
the mrtrix configuration stored in the
activated ExecutionContext.
'''

if use_runtime_env and mrtrix_runtime_env:
c0 = list(osp.split(command[0]))
c0 = osp.join(*c0)
cmd = [c0] + command[1:]
return cmd

mrtrix_dir = os.environ.get('MRTRIXPATH')
if mrtrix_dir:
shell = os.environ.get('SHELL', '/bin/sh')
if shell.endswith('csh'):
cmd = [shell, '-c',
'setenv MRTRIXPATH "{0}"; setenv PATH "{0}:$PATH";exec {1} '.format(
mrtrix_dir, command[0]) + \
' '.join("'%s'" % i.replace("'", "\\'") for i in command[1:])]
else:
cmd = [shell, '-c',
'export MRTRIXPATH="{0}"; export PATH="{0}:$PATH"; exec {1} '.format(
mrtrix_dir, command[0]) + \
' '.join("'%s'" % i.replace("'", "\\'") for i in command[1:])]

return cmd


def mrtrix_env():
'''
get mrtrix env variables
process
'''
global mrtrix_runtime_env

if mrtrix_runtime_env is not None:
return mrtrix_runtime_env

mrtrix_dir = os.environ.get('MRTRIXPATH')
kwargs = {}

cmd = mrtrix_command_with_environment(['env'], use_runtime_env=False)
new_env = soma.subprocess.check_output(cmd, **kwargs).decode(
'utf-8').strip()
new_env = parse_env_lines(new_env)
env = {}
for l in new_env:
name, val = l.strip().split('=', 1)
name = six.ensure_str(name)
val = six.ensure_str(val)
if name not in ('_', 'SHLVL') and (name not in os.environ
or os.environ[name] != val):
env[name] = val

# add PATH
if mrtrix_dir:
env['PATH'] = os.pathsep.join([mrtrix_dir, os.environ.get('PATH', '')])
# cache dict
mrtrix_runtime_env = env
return env


class MrtrixPopen(soma.subprocess.Popen):
'''
Equivalent to Python subprocess.Popen for mrtrix commands
'''
def __init__(self, command, **kwargs):
cmd = mrtrix_command_with_environment(command)
super(MrtrixPopen, self).__init__(cmd, **kwargs)


def mrtrix_call(command, **kwargs):
'''
Equivalent to Python subprocess.call for mrtrix commands
'''
cmd = mrtrix_command_with_environment(command)
return soma.subprocess.call(cmd, **kwargs)


def mrtrix_check_call(command, **kwargs):
'''
Equivalent to Python subprocess.check_call for mrtrix commands
'''
cmd = mrtrix_command_with_environment(command)
return soma.subprocess.check_call(cmd, **kwargs)


def mrtrix_check_output(command, **kwargs):
'''
Equivalent to Python subprocess.check_output for mrtrix commands
'''
cmd = mrtrix_command_with_environment(command)
return soma.subprocess.check_output(cmd, **kwargs)
18 changes: 17 additions & 1 deletion capsul/in_context/nipype.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
import os
import os.path as osp


def configure_all():
'''
Configure nipye for all known software interfaces their configuration
is present in os.environ. This environment must have been set by the
CapsulEngine mechanism.
'''
#print('!!!')

configure_matlab()
configure_spm()
configure_fsl()
configure_freesurfer()
configure_afni()
configure_ants()
configure_mrtrix()


def configure_spm():
Expand Down Expand Up @@ -110,6 +112,7 @@ def configure_afni():
for var, value in env.items():
os.environ[var] = value


def configure_ants():
'''
Configure ANTS for nipype
Expand All @@ -121,3 +124,16 @@ def configure_ants():
env = antsrun.ants_env()
for var, value in env.items():
os.environ[var] = value


def configure_mrtrix():
'''
Configure mrtrix for nipype
'''
from capsul import engine
conf = engine.configurations.get('capsul.engine.module.mrtrix')
if conf:
from capsul.in_context import mrtrix as mrtrixrun
env = mrtrixrun.mrtrix_env()
for var, value in env.items():
os.environ[var] = value
Loading

0 comments on commit 27a82d4

Please sign in to comment.