Skip to content
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

add mrtrix in capsul #288

Merged
merged 3 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
25 changes: 24 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,15 @@ def test_engine_settings(self):
settings.new_config('ants', 'global', {'directory': '/there',
cif: '235'})

# Create a global mrtrix configuration
# FIXME : mrtrix cif ?
config = settings.config('mrtrix', 'global')
if config:
settings.remove_config('mrtrix', 'global',
getattr(config, cif))
settings.new_config('mrtrix', 'global', {'directory': '/there',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that mrtrix is supposed to have a unique cif, not the same as ant. Any value would be possible here as long as it is unique among all other values.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a unique hard-coded CIF will just forbid to have several installs / versions of Mrtrix configured in Capsul, but it will probably be OK.

cif: '235'})

# Create two global SPM configurations
settings.new_config('spm', 'global', {'version': '8',
'standalone': True,
Expand All @@ -202,7 +211,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 +222,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: '235'}, # FIXME : mrtrix cif ?
'capsul.engine.module.spm': {'config_environment': 'my_machine',
'version': '20',
'standalone': True,
Expand Down Expand Up @@ -246,6 +259,16 @@ def test_engine_settings(self):
'capsul_engine':
{'uses': {'capsul.engine.module.ants': 'any'}}})

# FIXME : mrtrix cif ?
self.assertEqual(
self.ce.settings.select_configurations('global',
uses={'mrtrix': 'any'}),
{'capsul.engine.module.mrtrix':
{'config_environment': 'global', 'directory': '/there',
cif: '235'},
'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
Loading