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

method to allow adding an additional directory to find components #50

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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
4 changes: 2 additions & 2 deletions mcstasscript/configuration.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
other:
characters_per_line: 85
paths:
mcrun_path: /Applications/McStas-2.7.1.app/Contents/Resources/mcstas/2.7.1/bin/
mcstas_path: /Applications/McStas-2.7.1.app/Contents/Resources/mcstas/2.7.1/
mcrun_path: /usr/mcstas/3.3//bin/
mcstas_path: /usr/mcstas/3.3/
mcxtrace_path: /Applications/McXtrace-1.5.app/Contents/Resources/mcxtrace/1.5/
mxrun_path: /Applications/McXtrace-1.5.app/Contents/Resources/mcxtrace/1.5/bin/
49 changes: 49 additions & 0 deletions mcstasscript/helper/component_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,55 @@ def __init__(self, mcstas_path, input_path="."):

print("These definitions will be used instead of the installed "
+ "versions.")
def add_custom_component_dir(self, input_path=".", category="custom"):
"""
Method to add further directories containing custom components
"""
# Will overwrite McStas components with definitions in input_folder
current_directory = os.getcwd()

# Set up absolute input_path
if os.path.isabs(input_path):
input_directory = input_path
else:
if input_path == ".":
# Default case, avoid having /./ in absolute path
input_directory = current_directory
else:
input_directory = os.path.join(current_directory, input_path)

if not os.path.isdir(input_directory):
print("input_path: ", input_directory)
raise ValueError("Can't find given input_path," + " directory must exist.")
"""
If components are present both in the McStas install and the
work directory, the version in the work directory is used. The user
is informed of this behavior when the instrument object is created.
"""
overwritten_components = []
for file in os.listdir(input_directory):
if file.endswith(".comp"):
abs_path = os.path.join(input_directory, file)
component_name = os.path.split(abs_path)[1].split(".")[-2]

if component_name in self.component_path:
overwritten_components.append(file)

self.component_path[component_name] = abs_path
self.component_category[component_name] = category

# Report components found in the work directory and install to the user
if len(overwritten_components) > 0:
print(
"The following components are found in the work_directory"
+ " / input_path:"
)
for name in overwritten_components:
print(" ", name)

print(
"These definitions will be used instead of the installed " + "versions."
)

def show_categories(self):
"""
Expand Down
18 changes: 14 additions & 4 deletions mcstasscript/helper/managed_mcrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,19 @@ def __init__(self, instr_name, **kwargs):
If True, automatically appends output_path to make it unique
force_compile : bool, default True
If True, forces compile. If False no new instrument is written
run_folder : str
run_path : str
Path to folder in which to run McStas
openacc : bool, default False
If True, adds the --openacc flag to mcrun call
NeXus : bool, default False
If True, adds the --format=NeXus to mcrun call

component_dirs: list
Sets non standard paths to search for component definitions

"""

self.name_of_instrumentfile = instr_name

self.component_dirs=[]
self.data_folder_name = ""
self.ncount = int(1E6)
self.mpi = None
Expand All @@ -110,6 +112,9 @@ def __init__(self, instr_name, **kwargs):
self.seed = None
self.suppress_output = False

if "component_dirs" in kwargs:
self.component_dirs = kwargs["component_dirs"]

# executable_path always in kwargs
if "executable_path" in kwargs:
self.executable_path = kwargs["executable_path"]
Expand Down Expand Up @@ -279,10 +284,15 @@ def run_simulation(self):
self.executable)
mcrun_full_path = '"' + mcrun_full_path + '"' # Path in quotes to allow spaces

add_component_dirs=""
for d in self.component_dirs:
add_component_dirs="-I "+d

# Run the mcrun command on the system
full_command = (mcrun_full_path + " "
+ option_string + " "
+ self.custom_flags + " "
+ add_component_dirs + " "
+ self.name_of_instrumentfile
+ parameter_string)

Expand Down Expand Up @@ -842,4 +852,4 @@ def findall(s, p):
i = s.lower().find(p)
while i != -1:
yield i
i = s.lower().find(p, i+1)
i = s.lower().find(p, i+1)
2 changes: 1 addition & 1 deletion mcstasscript/instrument_diagnostics/beam_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,5 +413,5 @@ def plot(self):
print("No data to plot! Use the run method to generate data.")

