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
141 changes: 108 additions & 33 deletions tools/Python/mccodelib/mcplotloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ def walkfunc(arg, dirname, fnames):
dirsignature = (dirname, mnames)
for f in fnames:
# NOTE: this will attempt to load all files except for mccode.sim
if f not in mnames and f != 'mccode.sim' and f != 'mcstas.sim':
if f not in mnames and f != 'mccode.sim':
mnames.append(f)
arg.append(dirsignature)

Expand All @@ -512,16 +512,35 @@ def walkfunc(arg, dirname, fnames):
walkfunc(subdirtuple, root, files)
del subdirtuple[0] # remove root dir
subdirs = [t[0] for t in subdirtuple]
# get the right order of subdirs by recreating them a little bit
subdirs = [join(dirname(subdirs[i]), str(i)) for i in range(len(subdirs))] # sortalpha(subdirs)
# Sort by the REAL, actual numeric folder name (e.g. "0", "1", "3",
# "4" - numerically, not alphabetically, since alphabetical sort would
# put "10" before "2") - rather than the previous approach of
# renaming every subdir BY ITS POSITION in os.walk()'s traversal order
# (`join(dirname(subdirs[i]), str(i))`). That renaming assumed
# scan-step subfolders are always named consecutively with no gaps,
# which no longer holds now that a failed scan step's subfolder is
# deliberately never created (mcrun's Scanner.run()/Scanner_split now
# tolerate individual failed points rather than aborting the whole
# scan - see tools/Python/mcrun/optimisation.py). A gap used to
# silently rename every subfolder from that point on to a DIFFERENT,
# wrong path, misattributing each remaining step's data to the wrong
# index - surfacing as anything from wrong data to the "list index
# out of range" this caused (a later step's own mccode.sim declaring
# fewer monitors than the one it got misaligned with expected).
try:
subdirs = sorted(subdirs, key=lambda p: int(basename(p)))
except ValueError:
# Subfolder names aren't purely numeric for some reason - fall
# back to a plain alphabetical ordering rather than crashing
# outright (matches this function's previous fallback comment,
# "sortalpha(subdirs)", which was never actually reachable before).
subdirs = sorted(subdirs)

# get the monitor ordering right by snooping the ' filename:' labels out of the scan point file 0/mccode.sim
def get_subdir_monitors(subdir):
mons = []
if exists(join(subdir, 'mccode.sim')):
indexfile='mccode.sim'
elif exists(join(subdir, 'mccode.sim')):
indexfile='mcstas.sim'
else:
return

Expand All @@ -544,7 +563,30 @@ def get_subdir_monitors(subdir):

monitors_by_subdir = []
for s in subdirs:
monitors_by_subdir.append(get_subdir_monitors(s))
mons = get_subdir_monitors(s)
if mons:
monitors_by_subdir.append(mons)
else:
# A discovered subfolder without usable monitor data - either
# no mccode.sim at all (get_subdir_monitors() returns None),
# or a mccode.sim that exists but has zero "begin data" blocks
# (returns [] - e.g. the simulation crashed after writing its
# header but before writing monitor results
# Either way, skip it here rather than letting an empty (or
# None) entry propagate into monitors_by_subdir and crash the
# indexing below; load_sweep()'s own root/secondary
# length-mismatch check further down already warns (rather
# than crashing) if this ever leaves the secondary monitor
# count out of sync with mccode.dat's row count.
print("_load_sweep_monitors: skipping subdir %s (no usable monitor data found)" % s)

if not monitors_by_subdir:
# No subfolder had usable data at all (e.g. every scan step
# failed) - return no secondary monitors rather than crashing on
# monitors_by_subdir[0] below. load_sweep() still has the root
# sweep curves from mccode.dat either way; it just won't have a
# per-step drill-down to offer.
return []

# notice that columns and rows are swapped, so we get to use a
# list-of-lists data structure, with rows the same monitor
Expand Down Expand Up @@ -599,7 +641,7 @@ def has_filename(args):
def is_mccodesim_or_mccodedat(args):
f = args['simfile']
f_name = basename(f)
return (f_name == 'mccode.sim' or f_name == 'mcstas.sim' or f_name == 'mccode.dat') and isfile(f)
return (f_name == 'mccode.sim' or f_name == 'mccode.dat') and isfile(f)


def is_monitorfile(args):
Expand All @@ -623,13 +665,24 @@ def is_sweepfolder(args):


def is_broken_sweepfolder(args):
''' not implemented (returns trivial answer) '''
return False


