Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 0 additions & 7 deletions mcstasscript/configuration.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions mcstasscript/helper/managed_mcrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ def run_simulation(self, **kwargs):
universal_newlines=True,
cwd=self.run_path)

if process.returncode != 0:
error_message = f"Executing\n\t{full_command}\nfailed with error code {process.returncode}."
error_message += f"\nCommand output:\n{process.stdout}"
raise RuntimeError(error_message)

if "suppress_output" in kwargs:
if kwargs["suppress_output"] is False:
print_sim_output(process.stdout)
Expand Down
11 changes: 8 additions & 3 deletions mcstasscript/integration_tests/test_complex_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,15 @@ def setup_complex_instrument():

Instr.add_component("done", "Arm", RELATIVE="after_guide")

from packaging.version import Version
bin_attr = 'nx' if Instr.executable_version < Version('3.0.0') else 'nbins'

PSD1 = Instr.add_component("PSD_1D_1", "PSDlin_monitor")
PSD1.set_AT([0, 0, 0.2], RELATIVE="after_guide")
PSD1.xwidth = 0.1
PSD1.nx = 100
if not hasattr(PSD1, bin_attr):
raise RuntimeError(f"Expected {PSD1} to have attribute {bin_attr}")
setattr(PSD1, bin_attr, 100)
PSD1.yheight = 0.03
PSD1.filename = "\"PSD1.dat\""
PSD1.restore_neutron = 1
Expand All @@ -113,7 +118,7 @@ def setup_complex_instrument():
PSD2 = Instr.add_component("PSD_1D_2", "PSDlin_monitor")
PSD2.set_AT([0, 0, 0.2], RELATIVE="after_guide")
PSD2.xwidth = 0.1
PSD2.nx = 100
setattr(PSD2, bin_attr, 100)
PSD2.yheight = 0.03
PSD2.filename = "\"PSD2.dat\""
PSD2.restore_neutron = 1
Expand All @@ -122,7 +127,7 @@ def setup_complex_instrument():
PSD = Instr.add_component("PSD_1D", "PSDlin_monitor")
PSD.set_AT([0, 0, 0.2], RELATIVE="after_guide")
PSD.xwidth = 0.1
PSD.nx = 100
setattr(PSD, bin_attr, 100)
PSD.yheight = 0.03
PSD.filename = "\"PSD_all.dat\""
PSD.restore_neutron = 1
Expand Down
25 changes: 23 additions & 2 deletions mcstasscript/integration_tests/test_simple_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
def setup_simple_instrument():
Instr = instr.McStas_instr("integration_test_simple")

from packaging.version import Version
bin_attr = 'nx' if Instr.executable_version < Version('3.0.0') else 'nbins'

source = Instr.add_component("source", "Source_div")

source.xwidth = 0.03
Expand All @@ -22,7 +25,9 @@ def setup_simple_instrument():

PSD.set_AT([0, 0, 1], RELATIVE="source")
PSD.xwidth = 0.1
PSD.nx = 100
if not hasattr(PSD, bin_attr):
raise RuntimeError(f"Expected {PSD} to have attribute {bin_attr}")
setattr(PSD, bin_attr, 100)
PSD.yheight = 0.03
PSD.filename = "\"PSD.dat\""
PSD.restore_neutron = 1
Expand All @@ -37,6 +42,12 @@ def setup_simple_instrument_input_path():
Instr = instr.McStas_instr("integration_test_simple_input",
input_path=input_path)

from packaging.version import Version
if Instr.executable_version > Version('3.0.0'):
import warnings
warnings.warn("The version of McStas is not 2.x; this test would have failed if it had not been skipped")
return Instr

source = Instr.add_component("source", "Source_div")

source.xwidth = 0.03
Expand All @@ -62,6 +73,9 @@ def setup_simple_instrument_input_path():
def setup_simple_slit_instrument():
Instr = instr.McStas_instr("integration_test_simple")

from packaging.version import Version
bin_attr = 'nx' if Instr.executable_version < Version('3.0.0') else 'nbins'

source = Instr.add_component("source", "Source_div")
source.xwidth = 0.1
source.yheight = 0.01
Expand All @@ -81,7 +95,9 @@ def setup_simple_slit_instrument():
PSD = Instr.add_component("PSD_1D", "PSDlin_monitor")
PSD.set_AT([0, 0, 1], RELATIVE="source")
PSD.xwidth = 0.1
PSD.nx = 100
if not hasattr(PSD, bin_attr):
raise RuntimeError(f"Expected {PSD} to have attribute {bin_attr}")
setattr(PSD, bin_attr, 100)
PSD.yheight = 0.03
PSD.filename = "\"PSD.dat\""
PSD.restore_neutron = 1
Expand Down Expand Up @@ -137,6 +153,11 @@ def test_simple_instrument_input(self, mock_stdout):