overview = PlotOverview(self.event_plotters, self.views)
overview.plot_all()
return overview.plot_all()

3 changes: 2 additions & 1 deletion mcstasscript/instrument_diagnostics/plot_overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def plot_all(self, figsize=None, same_scale=True):
major_label_set = True

fig.tight_layout()
return fig

def set_same_scale(self):
"""
Expand Down Expand Up @@ -90,4 +91,4 @@ def set_same_scale(self):

view.set_axis1_limits(np.nanmin(axis1_mins), np.nanmax(axis1_maxs))
if view.axis2 is not None:
view.set_axis2_limits(np.nanmin(axis2_mins), np.nanmax(axis2_maxs))
view.set_axis2_limits(np.nanmin(axis2_mins), np.nanmax(axis2_maxs))
4 changes: 3 additions & 1 deletion mcstasscript/instrument_diagram/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,6 @@ def hover(event):
fig.canvas.mpl_connect("motion_notify_event", hover)

# Show the figure
plt.show()
plt.show()

return fig
3 changes: 2 additions & 1 deletion mcstasscript/instrument_diagram/make_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ def instrument_diagram(instrument, analysis=False, variable=None, limits=None):
limits=limits)

# Plot diagram
canvas.plot()
return canvas.plot()

92 changes: 76 additions & 16 deletions mcstasscript/interface/instr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import subprocess
import copy
import warnings
import io
import hashlib

from IPython.display import IFrame

Expand Down Expand Up @@ -149,6 +151,9 @@ class McCode_instr(BaseCalculator):
component_help(name)
Shows help on component of given name

add_component_dir(path)
Add path to the search dir for components

add_component(instance_name, component_name, **kwargs)
Add a component to the instrument file

Expand Down Expand Up @@ -341,6 +346,8 @@ def __init__(self, name, parameters=None, author=None,

self._run_settings = {} # Settings for running simulation

self._run_settings["component_dirs"] = []

# Sets max_line_length and adds paths to run_settings
self._read_calibration()

Expand Down Expand Up @@ -1193,6 +1200,19 @@ def _create_component_instance(self, name, component_name, **kwargs):
return self.component_class_lib[component_name](name, component_name,
**kwargs)

def add_component_dir(self, path=".", category="custom"):
"""
Method for adding a directory to the list of directories where to search for components"
"""
self.component_reader.add_custom_component_dir(path, category)

current_directory = os.getcwd()

if not os.path.isabs(path):
path = os.path.join(current_directory, path)

self._run_settings["component_dirs"].append(path)

def add_component(self, name, component_name=None, *, before=None,
after=None, AT=None, AT_RELATIVE=None, ROTATED=None,
ROTATED_RELATIVE=None, RELATIVE=None, WHEN=None,
Expand Down Expand Up @@ -2027,7 +2047,15 @@ def show_instrument_file(self, line_numbers=False):
full_line = line_number + line
print(full_line.replace("\n", ""))

def write_full_instrument(self):

def __hash__(self):
""" Define a hashing specifically for this calculator
The hash method in the BaseCalculator would pack also the internals of this class and it changes when calling the backengine
"""
contents = self.write_full_instrument(False)
return int.from_bytes(hashlib.sha256(contents.encode()).digest(), "big")

def write_full_instrument(self, do_write=True) -> str:
"""
Method for writing full instrument file to disk

Expand All @@ -2041,8 +2069,12 @@ def write_full_instrument(self):
self.check_for_errors()

# Create file identifier
fo = open(os.path.join(self.input_path, self.name + ".instr"), "w")

run_path = self._run_settings["run_path"]
fo = None
if do_write:
fo = open(os.path.join(run_path, self.name + ".instr"), "w")
else:
fo = io.StringIO()
# Write quick doc start
fo.write("/" + 80*"*" + "\n")
fo.write("* \n")
Expand All @@ -2062,7 +2094,8 @@ def write_full_instrument(self):
fo.write("* %Identification\n") # Could allow the user to insert this
fo.write("* Written by: %s\n" % self.author)
t_format = "%H:%M:%S on %B %d, %Y"
fo.write("* Date: %s\n" % datetime.datetime.now().strftime(t_format))
if do_write:
fo.write("* Date: %s\n" % datetime.datetime.now().strftime(t_format))
fo.write("* Origin: %s\n" % self.origin)
fo.write("* %INSTRUMENT_SITE: Generated_instruments\n")
fo.write("* \n")
Expand All @@ -2075,14 +2108,18 @@ def write_full_instrument(self):
fo.write("\n")
fo.write("DEFINE INSTRUMENT %s (" % self.name)
fo.write("\n")
# Insert parameters
parameter_list = list(self.parameters)
end_chars = [", "]*len(parameter_list)
if len(end_chars) >= 1:
end_chars[-1] = " "
for variable, end_char in zip(parameter_list, end_chars):
write_parameter(fo, variable, end_char)
fo.write(")\n")