def is_sweep_data_present(args):
''' not implemented '''
raise Exception('is_sweep_data_present has not been implemented.')
''' A sweep/scan directory that has mccode.dat (the combined scan-curve
file written by mcrun's Scanner/Scanner_split/Optimizer - see
tools/Python/mcrun/optimisation.py) but is missing mccode.sim -
e.g. because only mccode.dat was kept or shared on its own, or the
per-scan-step subfolders and their individual monitor files were
cleaned up/never transferred. is_sweepfolder() (checked just
before this in the flowchart) already requires BOTH files to be
present for the full, richer sweep view (load_sweep(), with its
per-step secondary drill-down) - reaching this function means that
check already failed, so finding mccode.dat here specifically
means mccode.sim must be the one missing. mccode.dat is
self-describing via its own '#'-prefixed header, though (see
_load_multiplot_1D_lst()), so the overlaid sweep curves themselves
can still be recovered and plotted even without mccode.sim or any
of the underlying per-monitor detector files - see
load_sweep_dat_only(). '''
d = args['directory']
return isfile(join(d, 'mccode.dat'))


def is_mccodesim_w_monitors(args):
Expand All @@ -638,8 +691,6 @@ def is_mccodesim_w_monitors(args):
# checks mccode.sim existence
if isfile(join(d, 'mccode.sim')):
indexfile='mccode.sim'
elif isfile(join(d, 'mcstas.sim')):
indexfile='mcstas.sim'
else:
return False

Expand All @@ -650,8 +701,6 @@ def is_mccodesim_w_monitors(args):
datfiles = glob.glob(join(d, '*'))
if 'mccode.sim' in datfiles:
datfiles.remove('mccode.sim')
if 'mcstas.sim' in datfiles:
datfiles.remove('mcstas.sim')
if 'mccode.dat' in datfiles:
datfiles.remove('mccode.dat')
return len(datfiles) > 0
Expand All @@ -664,8 +713,6 @@ def has_datfile(args):
datfiles = glob.glob(join(d, '*'))
if 'mccode.sim' in datfiles:
datfiles.remove('mccode.sim')
if 'mcstas.sim' in datfiles:
datfiles.remove('mcstas.sim')
if 'mccode.dat' in datfiles:
datfiles.remove('mccode.dat')
if len(datfiles) > 0:
Expand All @@ -685,8 +732,6 @@ def has_multiple_datfiles(args):
datfiles = glob.glob(join(d, '*'))
if 'mccode.sim' in datfiles:
datfiles.remove('mccode.sim')
if 'mcstas.sim' in datfiles:
datfiles.remove('mcstas.sim')
if 'mccode.dat' in datfiles:
datfiles.remove('mccode.dat')
for f in datfiles:
Expand All @@ -705,7 +750,6 @@ def test_decfuncs(simfile):
print('is_monitorfile: %s' % str(is_monitorfile(args)))
print('is_sweepfolder: %s' % str(is_sweepfolder(args)))
print('is_broken_sweepfolder: %s' % str(is_broken_sweepfolder(args)))
#print('is_sweep_data_present: %s' % str(is_sweep_data_present(args))) # should not be called until implemented
print('is_mccodesim_w_monitors: %s' % str(is_mccodesim_w_monitors(args)))
print('has_datfile: %s' % str(has_datfile(args)))
print('has_multiple_datfiles: %s' % str(has_multiple_datfiles(args)))
Expand Down Expand Up @@ -733,8 +777,6 @@ def load_simulation(args):
# load monitor data handles
if isfile(join(d, 'mccode.sim')):
indexfile='mccode.sim'
elif isfile(join(d, 'mcstas.sim')):
indexfile='mcstas.sim'
else:
indexfile=''
data_lst = _load_data_from_mcfiles(_get_filenames_from_mccodesim(join(d, indexfile)))
Expand All @@ -754,8 +796,6 @@ def load_simulation(args):
def load_sweep(args):
d = args['directory']
f_dat = join(d, 'mccode.dat')
if isfile(join(d, 'mcstas.sim')):
f_dat = join(d, 'mcstas.sim')

# load primary data_handle, 1D sweep values
data_handle_lst_sweep1D = _load_multiplot_1D_lst(f_dat)
Expand Down Expand Up @@ -853,6 +893,45 @@ def load_sweep_c(args):
raise Exception('load_sweep_c is not implemented.')


def load_sweep_dat_only(args):
''' Fallback for a sweep/scan directory that has mccode.dat (the
combined scan-curve file) but not mccode.sim and/or the
underlying per-step subfolders and monitor files - see
is_broken_sweepfolder(). mccode.dat is self-describing via its own
'#'-prefixed header (component/filename/title/xvars/xlimits/
variables/yvars - the same fields build_header() in
tools/Python/mcrun/optimisation.py writes), so
_load_multiplot_1D_lst() can parse it directly with no other
input at all.

Only builds the root+primary levels (mirroring load_simulation()/
load_monitor_folder()'s two-level graphs): an overview showing
every monitor's sweep curve overlaid, and a primary per-monitor
drill-down to see just that one curve. There deliberately are no
secondaries here - load_sweep()'s secondary level lets you drill
further into an individual scan step's own raw monitor data
(loaded from that step's own subfolder+mccode.sim), which simply
doesn't exist in this fallback - only the combined sweep curves
do. Leaving secondaries as the default empty list is a normal,
supported plot-graph state (matching how a PNSingle leaf node, or
load_simulation()'s single-level case, has none either), not an
incomplete/degraded one - frontends already handle it as "nothing
further to click into" rather than an error. '''
d = args['directory']
f_dat = join(d, 'mccode.dat')