Instr = setup_simple_instrument_input_path()

from packaging.version import Version
if Instr.executable_version > Version('3.0.0'):
import warnings
warnings.warn("The version of McStas is not 2.x; this test would have failed if it had not been skipped")

foldername = "integration_test_simple_input"
data = Instr.run_full_instrument(foldername=foldername,
ncount=1E6, mpi=1,
Expand Down
54 changes: 41 additions & 13 deletions mcstasscript/interface/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,19 +213,47 @@ def _create_new_config_file(self):
"""
Writes a default configuration file to the package root directory
"""

run = "/Applications/McStas-2.5.app/Contents/Resources/mcstas/2.5/bin/"
mcstas = "/Applications/McStas-2.5.app/Contents/Resources/mcstas/2.5/"

mxrun = "/Applications/McXtrace-1.5.app" \
+ "/Contents/Resources/mcxtrace/1.5/mxrun"
mcxtrace = "/Applications/McXtrace-1.5.app" \
+ "/Contents/Resources/mcxtrace/1.5/"

default_paths = {"mcrun_path": run,
"mcstas_path": mcstas,
"mxrun_path": mxrun,
"mcxtrace_path": mcxtrace}
items = ('McStas', '2.7.1'), ('McXtrace', '1.5')
def make_mac_defaults():
base = [f'/Applications/{n}-{v}.app/Contents/Resources/{n.lower()}/{v}/' for n, v in items]
defs = {f'{n}run_path': b+'bin/' for n, b in zip(('mc', 'mx'), base)}
defs.update({f'{n}_path': b for n, b in zip(('mcstas', 'mcxtrace'), base)})
return defs

def make_unix_defaults():
defs = {f'{n.lower()}_path': f'/usr/share/{n.lower()}/{v}' for n, v in items}
defs.update({f'{n}_path': '/usr/bin/' for n in ('mcrun', 'mxrun')})
return defs

def make_win_defaults():
defs = {f'{k}run_path': f'\\{n.lower()}-{v}\\bin\\' for k, (n, v) in zip(('mc', 'mx'), items)}
defs.update({f'{n.lower()}_path': f'\\{n.lower()}-{v}\\lib\\' for n, v in items})
return defs

import platform
default_paths = {}
if 'linux' in platform.system().lower():
default_paths = make_unix_defaults()
elif 'darwin' in platform.system().lower():
default_paths = make_mac_defaults()
elif 'win' in platform.system().lower():
default_paths = make_win_defaults()

# check if McStas or McXtrace are *on* the system path, then default to those:
import shutil
from pathlib import Path
if shutil.which('mcstas') is not None:
binary_path = Path(shutil.which('mcstas'))
if binary_path.is_symlink():
binary_path = binary_path.readlink()
default_paths['mcrun_path'] = f"{binary_path.parent}"
default_paths['mcstas_path'] = f"{binary_path.parent.parent}"
if shutil.which('mcxtrace') is not None:
binary_path = Path(shutil.which('mcxtrace'))
if binary_path.is_symlink():
binary_path = binary_path.readlink()
default_paths['mxrun_path'] = f'{binary_path.parent}'
default_paths['mcxtrace_path'] = f'{binary_path.parent.parent}'

default_other = {"characters_per_line": 85}

Expand Down
36 changes: 20 additions & 16 deletions mcstasscript/interface/instr.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,22 @@ def output_path(self) -> str:
def output_path(self, value: str) -> None:
self.calculator_base_dir = value

@property
def executable_version(self):
from subprocess import check_output
from pathlib import Path
from os import access, X_OK
from packaging.version import parse
if 'executable_path' not in self._run_settings or 'executable' not in self._run_settings:
return '0.0.0'
torun = Path(self._run_settings['executable_path']).joinpath(self._run_settings['executable'])
if not torun.exists() or not access(torun, X_OK):
return '0.0.0'
version_info = check_output([torun, '--version']).decode('utf-8')
version_string = [x for x in version_info.split('\n') if len(x)][-1]
return parse(version_string)


def init_parameters(self):
"""
Create empty ParameterContainer for new instrument
Expand Down Expand Up @@ -2351,14 +2367,8 @@ def __init__(self, name, **kwargs):
super().__init__(name, executable=executable, **kwargs)

def _read_calibration(self):
this_dir = os.path.dirname(os.path.abspath(__file__))
configuration_file_name = os.path.join(this_dir, "..",
"configuration.yaml")
if not os.path.isfile(configuration_file_name):
raise NameError("Could not find configuration file!")
with open(configuration_file_name, 'r') as ymlfile:
config = yaml.safe_load(ymlfile)

from .. import Configurator
config = Configurator()._read_yaml()
if type(config) is dict:
self._run_settings["executable_path"] = config["paths"]["mcrun_path"]
self._run_settings["package_path"] = config["paths"]["mcstas_path"]
Expand Down Expand Up @@ -2574,14 +2584,8 @@ def __init__(self, name, **kwargs):
super().__init__(name, executable=executable, **kwargs)

def _read_calibration(self):
this_dir = os.path.dirname(os.path.abspath(__file__))
configuration_file_name = os.path.join(this_dir, "..",
"configuration.yaml")
if not os.path.isfile(configuration_file_name):
raise NameError("Could not find configuration file!")
with open(configuration_file_name, 'r') as ymlfile:
config = yaml.safe_load(ymlfile)

from .. import Configurator
config = Configurator()._read_yaml()
if type(config) is dict:
self._run_settings["executable_path"] = config["paths"]["mxrun_path"]
self._run_settings["package_path"] = config["paths"]["mcxtrace_path"]
Expand Down
32 changes: 0 additions & 32 deletions mcstasscript/tests/test_Configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,6 @@ def test_simple_initialize(self):
if os.path.isfile(expected_file):
os.remove(expected_file)

def test_default_config(self):
"""
This tests confirms the content of the default configuration file
"""

test_name = "test_configuration"
expected_file = setup_expected_file(test_name)

# check the file did not exist before testing
self.assertFalse(os.path.isfile(expected_file))

my_configurator = Configurator(test_name)

default_config = my_configurator._read_yaml()

run = "/Applications/McStas-2.5.app/Contents/Resources/mcstas/2.5/bin/"
mcstas = "/Applications/McStas-2.5.app/Contents/Resources/mcstas/2.5/"
mxrun = "/Applications/McXtrace-1.5.app" \
+ "/Contents/Resources/mcxtrace/1.5/mxrun"
mcxtrace = "/Applications/McXtrace-1.5.app" \
+ "/Contents/Resources/mcxtrace/1.5/"

self.assertEqual(default_config["paths"]["mcrun_path"], run)
self.assertEqual(default_config["paths"]["mcstas_path"], mcstas)
self.assertEqual(default_config["paths"]["mxrun_path"], mxrun)
self.assertEqual(default_config["paths"]["mcxtrace_path"], mcxtrace)
self.assertEqual(default_config["other"]["characters_per_line"], 85)

# remove the testing configuration file
if os.path.isfile(expected_file):
os.remove(expected_file)

def test_yaml_write(self):
"""
This test checks that writing to the configuration file works
Expand Down
25 changes: 25 additions & 0 deletions mcstasscript/tests/test_Instr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,11 @@ def test_x_ray_run_full_instrument_basic(self, mock_sub, mock_stdout):
existing file so no error is thrown.
"""

# The mocked process needs to set a return-code of zero to avoid the process error checking
process_mock = unittest.mock.Mock()
process_mock.configure_mock(returncode=0)
mock_sub.return_value = process_mock

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
executable_path = os.path.join(THIS_DIR, "dummy_mcstas")

Expand Down Expand Up @@ -1768,6 +1773,11 @@ def test_run_backengine_basic(self, mock_sub, mock_stdout):
existing file so no error is thrown.
"""

# The mocked process needs to set a return-code of zero to avoid the process error checking
process_mock = unittest.mock.Mock()
process_mock.configure_mock(returncode=0)
mock_sub.return_value = process_mock

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
executable_path = os.path.join(THIS_DIR, "dummy_mcstas")

Expand Down Expand Up @@ -1814,6 +1824,11 @@ def test_run_full_instrument_complex(self, mock_sub, mock_stdout):
existing file so no error is thrown.
"""

# The mocked process needs to set a return-code of zero to avoid the process error checking
process_mock = unittest.mock.Mock()
process_mock.configure_mock(returncode=0)
mock_sub.return_value = process_mock

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
executable_path = os.path.join(THIS_DIR, "dummy_mcstas")

Expand Down Expand Up @@ -1867,6 +1882,11 @@ def test_run_full_instrument_overwrite_default(self, mock_sub,
parameters in run_full_instrument.
"""

# The mocked process needs to set a return-code of zero to avoid the process error checking
process_mock = unittest.mock.Mock()
process_mock.configure_mock(returncode=0)
mock_sub.return_value = process_mock

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
executable_path = os.path.join(THIS_DIR, "dummy_mcstas")

Expand Down Expand Up @@ -1921,6 +1941,11 @@ def test_run_full_instrument_x_ray_basic(self, mock_sub, mock_stdout):
existing file so no error is thrown.
"""

# The mocked process needs to set a return-code of zero to avoid the process error checking
process_mock = unittest.mock.Mock()
process_mock.configure_mock(returncode=0)
mock_sub.return_value = process_mock

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
executable_path = os.path.join(THIS_DIR, "dummy_mcstas")

Expand Down
Loading