# remove parameters when calculating hash because don't want
# variations in the parameter values to trigger a new hash
if do_write:
# Insert parameters
parameter_list = list(self.parameters)
end_chars = [", "]*len(parameter_list)
if len(end_chars) >= 1:
end_chars[-1] = " "
for variable, end_char in zip(parameter_list, end_chars):
write_parameter(fo, variable, end_char)
fo.write(")\n")
if self.dependency_statement != "":
fo.write("DEPENDENCY " + str(self.dependency_statement) + "\n")
self.search_statement_list.write(fo)
Expand Down Expand Up @@ -2133,7 +2170,11 @@ def write_full_instrument(self):
# End instrument file
fo.write("\nEND\n")

instrument_file_as_string = ""
if do_write is False:
instrument_file_as_string = fo.getvalue()
fo.close()
return instrument_file_as_string

def get_component_subset_index_range(self, start_ref=None, end_ref=None):
"""
Expand Down Expand Up @@ -2275,7 +2316,7 @@ def settings(self, ncount=None, mpi="not_set", seed=None,
increment_folder_name=None, custom_flags=None,
executable=None, executable_path=None,
suppress_output=None, gravity=None, checks=None,
openacc=None, NeXus=None):
openacc=None, NeXus=None, component_dirs=None, run_path=None):
"""
Sets settings for McStas run performed with backengine

Expand Down Expand Up @@ -2312,6 +2353,10 @@ def settings(self, ncount=None, mpi="not_set", seed=None,
If True, adds --openacc to mcrun call
NeXus : bool
If True, adds --format=NeXus to mcrun call
component_dirs : list
Additional directories where to find components (use absolute paths)
run_path : str
Change the directory where the code is compiled and run
"""

settings = {}
Expand Down Expand Up @@ -2373,6 +2418,15 @@ def settings(self, ncount=None, mpi="not_set", seed=None,
if NeXus is not None:
settings["NeXus"] = bool(NeXus)

if component_dirs is not None:
if len(component_dirs) > 0:
settings["component_dirs"].append( component_dirs)
else:
component_dirs = []

if run_path is not None:
settings["run_path"] = os.path.abspath(run_path)

self._run_settings.update(settings)

def settings_string(self):
Expand Down Expand Up @@ -2467,7 +2521,11 @@ def backengine(self):
self.__add_input_to_mcpl()

instrument_path = os.path.join(self.input_path, self.name + ".instr")

run_path = self._run_settings["run_path"]
instrument_path = os.path.join(run_path, self.name + ".instr")
if not os.path.exists(instrument_path) or self._run_settings["force_compile"]:
print("Instrument file not found: ", instrument_path, os.path.exists(instrument_path), self._run_settings["force_compile"])
self.write_full_instrument()

parameters = {}
Expand All @@ -2483,7 +2541,7 @@ def backengine(self):
options["output_path"] = self.output_path

# Set up the simulation
simulation = ManagedMcrun(self.name + ".instr", **options)
simulation = ManagedMcrun(instrument_path, **options) #self.name + ".instr", **options)

# Run the simulation and return data
simulation.run_simulation()
Expand Down Expand Up @@ -2728,15 +2786,17 @@ def show_diagram(self, analysis=False, variable=None, limits=None):
if variable is not None:
analysis = True

instrument_diagram(self, analysis=analysis, variable=variable, limits=limits)
fig = instrument_diagram(self, analysis=analysis, variable=variable, limits=limits)

if self._run_settings["checks"]:
self.check_for_errors()

return fig

def show_analysis(self, variable=None):
beam_diag = IntensityDiagnostics(self)
beam_diag.run_general(variable=variable)
beam_diag.plot()
return beam_diag.plot()

def saveH5(self, filename: str, openpmd: bool = True):
"""
Expand Down
Loading