data_handle_lst_sweep1D = _load_multiplot_1D_lst(f_dat)
root = PNMultiple(data_handle_lst_sweep1D)

primnodes_lst = []
for data_handle in data_handle_lst_sweep1D:
primnode = PNSingle(data_handle)
primnodes_lst.append(primnode)
root.set_primaries(primnodes_lst)

return root


def load_monitor_folder(args):
# assume simfile is folder with multiple dat files
d = args['directory']
Expand Down Expand Up @@ -892,8 +971,7 @@ def load(self):
exit_term_case1 = FCNTerminal(key = "case1", fct = load_monitor)
exit_term_case2 = FCNTerminal(key = "case2", fct = load_simulation)
exit_term_case3 = FCNTerminal(key = "case3", fct = load_sweep)
exit_term_case3b = FCNTerminal(key = "case3b", fct = throw_error)
exit_term_case3c = FCNTerminal(key = "case3c", fct = throw_error)
exit_term_case3fallback = FCNTerminal(key = "case3-fallback", fct = load_sweep_dat_only)
exit_term_case4 = FCNTerminal(key = "case4", fct = load_monitor_folder)

# decision nodes (assembled in backwards order)
Expand All @@ -906,11 +984,8 @@ def load(self):
dec_ismccodesimwmonitors = FCNDecisionBool(fct = is_mccodesim_w_monitors,
node_T = exit_term_case2,
node_F = dec_hasdatfile)
dec_datafolderspresent = FCNDecisionBool(fct = is_sweep_data_present,
node_T = exit_term_case3b,
node_F = exit_term_case3c)
dec_isbrokensweep = FCNDecisionBool(fct = is_broken_sweepfolder,
node_T = dec_datafolderspresent,
node_T = exit_term_case3fallback,
node_F = dec_ismccodesimwmonitors)
dec_issweepfolder = FCNDecisionBool(fct = is_sweepfolder,
node_T = exit_term_case3,
Expand Down
13 changes: 13 additions & 0 deletions tools/Python/mcplot/matplotlib/mcplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ def main(args):
if (h5file):
if not os.path.isabs(h5file):
h5file = os.path.join(os.getcwd(),h5file)
# A NeXus-format scan (as opposed to a single simulation)
# also writes a scan-summary mccode.dat alongside
# mccode.h5. If it's there, also spawn a separate,
# independent instance of this same mcplot variant pointed
# directly at mccode.dat, running in the background
# alongside the HDFVIEW.
datfile = os.path.join(os.path.dirname(h5file), 'mccode.dat')
if os.path.isfile(datfile):
try:
print('Also spawning %s on %s' % (os.path.basename(__file__), datfile))
subprocess.Popen([sys.executable, os.path.abspath(__file__), datfile])
except Exception as e:
print('Could not launch a second mcplot instance on ' + datfile + ': ' + e.__str__())
try:
cmd = mccode_config.configuration['HDFVIEW'] + ' ' + h5file
print('Spawning ' + mccode_config.configuration['HDFVIEW'])
Expand Down
13 changes: 13 additions & 0 deletions tools/Python/mcplot/pyqtgraph/mcplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ def main(args):
if (h5file):
if not os.path.isabs(h5file):
h5file = os.path.join(os.getcwd(),h5file)
# A NeXus-format scan (as opposed to a single simulation)
# also writes a scan-summary mccode.dat alongside
# mccode.h5. If it's there, also spawn a separate,
# independent instance of this same mcplot variant pointed
# directly at mccode.dat, running in the background
# alongside the HDFVIEW.
datfile = os.path.join(os.path.dirname(h5file), 'mccode.dat')
if os.path.isfile(datfile):
try:
print('Also spawning %s on %s' % (os.path.basename(__file__), datfile))
subprocess.Popen([sys.executable, os.path.abspath(__file__), datfile])
except Exception as e:
print('Could not launch a second mcplot instance on ' + datfile + ': ' + e.__str__())
try:
cmd = mccode_config.configuration['HDFVIEW'] + ' ' + h5file
print('Spawning ' + mccode_config.configuration['HDFVIEW'])
Expand Down
Loading
Loading