From 553756f9eca57f23bc27a2d770eab368f50e002e Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Thu, 22 Oct 2026 17:33:08 +0200 Subject: [PATCH 1/5] NeXus output on native HDF5, replacing the frozen napi Implement the subset of the NeXus API used by McCode and the Monitor_nD libraries (NXopen/close, make/open/closegroup, (comp)makedata(64), open/close/putdata, putslab64, putattr, getinfo64, getgroupinfo, getgroupID, makelink, error reporting) directly on HDF5 in mccode-r.c, with the same names so components using nxhandle keep compiling. The written layout (groups, NX_class, string types, chunking, shuffle + deflate level, links and 'target' attributes) matches libNeXus 4.4.3, verified with h5dump -p/h5diff on serial, list-flush, --IDF and MPI (VDS) runs for McStas and McXtrace. The open groups are a stack of HDF5 ids: no fixed path buffers, a failed open does not change the state, over-closing is reported, nested error suppression nests, and chunks are kept below the 4 GiB HDF5 limit. Fixes in the NeXus writers: - nxstr used the value as printf format: string parameters, titles or instrument text with '%' were garbled (e.g. "a%sb" -> "aa%sbb") or UB. Values are now passed as "%s", without the 1024-char buffer. - Monitor_nD BINS output wrote uninitialised MCDETECTOR fields (Date, xylimits, ... seen as stack garbage in MPI runs); zero it. - uninitialised NXlink used for /entry/data links when a group failed to open; unbalanced closegroup in mcdatainfo_out_nexus and the IDF group. - existing files were opened with mode NXACC_CREATE5|NXACC_RDWR (7). - nxhandle was defined in the header (duplicate symbol with -fno-common when the runtime is not embedded); siminfo_out() wrote to the NeXus handle as a FILE*. - drop a debug printf in mcdetector_out_3D_nexus and the duplicated zmin/zmax geometry attributes in Monitor_nD. Output changes: no root NX_class is written (as libNeXus 4.4.3), the file_time offset is now correct during DST, NeXus_version stays "4.4.2". Co-Authored-By: Claude Opus 5.5 --- common/lib/share/mccode-r.c | 499 ++++++++++++++++++++------ common/lib/share/mccode-r.h.in | 55 ++- mcstas-comps/share/monitor_nd-lib.c | 61 +--- mcxtrace-comps/share/monitor_nd-lib.c | 27 +- 4 files changed, 474 insertions(+), 168 deletions(-) diff --git a/common/lib/share/mccode-r.c b/common/lib/share/mccode-r.c index e5517470bc..40a5e0e341 100644 --- a/common/lib/share/mccode-r.c +++ b/common/lib/share/mccode-r.c @@ -801,7 +801,7 @@ FILE *mcnew_file(char *name, char *ext, int *exists) #ifdef USE_NEXUS if (mcformat && strcasestr(mcformat, "NeXus")) { /* NXhandle nxhandle is defined in the .h with USE_NEXUS */ - NXaccess mode = (*exists ? NXACC_CREATE5 | NXACC_RDWR : NXACC_CREATE5); + NXaccess mode = (*exists ? NXACC_RDWR : NXACC_CREATE5); if (NXopen(mem, mode, &nxhandle) != NX_OK) file = NULL; @@ -1333,6 +1333,9 @@ void siminfo_out(char *format, ...) { va_list ap; +#ifdef USE_NEXUS + if (siminfo_file == (FILE*)&nxhandle) return; /* NeXus: not a FILE */ +#endif if(siminfo_file && !mcdisable_output_files) { va_start(ap, format); @@ -1568,6 +1571,313 @@ static char *strcpy_valid(char *valid, char *original) #ifdef USE_NEXUS +/* ========================================================================== */ + +/* NeXus API subset (napi.h) on native HDF5, see mccode-r.h */ + +/* ========================================================================== */ + +NXhandle nxhandle = NULL; + +/* The open groups are a stack of HDF5 ids, so names are resolved by HDF5 + relative to the current group: no path buffers, and a failed open leaves + the stack unchanged. At most one data set is open (napi semantics). */ +#define MCNX_MAXDEPTH 32 +struct mcnx_file { hid_t fid, d, g[MCNX_MAXDEPTH]; int depth; }; +static int mcnx_quiet = 0; /* nesting depth of NXMDisableErrorReporting (napi5 did not nest) */ + +void NXMDisableErrorReporting(void) { mcnx_quiet++; } +void NXMEnableErrorReporting(void) { if (mcnx_quiet) mcnx_quiet--; } + +static NXstatus mcnx_error(const char *what, const char *name) +{ + if (!mcnx_quiet) fprintf(stderr, "NeXus error: %s '%s'\n", what, name ? name : ""); + return(NX_ERROR); +} + +/* current group, the file id standing for the root group */ +static hid_t mcnx_loc(struct mcnx_file *f) { return f->depth ? f->g[f->depth-1] : f->fid; } + +static hid_t mcnx_type(int type) +{ + return type == NX_INT32 ? H5T_NATIVE_INT : type == NX_FLOAT64 ? H5T_NATIVE_DOUBLE : -1; +} + +/* scalar attribute, replacing an existing one (napi5) */ +static herr_t mcnx_attr(hid_t obj, const char *name, hid_t type, const void *data) +{ + hid_t space = H5Screate(H5S_SCALAR), a; + herr_t ret = -1; + if (H5Aexists_by_name(obj, ".", name, H5P_DEFAULT) > 0) + H5Adelete_by_name(obj, ".", name, H5P_DEFAULT); + a = H5Acreate_by_name(obj, ".", name, type, space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (a >= 0) { ret = H5Awrite(a, type, data); H5Aclose(a); } + H5Sclose(space); + return(ret); +} + +/* fixed-length string attribute without terminating NUL; HDF5 has no empty fixed strings */ +static herr_t mcnx_attr_str(hid_t obj, const char *name, const char *value, size_t len) +{ + hid_t t; + herr_t ret; + if (!len) return(-1); + t = H5Tcopy(H5T_C_S1); + H5Tset_size(t, len); + ret = mcnx_attr(obj, name, t, value); + H5Tclose(t); + return(ret); +} + +NXstatus NXopen(const char *filename, NXaccess am, NXhandle *h) +{ + struct mcnx_file *f = calloc(1, sizeof(*f)); + hid_t fapl; + + *h = NULL; + if (!f) return(mcnx_error("out of memory opening", filename)); + H5Eset_auto2(H5E_DEFAULT, NULL, NULL); /* failures are reported here, as napi5 did */ + fapl = H5Pcreate(H5P_FILE_ACCESS); + H5Pset_fclose_degree(fapl, H5F_CLOSE_STRONG); /* H5Fclose also closes ids left open */ + f->fid = am == NXACC_CREATE5 ? H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl) + : H5Fopen(filename, H5F_ACC_RDWR, fapl); + H5Pclose(fapl); + if (f->fid < 0) { free(f); return(mcnx_error("cannot open file", filename)); } + if (am == NXACC_CREATE5) { /* root attributes, as written by napi5 */ + unsigned maj, min, rel; + char ver[32], date[64]; + time_t t = time(NULL); + size_t n = strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S%z", localtime(&t)); + if (n == 24) { memmove(date+23, date+22, 3); date[22] = ':'; n = 25; } /* +hhmm -> +hh:mm */ + H5get_libversion(&maj, &min, &rel); + snprintf(ver, sizeof(ver), "%u.%u.%u", maj, min, rel); + mcnx_attr_str(f->fid, "NeXus_version", "4.4.2", 5); /* ponytail: last napi version, kept for readers checking it */ + mcnx_attr_str(f->fid, "file_name", filename, strlen(filename)); + mcnx_attr_str(f->fid, "HDF5_Version", ver, strlen(ver)); + mcnx_attr_str(f->fid, "file_time", date, n); /* no NX_class: the napi 4.4.3 builds in use never wrote it */ + } + *h = f; + return(NX_OK); +} + +NXstatus NXclose(NXhandle *h) +{ + struct mcnx_file *f = *h; + herr_t ret; + if (!f) return(NX_ERROR); + ret = H5Fclose(f->fid); /* closes open groups/data set too (H5F_CLOSE_STRONG) */ + free(f); + *h = NULL; + return(ret < 0 ? mcnx_error("cannot close file", NULL) : NX_OK); +} + +NXstatus NXmakegroup(NXhandle h, const char *name, const char *nxclass) +{ + struct mcnx_file *f = h; + hid_t g = H5Gcreate2(mcnx_loc(f), name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + herr_t ret; + if (g < 0) return(mcnx_error("could not create group", name)); + ret = mcnx_attr_str(g, "NX_class", nxclass, strlen(nxclass)); + H5Gclose(g); + return(ret < 0 ? mcnx_error("failed to store class of group", name) : NX_OK); +} + +/* ponytail: the NX_class of the group is not checked against nxclass (napi5 did) */ +NXstatus NXopengroup(NXhandle h, const char *name, const char *nxclass) +{ + struct mcnx_file *f = h; + hid_t g; + if (f->depth == MCNX_MAXDEPTH) return(mcnx_error("groups nested too deep at", name)); + g = H5Gopen2(mcnx_loc(f), name, H5P_DEFAULT); + if (g < 0) return(mcnx_error("group does not exist", name)); + f->g[f->depth++] = g; + return(NX_OK); +} + +NXstatus NXclosegroup(NXhandle h) +{ + struct mcnx_file *f = h; + if (!f->depth) return(mcnx_error("no group open to close", NULL)); /* napi5: silent */ + H5Gclose(f->g[--f->depth]); + return(NX_OK); +} + +NXstatus NXcompmakedata64(NXhandle h, const char *name, int type, int rank, + int64_t dims[], int comp, int64_t chunk[]) +{ + struct mcnx_file *f = h; + hsize_t cur[H5S_MAX_RANK], max[H5S_MAX_RANK], ch[H5S_MAX_RANK]; + hid_t t, s, p, d; + int i, unlimited = 0; + int lzw = comp == NX_COMP_LZW || comp/100 == NX_COMP_LZW; + int level = comp == NX_COMP_LZW ? 6 : comp % 100; + double bytes; + + if (!f->depth || rank < 1 || rank > H5S_MAX_RANK + || (type != NX_CHAR && mcnx_type(type) < 0)) + return(mcnx_error("cannot create data set (no group, rank or type)", name)); + for (i = 0; i < rank; i++) { + unlimited |= dims[i] <= 0; + cur[i] = dims[i] > 0 ? dims[i] : 1; + max[i] = dims[i] > 0 ? dims[i] : H5S_UNLIMITED; + ch[i] = chunk[i] > 0 ? chunk[i] : 1; + } + if (type == NX_CHAR) { /* napi5: the last dimension is the string length */ + t = H5Tcopy(H5T_C_S1); + H5Tset_size(t, cur[rank-1]); + cur[rank-1] = max[rank-1] = ch[rank-1] = 1; + } else t = H5Tcopy(mcnx_type(type)); + p = H5Pcreate(H5P_DATASET_CREATE); + if (lzw || unlimited) { + /* HDF5 refuses chunks of 4 GiB or more: napi5 then lost the whole data set */ + for (bytes = H5Tget_size(t), i = 0; i < rank; i++) bytes *= ch[i]; + if (bytes >= 4294967295.0) ch[0] = ch[0]/(hsize_t)(bytes/4294967295.0 + 1) + 1; + H5Pset_chunk(p, rank, ch); + if (lzw) { H5Pset_shuffle(p); H5Pset_deflate(p, level); } + } + s = H5Screate_simple(rank, cur, max); + d = H5Dcreate2(mcnx_loc(f), name, t, s, H5P_DEFAULT, p, H5P_DEFAULT); + H5Sclose(s); H5Pclose(p); H5Tclose(t); + if (d < 0) return(mcnx_error("could not create data set", name)); + H5Dclose(d); + return(NX_OK); +} + +NXstatus NXcompmakedata(NXhandle h, const char *name, int type, int rank, int dims[], int comp, int chunk[]) +{ + int64_t d[H5S_MAX_RANK], c[H5S_MAX_RANK]; + int i; + for (i = 0; i < rank && i < H5S_MAX_RANK; i++) { d[i] = dims[i]; c[i] = chunk[i]; } + return(NXcompmakedata64(h, name, type, rank, d, comp, c)); +} + +NXstatus NXmakedata(NXhandle h, const char *name, int type, int rank, int dims[]) +{ + return(NXcompmakedata(h, name, type, rank, dims, NX_COMP_NONE, dims)); +} + +NXstatus NXopendata(NXhandle h, const char *name) +{ + struct mcnx_file *f = h; + if (f->d) H5Dclose(f->d); + f->d = H5Dopen2(mcnx_loc(f), name, H5P_DEFAULT); + if (f->d < 0) { f->d = 0; return(mcnx_error("data set not found", name)); } + return(NX_OK); +} + +NXstatus NXclosedata(NXhandle h) +{ + struct mcnx_file *f = h; + if (!f->d) return(mcnx_error("no data set open to close", NULL)); + H5Dclose(f->d); + f->d = 0; + return(NX_OK); +} + +/* write data with the type of the data set (napi5); only used with fixed-size data sets */ +NXstatus NXputdata(NXhandle h, const void *data) +{ + struct mcnx_file *f = h; + hid_t t; + herr_t ret; + if (!f->d) return(mcnx_error("no data set open to write", NULL)); + t = H5Dget_type(f->d); + ret = H5Dwrite(f->d, t, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + H5Tclose(t); + return(ret < 0 ? mcnx_error("failure to write data", NULL) : NX_OK); +} + +/* write a slab, extending unlimited dimensions as needed */ +NXstatus NXputslab64(NXhandle h, const void *data, const int64_t start[], const int64_t size[]) +{ + struct mcnx_file *f = h; + hsize_t s0[H5S_MAX_RANK], n[H5S_MAX_RANK], ext[H5S_MAX_RANK], max[H5S_MAX_RANK]; + hid_t fs, ms, t; + herr_t ret = -1; + int i, rank, grow = 0; + + if (!f->d) return(mcnx_error("no data set open to write", NULL)); + fs = H5Dget_space(f->d); + rank = H5Sget_simple_extent_dims(fs, ext, max); + for (i = 0; i < rank; i++) { + s0[i] = start[i]; n[i] = size[i]; + if (max[i] == H5S_UNLIMITED && s0[i] + n[i] > ext[i]) { ext[i] = s0[i] + n[i]; grow = 1; } + } + if (grow) { H5Sclose(fs); H5Dset_extent(f->d, ext); fs = H5Dget_space(f->d); } + ms = H5Screate_simple(rank, n, NULL); + t = H5Dget_type(f->d); + if (H5Sselect_hyperslab(fs, H5S_SELECT_SET, s0, NULL, n, NULL) >= 0) + ret = H5Dwrite(f->d, t, ms, fs, H5P_DEFAULT, data); + H5Tclose(t); H5Sclose(ms); H5Sclose(fs); + return(ret < 0 ? mcnx_error("writing slab failed", NULL) : NX_OK); +} + +/* attribute of the open data set, else of the current group (root included) */ +NXstatus NXputattr(NXhandle h, const char *name, const void *data, int len, int type) +{ + struct mcnx_file *f = h; + hid_t obj = f->d ? f->d : mcnx_loc(f); + herr_t ret = type == NX_CHAR ? mcnx_attr_str(obj, name, data, len > 0 ? len : 0) + : mcnx_type(type) >= 0 ? mcnx_attr(obj, name, mcnx_type(type), data) : -1; + return(ret < 0 ? mcnx_error("attribute cannot be created", name) : NX_OK); +} + +NXstatus NXgetinfo64(NXhandle h, int *rank, int64_t dims[], int *type) +{ + struct mcnx_file *f = h; + hsize_t d[H5S_MAX_RANK]; + hid_t s, t; + int i; + if (!f->d) return(mcnx_error("no data set open", NULL)); + s = H5Dget_space(f->d); t = H5Dget_type(f->d); + *rank = H5Sget_simple_extent_dims(s, d, NULL); + for (i = 0; i < *rank; i++) dims[i] = d[i]; + /* ponytail: only the types McCode writes; napi5 also mapped sizes/signedness */ + *type = H5Tget_class(t) == H5T_STRING ? NX_CHAR : H5Tget_class(t) == H5T_INTEGER ? NX_INT32 : NX_FLOAT64; + H5Tclose(t); H5Sclose(s); + return(NX_OK); +} + +/* number of items in the current group; name/class only for the root (all McCode uses) */ +NXstatus NXgetgroupinfo(NXhandle h, int *n, NXname name, NXname nxclass) +{ + struct mcnx_file *f = h; + H5G_info_t info; + if (H5Gget_info(mcnx_loc(f), &info) < 0) return(mcnx_error("cannot get group info", NULL)); + *n = (int)info.nlinks; + strcpy(name, f->depth ? "" : "root"); + strcpy(nxclass, f->depth ? "" : "NXroot"); + return(NX_OK); +} + +NXstatus NXgetgroupID(NXhandle h, NXlink *link) +{ + struct mcnx_file *f = h; + ssize_t len; + if (!f->depth) return(NX_ERROR); + len = H5Iget_name(f->g[f->depth-1], link->targetPath, sizeof(link->targetPath)); + if (len <= 0 || len >= (ssize_t)sizeof(link->targetPath)) + return(mcnx_error("group path too long to link", NULL)); + link->linkType = 0; + return(NX_OK); +} + +/* hard link to link->targetPath in the current group, and 'target' attribute on it */ +NXstatus NXmakelink(NXhandle h, NXlink *link) +{ + struct mcnx_file *f = h; + const char *item = strrchr(link->targetPath, '/'); + hid_t obj; + herr_t ret; + if (!f->depth || !item) return(mcnx_error("cannot link", link->targetPath)); + H5Lcreate_hard(f->fid, link->targetPath, mcnx_loc(f), item+1, H5P_DEFAULT, H5P_DEFAULT); /* an existing link is kept (napi5) */ + obj = H5Oopen(f->fid, link->targetPath, H5P_DEFAULT); + if (obj < 0) return(mcnx_error("link target does not exist", link->targetPath)); + ret = mcnx_attr_str(obj, "target", link->targetPath, strlen(link->targetPath)); + H5Oclose(obj); + return(ret < 0 ? mcnx_error("cannot set link target", link->targetPath) : NX_OK); +} + /* name of the NXentry opened by mcinfo_out_nexus (used by mclist_vds_write) */ static char mcnxentry[CHAR_BUF_LENGTH] = "entry1"; @@ -1582,47 +1892,39 @@ static char mcnxentry[CHAR_BUF_LENGTH] = "entry1"; /******************************************************************************* * nxstr: output a tag=value data set (char) in NeXus/current group -* when 'format' is larger that 1024 chars it is used as value for the 'tag' -* else the value is assembled with format and following arguments. +* the value is assembled with format and following arguments: a string +* value must be passed as nxstr(.., "%s", value). Empty values are skipped. * type='d' -> data set * 'a' -> attribute for current data set *******************************************************************************/ -static int nxstr(char type, NXhandle *f, char *tag, char *format, ...) +static int nxstr(char type, NXhandle f, char *tag, char *format, ...) { va_list ap; - char value[CHAR_BUF_LENGTH]; - int i; - int ret=NX_OK; + char *value; + int len, ret; if (!tag || !format || !strlen(tag) || !strlen(format)) return(NX_OK); /* assemble the value string */ - if (strlen(format) < CHAR_BUF_LENGTH) { - va_start(ap, format); - ret = vsnprintf(value, CHAR_BUF_LENGTH, format, ap); - va_end(ap); - - i = strlen(value); - } else { - i = strlen(format); - } + va_start(ap, format); + len = vsnprintf(NULL, 0, format, ap); + va_end(ap); + if (len <= 0) return(NX_OK); + if (!(value = malloc(len+1))) return(NX_ERROR); + va_start(ap, format); + vsnprintf(value, len+1, format, ap); + va_end(ap); if (type == 'd') { - /* open/put/close data set */ - if (NXmakedata (f, tag, NX_CHAR, 1, &i) != NX_OK) return(NX_ERROR); - NXopendata (f, tag); - if (strlen(format) < CHAR_BUF_LENGTH) - ret = NXputdata (f, value); - else - ret = NXputdata (f, format); - NXclosedata(f); - } else { - if (strlen(format) < CHAR_BUF_LENGTH) - ret = NXputattr (f, tag, value, strlen(value), NX_CHAR); - else - ret = NXputattr (f, tag, format, strlen(format), NX_CHAR); - } - + /* create/open/put/close data set */ + ret = NXmakedata(f, tag, NX_CHAR, 1, &len); + if (ret == NX_OK && (ret = NXopendata(f, tag)) == NX_OK) { + ret = NXputdata(f, value); + NXclosedata(f); + } + } else + ret = NXputattr(f, tag, value, len, NX_CHAR); + free(value); return(ret); } /* nxstr */ @@ -1639,13 +1941,9 @@ char *mcinfo_readfile(char *filename) fseek(f, 0, SEEK_END); long fsize = ftell(f); rewind(f); - char *string = malloc(fsize + 1); - if (string) { - int n = fread(string, fsize, 1, f); - fclose(f); - - string[fsize] = 0; - } + char *string = fsize >= 0 ? malloc(fsize + 1) : NULL; + if (string) string[fread(string, 1, fsize, f)] = 0; + fclose(f); return(string); } @@ -1677,13 +1975,13 @@ static void mcinfo_out_nexus(NXhandle f) /* create the main NXentry (mandatory in NeXus) */ if (NXmakegroup(f, entry0, "NXentry") == NX_OK) if (NXopengroup(f, entry0, "NXentry") == NX_OK) { - nxprintf(nxhandle, "program_name", MCCODE_STRING); - nxprintf(f, "start_time", ctime(&t)); + nxprintf(f, "program_name", MCCODE_STRING); + nxprintf(f, "start_time", "%s", ctime(&t)); nxprintf(f, "title", "%s%s%s simulation generated by instrument %s", dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name, instrument_name); nxprintattr(f, "program_name", MCCODE_STRING); - nxprintattr(f, "instrument", instrument_name); + nxprintattr(f, "instrument", "%s", instrument_name); nxprintattr(f, "simulation", "%s%s%s", dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name); @@ -1706,16 +2004,16 @@ static void mcinfo_out_nexus(NXhandle f) if (strlen(string) + strlen(ThisParam) < CHAR_BUF_LENGTH) strcat(string, ThisParam); } - nxprintattr(f, "Parameters", string); + nxprintattr(f, "Parameters", "%s", string); free(string); } - nxprintattr(f, "name", instrument_name); - nxprintf (f, "name", instrument_name); - nxprintattr(f, "Source", instrument_source); + nxprintattr(f, "name", "%s", instrument_name); + nxprintf (f, "name", "%s", instrument_name); + nxprintattr(f, "Source", "%s", instrument_source); - nxprintattr(f, "Trace_enabled", traceenabled ? "yes" : "no"); - nxprintattr(f, "Default_main", defaultmain ? "yes" : "no"); + nxprintattr(f, "Trace_enabled", "%s", traceenabled ? "yes" : "no"); + nxprintattr(f, "Default_main", "%s", defaultmain ? "yes" : "no"); #ifdef MC_EMBEDDED_RUNTIME nxprintattr(f, "Embedded_runtime", "yes"); #else @@ -1726,9 +2024,9 @@ static void mcinfo_out_nexus(NXhandle f) buffer = mcinfo_readfile(instrument_source); if (buffer && strlen(buffer)) { long length=strlen(buffer); - nxprintf (f, "description", buffer); + nxprintf (f, "description", "%s", buffer); NXopendata(f,"description"); - nxprintattr(f, "file_name", instrument_source); + nxprintattr(f, "file_name", "%s", instrument_source); nxprintattr(f, "file_size", "%li", length); nxprintattr(f, "MCCODE_STRING", MCCODE_STRING); NXclosedata(f); @@ -1745,14 +2043,15 @@ static void mcinfo_out_nexus(NXhandle f) sprintf(IDFfile,"%s%s",instrument_source,".xml"); buffer = mcinfo_readfile(IDFfile); if (buffer && strlen(buffer)) { - NXmakegroup (nxhandle, "instrument_xml", "NXnote"); - NXopengroup (nxhandle, "instrument_xml", "NXnote"); - nxprintf(f, "data", buffer); - nxprintf(f, "description", "IDF.xml file found with instrument %s", instrument_source); - nxprintf(f, "type", "text/xml"); - NXclosegroup(f); /* instrument_xml */ - free(buffer); + NXmakegroup (f, "instrument_xml", "NXnote"); + if (NXopengroup(f, "instrument_xml", "NXnote") == NX_OK) { + nxprintf(f, "data", "%s", buffer); + nxprintf(f, "description", "IDF.xml file found with instrument %s", instrument_source); + nxprintf(f, "type", "text/xml"); + NXclosegroup(f); /* instrument_xml */ + } } + free(buffer); free(IDFfile); } @@ -1775,16 +2074,16 @@ static void mcinfo_out_nexus(NXhandle f) dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name); nxprintf (f, "name", "%s", siminfo_name); - nxprintattr(f, "Format", mcformat && strlen(mcformat) ? mcformat : MCCODE_NAME); + nxprintattr(f, "Format", "%s", mcformat && strlen(mcformat) ? mcformat : MCCODE_NAME); nxprintattr(f, "URL", "http://www.mccode.org"); nxprintattr(f, "program", MCCODE_STRING); - nxprintattr(f, "Instrument",instrument_source); - nxprintattr(f, "Trace", mcdotrace ? "yes" : "no"); - nxprintattr(f, "Gravitation",mcgravitation ? "yes" : "no"); + nxprintattr(f, "Instrument", "%s",instrument_source); + nxprintattr(f, "Trace", "%s", mcdotrace ? "yes" : "no"); + nxprintattr(f, "Gravitation", "%s",mcgravitation ? "yes" : "no"); nxprintattr(f, "Seed", "%li", mcseed); /* written before mccode_main splits the rays over MPI nodes */ nxprintattr(f, "Ncount_requested", "%llu", mcget_ncount()); - nxprintattr(f, "Directory", dirname); + nxprintattr(f, "Directory", "%s", dirname ? dirname : ""); #ifdef USE_MPI if (mpi_node_count > 1) nxprintf(f, "Nodes", "%i", mpi_node_count); @@ -1803,7 +2102,7 @@ static void mcinfo_out_nexus(NXhandle f) (*mcinputtypes[mcinputtable[i].type].printer)(string, mcinputtable[i].par); nxprintf(f, mcinputtable[i].name, "%s", string); - nxprintattr(f, mcinputtable[i].name, string); + nxprintattr(f, mcinputtable[i].name, "%s", string); } } NXclosegroup(f); /* Param */ @@ -2324,7 +2623,7 @@ static void mccomp_placement_type_nexus(NXhandle nxhandle, char* component, Coor fprintf(stderr, "Warning: could not open Rotation field for component %s\n",component); } } - nxprintf(nxhandle, "Component_type", comptype); + nxprintf(nxhandle, "Component_type", "%s", comptype ? comptype : ""); NXclosegroup(nxhandle); // component } else { printf("FAILED to open comp data group %s\n",component); @@ -2369,9 +2668,9 @@ static void mccomp_param_nexus(NXhandle nxhandle, char* component, char* paramet if (NXopengroup(nxhandle, "parameters", "NXdata") == NX_OK) { NXmakegroup(nxhandle, parameter, "NXnote"); if (NXopengroup(nxhandle, parameter, "NXnote") == NX_OK) { - nxprintattr(nxhandle, "type", type); - nxprintattr(nxhandle, "default", defval); - nxprintattr(nxhandle, "value", value); + nxprintattr(nxhandle, "type", "%s", type ? type : ""); + nxprintattr(nxhandle, "default", "%s", defval ? defval : ""); + nxprintattr(nxhandle, "value", "%s", value ? value : ""); NXclosegroup(nxhandle); // parameter } else { printf("FAILED to open parameters %s data group \n",parameter); @@ -2425,48 +2724,48 @@ mcdatainfo_out_nexus(NXhandle f, MCDETECTOR detector) if (NXmakegroup(f, data_name, "NXdata") == NX_OK) { if (NXopengroup(f, data_name, "NXdata") == NX_OK) { /* output metadata (as attributes) ======================================== */ - nxprintattr(f, "Date", detector.date); - nxprintattr(f, "type", detector.type); - nxprintattr(f, "Source", detector.instrument); - nxprintattr(f, "component", detector.component); - nxprintattr(f, "position", detector.position); + nxprintattr(f, "Date", "%s", detector.date); + nxprintattr(f, "type", "%s", detector.type); + nxprintattr(f, "Source", "%s", detector.instrument); + nxprintattr(f, "component", "%s", detector.component); + nxprintattr(f, "position", "%s", detector.position); - nxprintattr(f, "title", detector.title); + nxprintattr(f, "title", "%s", detector.title); nxprintattr(f, !mcget_run_num() || mcget_run_num() >= mcget_ncount() ? "Ncount" : - "ratio", detector.ncount); + "ratio", "%s", detector.ncount); if (strlen(detector.filename)) { - nxprintattr(f, "filename", detector.filename); + nxprintattr(f, "filename", "%s", detector.filename); } - nxprintattr(f, "statistics", detector.statistics); - nxprintattr(f, "signal", detector.signal); - nxprintattr(f, "values", detector.values); + nxprintattr(f, "statistics", "%s", detector.statistics); + nxprintattr(f, "signal", "%s", detector.signal); + nxprintattr(f, "values", "%s", detector.values); if (detector.rank >= 1) { - nxprintattr(f, "xvar", detector.xvar); - nxprintattr(f, "yvar", detector.yvar); - nxprintattr(f, "xlabel", detector.xlabel); - nxprintattr(f, "ylabel", detector.ylabel); + nxprintattr(f, "xvar", "%s", detector.xvar); + nxprintattr(f, "yvar", "%s", detector.yvar); + nxprintattr(f, "xlabel", "%s", detector.xlabel); + nxprintattr(f, "ylabel", "%s", detector.ylabel); if (detector.rank > 1) { - nxprintattr(f, "zvar", detector.zvar); - nxprintattr(f, "zlabel", detector.zlabel); + nxprintattr(f, "zvar", "%s", detector.zvar); + nxprintattr(f, "zlabel", "%s", detector.zlabel); } } nxprintattr(f, abs(detector.rank)==1 ? "xlimits" : - "xylimits", detector.limits); - nxprintattr(f, "variables", + "xylimits", "%s", detector.limits); + nxprintattr(f, "variables", "%s", strcasestr(detector.format, "list") ? detector.ylabel : detector.variables); NXclosegroup(f); // data_name } } + NXclosegroup(f); // output } - NXclosegroup(f); // output NXclosegroup(f); // detector.nexuscomp } NXclosegroup(f); // components @@ -2505,7 +2804,9 @@ int mcdetector_out_axis_nexus(NXhandle f, char *label, char *var, int rank, long axis[i] = min+(max-min)*(i+0.5)/length; /* create the data set */ strcpy_valid(valid, label); + NXMDisableErrorReporting(); /* exists when a list buffer was flushed before */ NXcompmakedata(f, valid, NX_FLOAT64, 1, &dim, NX_COMPRESSION, &dim); + NXMEnableErrorReporting(); /* open it */ if (NXopendata(f, valid) != NX_OK) { fprintf(stderr, "Warning: could not open axis rank %i '%s' (NeXus)\n", @@ -2516,10 +2817,10 @@ int mcdetector_out_axis_nexus(NXhandle f, char *label, char *var, int rank, long } /* put the axis and its attributes */ NXputdata (f, axis); - nxprintattr(f, "long_name", label); - nxprintattr(f, "short_name", var); + nxprintattr(f, "long_name", "%s", label); + nxprintattr(f, "short_name", "%s", var); NXputattr (f, "axis", &rank, 1, NX_INT32); - nxprintattr(f, "units", var); + nxprintattr(f, "units", "%s", var); NXputattr (f, "primary", &nprimary, 1, NX_INT32); NXclosedata(f); free(axis); @@ -2583,7 +2884,7 @@ int mcdetector_out_array_nexus(NXhandle f, char *part, double *data, MCDETECTOR if (strstr(part,"data") || strstr(part, "events")) { NXputattr(f, "signal", &signal, 1, NX_INT32); - nxprintattr(f, "short_name", strlen(detector.filename) ? + nxprintattr(f, "short_name", "%s", strlen(detector.filename) ? detector.filename : detector.component); } nxprintattr(f, "long_name", "%s '%s'", part, detector.title); @@ -2607,6 +2908,7 @@ int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector) strlen(detector.filename) ? detector.filename : detector.component); NXlink pLink; + int linked = 0; /* the NXdetector group has been created in mcinfo_out_nexus (siminfo_init) */ if (NXopengroup(f, "instrument", "NXinstrument") == NX_OK) { if (NXopengroup(f, "components", "NXdata") == NX_OK) { @@ -2617,7 +2919,7 @@ int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector) if (NXopengroup(f, data_name, "NXdata") == NX_OK) { MPI_MASTER( - nxprintattr(f, "options", + nxprintattr(f, "options", "%s", strlen(detector.options) ? detector.options : "None"); ); /* write axes, for histogram data sets, not for lists */ @@ -2630,7 +2932,7 @@ int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector) 3, detector.p, detector.zmin, detector.zmax); } else { MPI_MASTER( - nxprintattr(f, "dataset columns", + nxprintattr(f, "dataset columns", "%s", strlen(detector.ylabel) ? detector.ylabel : "None"); ); } @@ -2645,9 +2947,7 @@ int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector) } else { mcdetector_out_array_nexus( f, "events", detector.p1, detector); } - NXclosegroup(f); - NXopengroup(f, data_name, "NXdata"); - NXgetgroupID(nxhandle, &pLink); + linked = NXgetgroupID(f, &pLink) == NX_OK; NXclosegroup(f); } /* NXdata data_name*/ NXclosegroup(f); @@ -2659,9 +2959,9 @@ int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector) NXclosegroup(f); } /* NXdata instrument */ - if (!strcasestr(detector.format, "pixels")) { + if (linked && !strcasestr(detector.format, "pixels")) { if (NXopengroup(f, "data", "NXdetector") == NX_OK) { - NXmakelink(nxhandle, &pLink); + NXmakelink(f, &pLink); NXclosegroup(f); } } @@ -2701,7 +3001,6 @@ MCDETECTOR mcdetector_out_2D_nexus(MCDETECTOR detector) MCDETECTOR mcdetector_out_3D_nexus(MCDETECTOR detector) { - printf("Received detector from %s\n",detector.component); MPI_MASTER( mcdatainfo_out_nexus(nxhandle, detector); mcdetector_out_data_nexus(nxhandle, detector); @@ -2816,7 +3115,7 @@ void siminfo_close() #ifdef USE_NEXUS if (mcformat && strcasestr(mcformat, "NeXus")) { time_t t=time(NULL); - nxprintf(nxhandle, "end_time", ctime(&t)); + nxprintf(nxhandle, "end_time", "%s", ctime(&t)); nxprintf(nxhandle, "duration", "%li", (long)t-mcstartdate); NXclosegroup(nxhandle); /* NXentry */ NXclose(&nxhandle); @@ -3121,8 +3420,6 @@ static void mcdetector_list_write(MCDETECTOR detector, int append_raw) * result; 'h5repack -l CONTI' makes a self-contained copy. *******************************************************************************/ #ifdef USE_NEXUS -#include - /* one VDS to create in mccode.h5 after NXclose (master only) */ struct mclist_vds { char path[4*CHAR_BUF_LENGTH]; /* /entry/.../events in mccode.h5 */ diff --git a/common/lib/share/mccode-r.h.in b/common/lib/share/mccode-r.h.in index 62613e21de..0754c42c4d 100644 --- a/common/lib/share/mccode-r.h.in +++ b/common/lib/share/mccode-r.h.in @@ -535,6 +535,10 @@ int any_set(int n, ...); void* _getvar_parameters(char* compname); int _getcomp_index(char* compname); +/* type name and absolute rotation of a named instance (cogen'd); + * MCCODE_COMP_TYPE_ is defined for every component type present */ +char* _getcomp_type(char* compname); +int _getcomp_rotation(char* compname, Rotation r); /* Note: The two-stage approach to COMP_GETPAR is NOT redundant; without it, * after #define C sample, COMP_GETPAR(C,x) would refer to component C, not to @@ -995,8 +999,55 @@ MCDETECTOR mcdetector_out_list(char *t, char *xl, char *yl, mcdetector_out_2D(t,xl,yl,x1,x2,y1,y2,m,n,p0,p1,p2,f,NAME_CURRENT_COMP,POS_A_CURRENT_COMP,ROT_A_CURRENT_COMP,INDEX_CURRENT_COMP) #ifdef USE_NEXUS -#include "napi.h" -NXhandle nxhandle; +/* NeXus output: the subset of the frozen NeXus API (napi.h) used by McCode and + its components, implemented on native HDF5 in mccode-r.c. Names, values and + the written file layout follow napi5. */ +#include +typedef void *NXhandle; +typedef int NXstatus; +typedef int NXaccess; +typedef char NXname[128]; +typedef struct { char targetPath[1024]; int linkType; } NXlink; +#define NX_OK 1 +#define NX_ERROR 0 +#define NX_UNLIMITED -1 +#define NX_CHAR 4 +#define NX_FLOAT64 6 +#define NX_INT32 24 +#define NXACC_RDWR 2 +#define NXACC_CREATE5 5 +#define NX_COMP_NONE 100 +#define NX_COMP_LZW 200 +#define NX_COMP_LZW_LVL0 (100*NX_COMP_LZW + 0) +#define NX_COMP_LZW_LVL1 (100*NX_COMP_LZW + 1) +#define NX_COMP_LZW_LVL2 (100*NX_COMP_LZW + 2) +#define NX_COMP_LZW_LVL3 (100*NX_COMP_LZW + 3) +#define NX_COMP_LZW_LVL4 (100*NX_COMP_LZW + 4) +#define NX_COMP_LZW_LVL5 (100*NX_COMP_LZW + 5) +#define NX_COMP_LZW_LVL6 (100*NX_COMP_LZW + 6) +#define NX_COMP_LZW_LVL7 (100*NX_COMP_LZW + 7) +#define NX_COMP_LZW_LVL8 (100*NX_COMP_LZW + 8) +#define NX_COMP_LZW_LVL9 (100*NX_COMP_LZW + 9) +NXstatus NXopen(const char *filename, NXaccess am, NXhandle *h); +NXstatus NXclose(NXhandle *h); +NXstatus NXmakegroup(NXhandle h, const char *name, const char *nxclass); +NXstatus NXopengroup(NXhandle h, const char *name, const char *nxclass); +NXstatus NXclosegroup(NXhandle h); +NXstatus NXmakedata(NXhandle h, const char *name, int type, int rank, int dims[]); +NXstatus NXcompmakedata(NXhandle h, const char *name, int type, int rank, int dims[], int comp, int chunk[]); +NXstatus NXcompmakedata64(NXhandle h, const char *name, int type, int rank, int64_t dims[], int comp, int64_t chunk[]); +NXstatus NXopendata(NXhandle h, const char *name); +NXstatus NXclosedata(NXhandle h); +NXstatus NXputdata(NXhandle h, const void *data); +NXstatus NXputslab64(NXhandle h, const void *data, const int64_t start[], const int64_t size[]); +NXstatus NXputattr(NXhandle h, const char *name, const void *data, int len, int type); +NXstatus NXgetinfo64(NXhandle h, int *rank, int64_t dims[], int *type); +NXstatus NXgetgroupinfo(NXhandle h, int *n, NXname name, NXname nxclass); +NXstatus NXgetgroupID(NXhandle h, NXlink *link); +NXstatus NXmakelink(NXhandle h, NXlink *link); +void NXMDisableErrorReporting(void); +void NXMEnableErrorReporting(void); +extern NXhandle nxhandle; /* store post-INIT values of component SETTING parameters (called after init()) */ void mccomp_param_runtime_nexus_all(NXhandle f); #endif diff --git a/mcstas-comps/share/monitor_nd-lib.c b/mcstas-comps/share/monitor_nd-lib.c index 5bf9a26e45..c21b0f62e1 100644 --- a/mcstas-comps/share/monitor_nd-lib.c +++ b/mcstas-comps/share/monitor_nd-lib.c @@ -878,37 +878,32 @@ void Monitor_nD_Init(MonitornD_Defines_type *DEFS, if (NXopengroup(nxhandle, "Geometry", "NXdata") == NX_OK) { char tmp[CHAR_BUF_LENGTH]; sprintf(tmp,"%g",Vars->Sphere_Radius); - nxprintattr(nxhandle, "radius", tmp); + nxprintattr(nxhandle, "radius", "%s", tmp); sprintf(tmp,"%g",Vars->Cylinder_Height); - nxprintattr(nxhandle, "height", tmp); + nxprintattr(nxhandle, "height", "%s", tmp); sprintf(tmp,"%g",Vars->mxmin); - nxprintattr(nxhandle, "xmin", tmp); + nxprintattr(nxhandle, "xmin", "%s", tmp); sprintf(tmp,"%g",Vars->mxmax); - nxprintattr(nxhandle, "xmax", tmp); + nxprintattr(nxhandle, "xmax", "%s", tmp); sprintf(tmp,"%g",Vars->mymin); - nxprintattr(nxhandle, "ymin", tmp); + nxprintattr(nxhandle, "ymin", "%s", tmp); sprintf(tmp,"%g",Vars->mymax); - nxprintattr(nxhandle, "ymax", tmp); + nxprintattr(nxhandle, "ymax", "%s", tmp); sprintf(tmp,"%g",Vars->mzmin); - nxprintattr(nxhandle, "zmin", tmp); + nxprintattr(nxhandle, "zmin", "%s", tmp); sprintf(tmp,"%g",Vars->mzmax); - nxprintattr(nxhandle, "zmax", tmp); - - sprintf(tmp,"%g",Vars->mzmin); - nxprintattr(nxhandle, "zmin", tmp); - sprintf(tmp,"%g",Vars->mzmax); - nxprintattr(nxhandle, "zmax", tmp); + nxprintattr(nxhandle, "zmax", "%s", tmp); sprintf(tmp,"%i",Vars->Flag_Shape); - nxprintattr(nxhandle, "Shape identifier", tmp); + nxprintattr(nxhandle, "Shape identifier", "%s", tmp); sprintf(tmp,"%s",Vars->Monitor_Label); - nxprintattr(nxhandle, "Shape string", tmp); + nxprintattr(nxhandle, "Shape string", "%s", tmp); sprintf(tmp,"%s",Vars->option); - nxprintattr(nxhandle, "Option string", tmp); + nxprintattr(nxhandle, "Option string", "%s", tmp); NXclosegroup(nxhandle); // Geometry } else { @@ -941,39 +936,7 @@ void Monitor_nD_Init(MonitornD_Defines_type *DEFS, MCDETECTOR detector; - /* Init - perhaps better with an init-function in mccode-r? */ - detector.m = 0; - detector.xmin = 0; - detector.xmax = 0; - detector.ymin = 0; - detector.ymax = 0; - detector.zmin = 0; - detector.zmax = 0; - detector.intensity = 0; - detector.error = 0; - detector.events = 0; - detector.min = 0; - detector.max = 0; - detector.mean = 0; - detector.centerX = 0; - detector.halfwidthX = 0; - detector.centerY = 0; - detector.halfwidthY = 0; - detector.rank = 0; - detector.istransposed = 0; - detector.n = 0; - detector.p = 0; - detector.date_l = 0; - detector.p0 = NULL; - detector.p1 = NULL; - detector.p2 = NULL; - sprintf(detector.ncount,"%s",""); - sprintf(detector.xvar,"%s",""); - sprintf(detector.xlabel,"%s",""); - sprintf(detector.yvar,"%s",""); - sprintf(detector.ylabel,"%s",""); - sprintf(detector.zvar,"%s",""); - sprintf(detector.zvar,"%s",""); + memset(&detector, 0, sizeof(detector)); sprintf(detector.statistics,"%s","None"); sprintf(detector.variables,"%s","None"); sprintf(detector.signal,"%s","Pixel index"); diff --git a/mcxtrace-comps/share/monitor_nd-lib.c b/mcxtrace-comps/share/monitor_nd-lib.c index 61d647c72d..1060afdaf4 100644 --- a/mcxtrace-comps/share/monitor_nd-lib.c +++ b/mcxtrace-comps/share/monitor_nd-lib.c @@ -824,37 +824,32 @@ void Monitor_nD_Init(MonitornD_Defines_type *DEFS, if (NXopengroup(nxhandle, "Geometry", "NXdata") == NX_OK) { char tmp[CHAR_BUF_LENGTH]; sprintf(tmp,"%g",Vars->Sphere_Radius); - nxprintattr(nxhandle, "radius", tmp); + nxprintattr(nxhandle, "radius", "%s", tmp); sprintf(tmp,"%g",Vars->Cylinder_Height); - nxprintattr(nxhandle, "height", tmp); + nxprintattr(nxhandle, "height", "%s", tmp); sprintf(tmp,"%g",Vars->mxmin); - nxprintattr(nxhandle, "xmin", tmp); + nxprintattr(nxhandle, "xmin", "%s", tmp); sprintf(tmp,"%g",Vars->mxmax); - nxprintattr(nxhandle, "xmax", tmp); + nxprintattr(nxhandle, "xmax", "%s", tmp); sprintf(tmp,"%g",Vars->mymin); - nxprintattr(nxhandle, "ymin", tmp); + nxprintattr(nxhandle, "ymin", "%s", tmp); sprintf(tmp,"%g",Vars->mymax); - nxprintattr(nxhandle, "ymax", tmp); + nxprintattr(nxhandle, "ymax", "%s", tmp); sprintf(tmp,"%g",Vars->mzmin); - nxprintattr(nxhandle, "zmin", tmp); + nxprintattr(nxhandle, "zmin", "%s", tmp); sprintf(tmp,"%g",Vars->mzmax); - nxprintattr(nxhandle, "zmax", tmp); - - sprintf(tmp,"%g",Vars->mzmin); - nxprintattr(nxhandle, "zmin", tmp); - sprintf(tmp,"%g",Vars->mzmax); - nxprintattr(nxhandle, "zmax", tmp); + nxprintattr(nxhandle, "zmax", "%s", tmp); sprintf(tmp,"%i",Vars->Flag_Shape); - nxprintattr(nxhandle, "Shape identifier", tmp); + nxprintattr(nxhandle, "Shape identifier", "%s", tmp); sprintf(tmp,"%s",Vars->Monitor_Label); - nxprintattr(nxhandle, "Shape string", tmp); + nxprintattr(nxhandle, "Shape string", "%s", tmp); sprintf(tmp,"%s",Vars->option); - nxprintattr(nxhandle, "Option string", tmp); + nxprintattr(nxhandle, "Option string", "%s", tmp); NXclosegroup(nxhandle); // Geometry } else { From fe94055ae239c0565fb933580f21aa8bb2d09bed Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Thu, 22 Oct 2026 17:33:08 +0200 Subject: [PATCH 2/5] Build: NeXus support needs HDF5 only, not libNeXus Co-Authored-By: Claude Opus 5.5 --- cmake/Modules/InstallMCCODE.cmake | 2 +- cmake/Modules/MCUtil.cmake | 6 ------ cmake/Modules/PlatformDefaults.cmake | 8 ++++---- cmake/toolchains/deb32.cmake | 4 ++-- cmake/toolchains/deb64.cmake | 4 ++-- cmake/toolchains/deb64arm.cmake | 4 ++-- mcstas-comps/CMakeLists.txt | 4 ++-- mcxtrace-comps/CMakeLists.txt | 4 ++-- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/cmake/Modules/InstallMCCODE.cmake b/cmake/Modules/InstallMCCODE.cmake index b9b24db128..bd263eb66f 100644 --- a/cmake/Modules/InstallMCCODE.cmake +++ b/cmake/Modules/InstallMCCODE.cmake @@ -99,7 +99,7 @@ macro(installMCCODE) # update definitions to match choices if (USE_NEXUS) - AppendDef(HAVE_NEXUS="-DUSE_NEXUS -lNeXus") + AppendDef(HAVE_NEXUS="-DUSE_NEXUS -lhdf5") AppendDef(USE_NEXUS) endif() diff --git a/cmake/Modules/MCUtil.cmake b/cmake/Modules/MCUtil.cmake index 55538985a6..2d9f6c6e34 100644 --- a/cmake/Modules/MCUtil.cmake +++ b/cmake/Modules/MCUtil.cmake @@ -61,12 +61,6 @@ macro(setupMCCODE FLAVOR) set(WINDOWS true) endif() - # Set Nexus linking flag - if(USE_NEXUS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lNeXus") - set(CMAKE_C_LFLAGS "${CMAKE_C_LFLAGS} -lNeXus") - endif() - # Set 32-bit flags if(ARCH EQUAL "i386") set(CMAKE_C_FLAGS "-m32") diff --git a/cmake/Modules/PlatformDefaults.cmake b/cmake/Modules/PlatformDefaults.cmake index bf0d13d619..e275524a5e 100644 --- a/cmake/Modules/PlatformDefaults.cmake +++ b/cmake/Modules/PlatformDefaults.cmake @@ -225,15 +225,15 @@ function( detect_platform_variables resultvarname ) #NeXus-flags: if ( NOT NEXUSFLAGS ) - set(NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lNeXus -lhdf5")#?? should probably have another value + set(NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5") if ( MCCODE_BUILD_CONDA_PKG ) - set( NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -I\$\{CONDA_PREFIX\}/include/nexus -I\$\{CONDA_PREFIX\}/include -Wl,-rpath,$\{CONDA_PREFIX\}/lib -L\$\{CONDA_PREFIX\}/lib -lNeXus -lhdf5" ) + set( NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -I\$\{CONDA_PREFIX\}/include -Wl,-rpath,$\{CONDA_PREFIX\}/lib -L\$\{CONDA_PREFIX\}/lib -lhdf5" ) endif() if ( WINDOWS ) if ( "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC" ) - set( NEXUSFLAGS " /DUSE_NEXUS /DNX_COMPRESSION=NX_COMP_LZW_LVL1 /DH5_BUILT_AS_DYNAMIC_LIB /I\$\{CONDA_PREFIX\}/Library/include/nexus /I\$\{CONDA_PREFIX\}/Library/include /link /LIBPATH:\$\{CONDA_PREFIX\}/Library/lib NeXus.lib hdf5.lib" ) + set( NEXUSFLAGS " /DUSE_NEXUS /DNX_COMPRESSION=NX_COMP_LZW_LVL1 /DH5_BUILT_AS_DYNAMIC_LIB /I\$\{CONDA_PREFIX\}/Library/include /link /LIBPATH:\$\{CONDA_PREFIX\}/Library/lib hdf5.lib" ) else() - set( NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -DH5_BUILT_AS_DYNAMIC_LIB -I\$\{CONDA_PREFIX\}/Library/include/nexus -I\$\{CONDA_PREFIX\}/Library/include -Wl,-rpath,$\{CONDA_PREFIX\}/Library/lib -L\$\{CONDA_PREFIX\}/Library/lib -lNeXus -lhdf5" ) + set( NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -DH5_BUILT_AS_DYNAMIC_LIB -I\$\{CONDA_PREFIX\}/Library/include -Wl,-rpath,$\{CONDA_PREFIX\}/Library/lib -L\$\{CONDA_PREFIX\}/Library/lib -lhdf5" ) endif() endif() if ( NEXUSLIB ) diff --git a/cmake/toolchains/deb32.cmake b/cmake/toolchains/deb32.cmake index c48945d527..fdba187052 100644 --- a/cmake/toolchains/deb32.cmake +++ b/cmake/toolchains/deb32.cmake @@ -12,8 +12,8 @@ set(OACCFLAGS "-fast -Minfo=accel -acc=gpu -gpu=managed -DOPENACC") set(MPIRUN "mpirun") set(NEXUSLIB "/usr/lib") set(NEXUSINCLUDE "/usr/include/nexus") -# Packages are built before libnexus-dev/libhdf5-dev are installed: set the +# Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial -set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lNeXus -lhdf5_serial -I/usr/include/nexus -I/usr/include/hdf5/serial") +set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") set(MCCODE_CFLAGS "-fno-PIC -fPIE -flto -O3 -mtune=native -march=native -fno-math-errno -ftree-vectorize -g -DNDEBUG -D_POSIX_SOURCE -std=c99 -lm") set(EDITOR "gedit") diff --git a/cmake/toolchains/deb64.cmake b/cmake/toolchains/deb64.cmake index 95e4710381..2fdeb49f44 100644 --- a/cmake/toolchains/deb64.cmake +++ b/cmake/toolchains/deb64.cmake @@ -12,8 +12,8 @@ set(MPILIB "mpi") set(MPIRUN "mpirun") set(NEXUSLIB "/usr/lib") set(NEXUSINCLUDE "/usr/include/nexus") -# Packages are built before libnexus-dev/libhdf5-dev are installed: set the +# Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial -set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lNeXus -lhdf5_serial -I/usr/include/nexus -I/usr/include/hdf5/serial") +set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") set(MCCODE_CFLAGS "-fno-PIC -fPIE -flto -O3 -mtune=native -march=native -fno-math-errno -ftree-vectorize -g -DNDEBUG -D_POSIX_SOURCE -std=c99 -lm") set(EDITOR "gedit") diff --git a/cmake/toolchains/deb64arm.cmake b/cmake/toolchains/deb64arm.cmake index 9966e2034b..6bc400f5a7 100644 --- a/cmake/toolchains/deb64arm.cmake +++ b/cmake/toolchains/deb64arm.cmake @@ -12,8 +12,8 @@ set(OACCFLAGS "-fast -Minfo=accel -acc=gpu -gpu=managed -DOPENACC") set(MPIRUN "mpirun") set(NEXUSLIB "/usr/lib") set(NEXUSINCLUDE "/usr/include/nexus") -# Packages are built before libnexus-dev/libhdf5-dev are installed: set the +# Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial -set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lNeXus -lhdf5_serial -I/usr/include/nexus -I/usr/include/hdf5/serial") +set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") set(MCCODE_CFLAGS "-fno-PIC -fPIE -flto -O3 -mtune=native -march=native -fno-math-errno -ftree-vectorize -g -DNDEBUG -D_POSIX_SOURCE -std=c99 -lm") set(EDITOR "gedit") diff --git a/mcstas-comps/CMakeLists.txt b/mcstas-comps/CMakeLists.txt index a3be3c2fab..b9524a886a 100644 --- a/mcstas-comps/CMakeLists.txt +++ b/mcstas-comps/CMakeLists.txt @@ -13,9 +13,9 @@ include(External) option( ENABLE_CIF2HKL "Build Third Party code cif2hkl (fortran)" ON )#TODO: Only enable Fortran if this is on if ( ENABLE_CIF2HKL ) - set(DEBIDEPS "${FLAVOR}, libnexus-dev, libgsl-dev, mcpl (>= 2.2.0), ncrystal (>= 4.2.0)") + set(DEBIDEPS "${FLAVOR}, libhdf5-dev, libgsl-dev, mcpl (>= 2.2.0), ncrystal (>= 4.2.0)") else() - set(DEBIDEPS "${FLAVOR}, libnexus-dev, libgsl-dev, mcpl (>= 2.2.0), ncrystal (>= 4.2.0), cif2hkl (>= 1.5.1)") + set(DEBIDEPS "${FLAVOR}, libhdf5-dev, libgsl-dev, mcpl (>= 2.2.0), ncrystal (>= 4.2.0), cif2hkl (>= 1.5.1)") endif() set(WORK "${PROJECT_BINARY_DIR}/work") diff --git a/mcxtrace-comps/CMakeLists.txt b/mcxtrace-comps/CMakeLists.txt index 81a6e7907f..b7a9d4598e 100644 --- a/mcxtrace-comps/CMakeLists.txt +++ b/mcxtrace-comps/CMakeLists.txt @@ -27,9 +27,9 @@ set(CPACK_PACKAGE_VERSION_PATCH "${MCCODE_PATCH}") # (as in mcstas-comps). ENABLE_CIF2HKL is only declared further down, so # read it as a cache variable here (default ON, as in its option()). if ( NOT DEFINED ENABLE_CIF2HKL OR ENABLE_CIF2HKL ) - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${FLAVOR}, libnexus-dev, libxrl-dev, libgsl-dev, mcpl (>= 2.2.0)") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${FLAVOR}, libhdf5-dev, libxrl-dev, libgsl-dev, mcpl (>= 2.2.0)") else() - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${FLAVOR}, libnexus-dev, libxrl-dev, libgsl-dev, mcpl (>= 2.2.0), cif2hkl (>= 1.5.1)") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${FLAVOR}, libhdf5-dev, libxrl-dev, libgsl-dev, mcpl (>= 2.2.0), cif2hkl (>= 1.5.1)") endif() set(CPACK_DEBIAN_PACKAGE_REPLACES "${FLAVOR}-comps-3.5.1") From 0caacc367e91bc38ebe928a74d251c655b358a7a Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Thu, 22 Oct 2026 17:49:26 +0200 Subject: [PATCH 3/5] mccode-r.h.in: drop _getcomp_type/_getcomp_rotation prototypes They belong to the UB_matrix work on ub-matrix-injector and were picked up here by accident from a shared working tree. Nothing on this branch uses them. Co-Authored-By: Claude Opus 5.5 --- common/lib/share/mccode-r.h.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/lib/share/mccode-r.h.in b/common/lib/share/mccode-r.h.in index 0754c42c4d..bd5a1c72aa 100644 --- a/common/lib/share/mccode-r.h.in +++ b/common/lib/share/mccode-r.h.in @@ -535,10 +535,6 @@ int any_set(int n, ...); void* _getvar_parameters(char* compname); int _getcomp_index(char* compname); -/* type name and absolute rotation of a named instance (cogen'd); - * MCCODE_COMP_TYPE_ is defined for every component type present */ -char* _getcomp_type(char* compname); -int _getcomp_rotation(char* compname, Rotation r); /* Note: The two-stage approach to COMP_GETPAR is NOT redundant; without it, * after #define C sample, COMP_GETPAR(C,x) would refer to component C, not to From cd1761b3ba6498be8f305a521260a50f3ae5ff6a Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Thu, 22 Oct 2026 18:23:06 +0200 Subject: [PATCH 4/5] Add NeXus output regression test (McStas and McXtrace) Test_NeXus_output.instr with test_nexus_output.py, run by the basictest workflows after the MPI list test. It runs the instrument with --format=NeXus, serial and with MPI, and checks mccode.h5 with h5py: - string values containing '%' are stored as given (instrument parameter and component parameter literal) - all string attributes and data sets are printable (no uninitialised memory, as in the Monitor_nD BINS output) - no debug output on stdout - a monitor file name of 1000 characters does not crash the run - --append adds entry2 to mccode.h5 With the current napi-based writer the first four fail. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/mcstas-basictest.yml | 18 +++ .github/workflows/mcxtrace-basictest.yml | 18 +++ .../Test_NeXus_output/Test_NeXus_output.instr | 72 +++++++++ .../Test_NeXus_output/test_nexus_output.py | 153 ++++++++++++++++++ .../Test_NeXus_output/Test_NeXus_output.instr | 66 ++++++++ .../Test_NeXus_output/test_nexus_output.py | 153 ++++++++++++++++++ 6 files changed, 480 insertions(+) create mode 100644 mcstas-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr create mode 100644 mcstas-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py create mode 100644 mcxtrace-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr create mode 100644 mcxtrace-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py diff --git a/.github/workflows/mcstas-basictest.yml b/.github/workflows/mcstas-basictest.yml index 4466389e5e..8d91c829f9 100644 --- a/.github/workflows/mcstas-basictest.yml +++ b/.github/workflows/mcstas-basictest.yml @@ -488,6 +488,24 @@ jobs: ${PY} -m pip install h5py || ${PY} -m pip install h5py --break-system-packages || true ${PY} src/mcstas-comps/examples/Tests_monitors/Test_Monitor_nD_list_MPI/test_mpi_lists.py --np 2 --mcrun mcrun${SUFFIX} --workdir run_mpilists + - name: NeXus output regression test + id: nexusoutputtest + # classic only: the antlr code generator embeds a released runtime + if: matrix.cogen == 'classic' && matrix.nmpi != '0' + run: | + set -e + set -u + set -x + if [ "$RUNNER_OS" == "Windows" ]; + then + export SUFFIX=".bat" + else + export SUFFIX="" + fi + PY=$(command -v python3 || command -v python) + ${PY} -m pip install h5py || ${PY} -m pip install h5py --break-system-packages + ${PY} src/mcstas-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py --np 2 --mcrun mcrun${SUFFIX} --workdir run_nexusoutput + - name: Check for modified instruments id: instr-test run: | diff --git a/.github/workflows/mcxtrace-basictest.yml b/.github/workflows/mcxtrace-basictest.yml index ef324179cc..be825d0263 100644 --- a/.github/workflows/mcxtrace-basictest.yml +++ b/.github/workflows/mcxtrace-basictest.yml @@ -486,6 +486,24 @@ jobs: ${PY} -m pip install h5py || ${PY} -m pip install h5py --break-system-packages || true ${PY} src/mcxtrace-comps/examples/Tests/Test_Monitor_nD_list_MPI/test_mpi_lists.py --np 2 --mcrun mxrun${SUFFIX} --workdir run_mpilists + - name: NeXus output regression test + id: nexusoutputtest + # classic only: the antlr code generator embeds a released runtime + if: matrix.cogen == 'classic' && matrix.nmpi != '0' + run: | + set -e + set -u + set -x + if [ "$RUNNER_OS" == "Windows" ]; + then + export SUFFIX=".bat" + else + export SUFFIX="" + fi + PY=$(command -v python3 || command -v python) + ${PY} -m pip install h5py || ${PY} -m pip install h5py --break-system-packages + ${PY} src/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py --np 2 --mcrun mxrun${SUFFIX} --workdir run_nexusoutput + - name: Check for modified instruments id: instr-test run: | diff --git a/mcstas-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr b/mcstas-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr new file mode 100644 index 0000000000..ace596f76d --- /dev/null +++ b/mcstas-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr @@ -0,0 +1,72 @@ +/******************************************************************************* +* Instrument: Test_NeXus_output +* +* %I +* Written by: Peter Willendrup +* Date: October 2026 +* Origin: DTU +* %INSTRUMENT_SITE: Tests_other +* +* Regression test of the NeXus writer of the McCode runtime. +* +* %D +* Monitors of every NeXus output kind (0D, 1D, 2D, Monitor_nD histogram, +* Monitor_nD BINS maps of rank 2 and 3, and an event list) for +* test_nexus_output.py, which runs this instrument with --format=NeXus, +* serial and with MPI, and checks mccode.h5 with h5py: +* +* - string values containing '%' are stored as given (instrument parameter +* 'tag', component parameter literal 'filename="f%%1"') +* - all string attributes and data sets are printable text +* (no uninitialised memory, e.g. in the Monitor_nD BINS output) +* - no debug output on stdout +* - a monitor with a very long file name ('longname', about 1000 +* characters) does not crash the simulation or lose other monitors +* - a second run with --append adds entry2 to mccode.h5 +* +* The instrument also runs in the default (McCode) format. +* +* %P +* tag: [str] String stored in the NeXus file, containing '%' +* longname: [str] File name of the 'lmon' monitor +* +* %L +* test_nexus_output.py next to this instrument. +* +* %E +*******************************************************************************/ +DEFINE INSTRUMENT Test_NeXus_output(string tag="50%% done", string longname="lmon") + +TRACE + +COMPONENT Origin = Progress_bar() +AT (0, 0, 0) ABSOLUTE + +COMPONENT source = Source_simple(radius=0.02, dist=1, focus_xw=0.05, focus_yh=0.05, + lambda0=5, dlambda=1) +AT (0, 0, 0) RELATIVE Origin + +COMPONENT m0 = Monitor(xmin=-0.05, xmax=0.05, ymin=-0.05, ymax=0.05) +AT (0, 0, 1) RELATIVE Origin + +COMPONENT lmon = L_monitor(nL=10, filename=longname, xwidth=0.1, yheight=0.1, + Lmin=3, Lmax=7, restore_neutron=1) +AT (0, 0, 1.001) RELATIVE Origin + +COMPONENT psd = PSD_monitor(nx=8, ny=6, filename="f%%1", xwidth=0.1, yheight=0.1, + restore_neutron=1) +AT (0, 0, 1.002) RELATIVE Origin + +COMPONENT nd2 = Monitor_nD(options="x bins=5 y bins=4", xwidth=0.1, yheight=0.1, + filename="nd2", restore_neutron=1, nexus_bins=1) +AT (0, 0, 1.003) RELATIVE Origin + +COMPONENT nd3 = Monitor_nD(options="box x bins=2 y bins=3 z bins=4", xwidth=0.1, + yheight=0.1, zdepth=0.01, filename="nd3", restore_neutron=1, nexus_bins=1) +AT (0, 0, 1.01) RELATIVE Origin + +COMPONENT ndl = Monitor_nD(options="list all x y lambda", xwidth=0.1, yheight=0.1, + filename="ndl", restore_neutron=1) +AT (0, 0, 1.02) RELATIVE Origin + +END diff --git a/mcstas-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py b/mcstas-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py new file mode 100644 index 0000000000..2e13a28289 --- /dev/null +++ b/mcstas-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""Regression test for the NeXus output of the McCode runtime. + +Runs Test_NeXus_output.instr (next to this script; the McStas and McXtrace +copies of this script are identical) with --format=NeXus and checks +mccode.h5 with h5py: + + * string values containing '%' are stored as given: the instrument + parameter 'tag' (attribute and data set in entry1/simulation/Param) and + the literal of a component string parameter (parameters/filename@value) + * every string attribute and string data set is printable text, i.e. no + uninitialised memory is written (Monitor_nD BINS output) + * no debug output ("Received detector from") on stdout + * a monitor with a file name of 1000 characters neither crashes the + simulation nor loses the other monitors of the serial run + * a second run with --append adds entry2 + +The serial run, and with --np > 0 also an MPI run, are checked. + +Usage: python3 test_nexus_output.py [--mcrun mcrun] [--np 2] [--workdir DIR] +Exit status is 0 when all checks pass. +""" +import argparse +import shutil +import subprocess +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +INSTR = HERE / "Test_NeXus_output.instr" +TAG = "50%%done" # stored as given ('%%' only: no conversion that reads a missing argument) +PSD_FILENAME = "f%%1" # psd 'filename' literal in the instrument (quotes removed by the code generator) +PRINTABLE = set(range(32, 127)) | {9, 10, 13} + +failures = [] +serial_data = [] # entry1/data groups of the serial run + + +def check(ok, what): + print(("PASS " if ok else "FAIL ") + what) + if not ok: + failures.append(what) + + +def text(v): + if hasattr(v, "shape") and v.shape: # one-element string data set + v = v[0] + return v.decode("latin-1") if isinstance(v, bytes) else str(v) + + +def mcrun(args, cwd, label): + cmd = [args.mcrun, INSTR.name, "--format=NeXus", "-n", "10000", "-s", "1234"] + label + print("+ " + " ".join(cmd), flush=True) + p = subprocess.run(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, errors="replace", timeout=900) + print(p.stdout[-3000:]) + return p.returncode, p.stdout + + +def open_h5(path, label): + """mccode.h5 opened with h5py, or None (a failed check) when missing or unreadable.""" + import h5py + try: + return h5py.File(path, "r") + except OSError as e: + check(False, f"{label}: {path.name} readable ({e})") + return None + + +def check_strings(f, label): + """Every string attribute and string data set is printable.""" + import h5py + bad = [] + + def visit(name, obj): + for k, v in obj.attrs.items(): + if isinstance(v, (bytes, str)) and set(text(v).encode("latin-1")) - PRINTABLE: + bad.append(f"{obj.name}@{k}={text(v)[:40]!r}") + if isinstance(obj, h5py.Dataset) and obj.dtype.kind == "S": + if set(text(obj[()]).encode("latin-1")) - PRINTABLE: + bad.append(f"{obj.name}={text(obj[()])[:40]!r}") + visit("/", f) + f.visititems(visit) + check(not bad, f"{label}: all strings printable" + ("" if not bad else ": " + "; ".join(bad[:5]))) + + +def check_run(args, cwd, outdir, label, mpi=None): + extra = ["-d", outdir] + ([f"--mpi={mpi}"] if mpi else []) + [f"tag={TAG}"] + rc, out = mcrun(args, cwd, extra) + check(rc == 0, f"{label}: simulation exit status 0 (got {rc})") + check("Received detector from" not in out, f"{label}: no debug output on stdout") + f = open_h5(cwd / outdir / "mccode.h5", label) + if f is None: + return + with f: + param = f["entry1/simulation/Param"] + check(text(param.attrs["tag"]) == TAG, f"{label}: Param@tag == {TAG!r} (got {text(param.attrs['tag'])!r})") + check(text(param["tag"][()]) == TAG, f"{label}: Param/tag == {TAG!r} (got {text(param['tag'][()])!r})") + comps = f["entry1/instrument/components"] + psd = [g for n, g in comps.items() if n.endswith("_psd")][0] + val = text(psd["parameters/filename"].attrs["value"]) + check(val == PSD_FILENAME, f"{label}: psd parameters/filename@value == {PSD_FILENAME!r} (got {val!r})") + if "nexus_bins" in INSTR.read_text(): # McStas Monitor_nD only + bins = [n for c in comps.values() if "output" in c for n in c["output"] if n == "BINS"] + check(len(bins) >= 2, f"{label}: Monitor_nD BINS maps written (got {len(bins)})") + if not serial_data: + serial_data.extend(n for n in f["entry1/data"] if not n.startswith("lmon")) + check_strings(f, label) + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--mcrun", default="mcrun") + ap.add_argument("--np", type=int, default=2, help="MPI processes, 0 to skip the MPI run") + ap.add_argument("--workdir", default="run_nexus_output") + args = ap.parse_args() + import h5py # noqa: F401 - required, fail early + + cwd = Path(args.workdir).resolve() + shutil.rmtree(cwd, ignore_errors=True) + cwd.mkdir(parents=True) + shutil.copy(INSTR, cwd) + + check_run(args, cwd, "serial", "serial") + if args.np > 0: + check_run(args, cwd, "mpi", f"MPI np={args.np}", mpi=args.np) + + # very long monitor file name: 1000 characters (CHAR_BUF_LENGTH is 1024) + longname = "L" * 1000 + rc, out = mcrun(args, cwd, ["-d", "long", f"longname={longname}"]) + check(rc == 0, f"long name: simulation exit status 0 (got {rc})") + f = open_h5(cwd / "long" / "mccode.h5", "long name") + if f is not None: + with f: + data = f["entry1/data"] + for mon in serial_data: + check(mon in data, f"long name: entry1/data/{mon} written") + print("INFO long name: lmon stored:", any(n.startswith("LLLL") for n in data)) + + # second run appended to the same file + rc, out = mcrun(args, cwd, ["-d", "serial", "-a", f"tag={TAG}"]) + check(rc == 0, f"append: simulation exit status 0 (got {rc})") + f = open_h5(cwd / "serial" / "mccode.h5", "append") + if f is not None: + with f: + check("entry2/instrument/components" in f, "append: entry2 written to mccode.h5") + + print(f"\n{len(failures)} check(s) failed" if failures else "\nAll checks passed") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr b/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr new file mode 100644 index 0000000000..e421cf2b45 --- /dev/null +++ b/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/Test_NeXus_output.instr @@ -0,0 +1,66 @@ +/******************************************************************************* +* Instrument: Test_NeXus_output +* +* %I +* Written by: Peter Willendrup +* Date: October 2026 +* Origin: DTU +* %INSTRUMENT_SITE: Tests_other +* +* Regression test of the NeXus writer of the McCode runtime. +* +* %D +* Monitors of the NeXus output kinds (0D, 1D, 2D, Monitor_nD histogram and an +* event list) for test_nexus_output.py, which runs this instrument with +* --format=NeXus, serial and with MPI, and checks mccode.h5 with h5py: +* +* - string values containing '%' are stored as given (instrument parameter +* 'tag', component parameter literal 'filename="f%%1"') +* - all string attributes and data sets are printable text +* - no debug output on stdout +* - a monitor with a very long file name ('longname', about 1000 +* characters) does not crash the simulation or lose other monitors +* - a second run with --append adds entry2 to mccode.h5 +* +* The instrument also runs in the default (McCode) format. +* +* %P +* tag: [str] String stored in the NeXus file, containing '%' +* longname: [str] File name of the 'lmon' monitor +* +* %L +* test_nexus_output.py next to this instrument. +* +* %E +*******************************************************************************/ +DEFINE INSTRUMENT Test_NeXus_output(string tag="50%% done", string longname="lmon") + +TRACE + +COMPONENT Origin = Progress_bar() +AT (0, 0, 0) ABSOLUTE + +COMPONENT source = Source_flat(radius=0.001, dist=1, focus_xw=0.05, focus_yh=0.05, + E0=12, dE=1) +AT (0, 0, 0) RELATIVE Origin + +COMPONENT m0 = Monitor(xwidth=0.1, yheight=0.1, restore_xray=1) +AT (0, 0, 1) RELATIVE Origin + +COMPONENT lmon = E_monitor(nE=10, filename=longname, xwidth=0.1, yheight=0.1, + Emin=10, Emax=14, restore_xray=1) +AT (0, 0, 1.001) RELATIVE Origin + +COMPONENT psd = PSD_monitor(nx=8, ny=6, filename="f%%1", xwidth=0.1, yheight=0.1, + restore_xray=1) +AT (0, 0, 1.002) RELATIVE Origin + +COMPONENT nd2 = Monitor_nD(options="x bins=5 y bins=4", xwidth=0.1, yheight=0.1, + filename="nd2", restore_xray=1) +AT (0, 0, 1.003) RELATIVE Origin + +COMPONENT ndl = Monitor_nD(options="list all x y energy", xwidth=0.1, yheight=0.1, + filename="ndl", restore_xray=1) +AT (0, 0, 1.02) RELATIVE Origin + +END diff --git a/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py b/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py new file mode 100644 index 0000000000..2e13a28289 --- /dev/null +++ b/mcxtrace-comps/examples/Tests_other/Test_NeXus_output/test_nexus_output.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""Regression test for the NeXus output of the McCode runtime. + +Runs Test_NeXus_output.instr (next to this script; the McStas and McXtrace +copies of this script are identical) with --format=NeXus and checks +mccode.h5 with h5py: + + * string values containing '%' are stored as given: the instrument + parameter 'tag' (attribute and data set in entry1/simulation/Param) and + the literal of a component string parameter (parameters/filename@value) + * every string attribute and string data set is printable text, i.e. no + uninitialised memory is written (Monitor_nD BINS output) + * no debug output ("Received detector from") on stdout + * a monitor with a file name of 1000 characters neither crashes the + simulation nor loses the other monitors of the serial run + * a second run with --append adds entry2 + +The serial run, and with --np > 0 also an MPI run, are checked. + +Usage: python3 test_nexus_output.py [--mcrun mcrun] [--np 2] [--workdir DIR] +Exit status is 0 when all checks pass. +""" +import argparse +import shutil +import subprocess +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +INSTR = HERE / "Test_NeXus_output.instr" +TAG = "50%%done" # stored as given ('%%' only: no conversion that reads a missing argument) +PSD_FILENAME = "f%%1" # psd 'filename' literal in the instrument (quotes removed by the code generator) +PRINTABLE = set(range(32, 127)) | {9, 10, 13} + +failures = [] +serial_data = [] # entry1/data groups of the serial run + + +def check(ok, what): + print(("PASS " if ok else "FAIL ") + what) + if not ok: + failures.append(what) + + +def text(v): + if hasattr(v, "shape") and v.shape: # one-element string data set + v = v[0] + return v.decode("latin-1") if isinstance(v, bytes) else str(v) + + +def mcrun(args, cwd, label): + cmd = [args.mcrun, INSTR.name, "--format=NeXus", "-n", "10000", "-s", "1234"] + label + print("+ " + " ".join(cmd), flush=True) + p = subprocess.run(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, errors="replace", timeout=900) + print(p.stdout[-3000:]) + return p.returncode, p.stdout + + +def open_h5(path, label): + """mccode.h5 opened with h5py, or None (a failed check) when missing or unreadable.""" + import h5py + try: + return h5py.File(path, "r") + except OSError as e: + check(False, f"{label}: {path.name} readable ({e})") + return None + + +def check_strings(f, label): + """Every string attribute and string data set is printable.""" + import h5py + bad = [] + + def visit(name, obj): + for k, v in obj.attrs.items(): + if isinstance(v, (bytes, str)) and set(text(v).encode("latin-1")) - PRINTABLE: + bad.append(f"{obj.name}@{k}={text(v)[:40]!r}") + if isinstance(obj, h5py.Dataset) and obj.dtype.kind == "S": + if set(text(obj[()]).encode("latin-1")) - PRINTABLE: + bad.append(f"{obj.name}={text(obj[()])[:40]!r}") + visit("/", f) + f.visititems(visit) + check(not bad, f"{label}: all strings printable" + ("" if not bad else ": " + "; ".join(bad[:5]))) + + +def check_run(args, cwd, outdir, label, mpi=None): + extra = ["-d", outdir] + ([f"--mpi={mpi}"] if mpi else []) + [f"tag={TAG}"] + rc, out = mcrun(args, cwd, extra) + check(rc == 0, f"{label}: simulation exit status 0 (got {rc})") + check("Received detector from" not in out, f"{label}: no debug output on stdout") + f = open_h5(cwd / outdir / "mccode.h5", label) + if f is None: + return + with f: + param = f["entry1/simulation/Param"] + check(text(param.attrs["tag"]) == TAG, f"{label}: Param@tag == {TAG!r} (got {text(param.attrs['tag'])!r})") + check(text(param["tag"][()]) == TAG, f"{label}: Param/tag == {TAG!r} (got {text(param['tag'][()])!r})") + comps = f["entry1/instrument/components"] + psd = [g for n, g in comps.items() if n.endswith("_psd")][0] + val = text(psd["parameters/filename"].attrs["value"]) + check(val == PSD_FILENAME, f"{label}: psd parameters/filename@value == {PSD_FILENAME!r} (got {val!r})") + if "nexus_bins" in INSTR.read_text(): # McStas Monitor_nD only + bins = [n for c in comps.values() if "output" in c for n in c["output"] if n == "BINS"] + check(len(bins) >= 2, f"{label}: Monitor_nD BINS maps written (got {len(bins)})") + if not serial_data: + serial_data.extend(n for n in f["entry1/data"] if not n.startswith("lmon")) + check_strings(f, label) + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--mcrun", default="mcrun") + ap.add_argument("--np", type=int, default=2, help="MPI processes, 0 to skip the MPI run") + ap.add_argument("--workdir", default="run_nexus_output") + args = ap.parse_args() + import h5py # noqa: F401 - required, fail early + + cwd = Path(args.workdir).resolve() + shutil.rmtree(cwd, ignore_errors=True) + cwd.mkdir(parents=True) + shutil.copy(INSTR, cwd) + + check_run(args, cwd, "serial", "serial") + if args.np > 0: + check_run(args, cwd, "mpi", f"MPI np={args.np}", mpi=args.np) + + # very long monitor file name: 1000 characters (CHAR_BUF_LENGTH is 1024) + longname = "L" * 1000 + rc, out = mcrun(args, cwd, ["-d", "long", f"longname={longname}"]) + check(rc == 0, f"long name: simulation exit status 0 (got {rc})") + f = open_h5(cwd / "long" / "mccode.h5", "long name") + if f is not None: + with f: + data = f["entry1/data"] + for mon in serial_data: + check(mon in data, f"long name: entry1/data/{mon} written") + print("INFO long name: lmon stored:", any(n.startswith("LLLL") for n in data)) + + # second run appended to the same file + rc, out = mcrun(args, cwd, ["-d", "serial", "-a", f"tag={TAG}"]) + check(rc == 0, f"append: simulation exit status 0 (got {rc})") + f = open_h5(cwd / "serial" / "mccode.h5", "append") + if f is not None: + with f: + check("entry2/instrument/components" in f, "append: entry2 written to mccode.h5") + + print(f"\n{len(failures)} check(s) failed" if failures else "\nAll checks passed") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 1318d48ff5e50303ddd8d704adfb090bd419c459 Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Tue, 27 Oct 2026 11:03:20 +0100 Subject: [PATCH 5/5] Drop libNeXus from dependencies, devel tools, CI, toolchains and docs NeXus output needs HDF5 only since the native HDF5 writer: - conda: hdf5 instead of libnexus in mccode-create-conda-yml (used by the conda CI jobs, so these now build without libNeXus present); drop 'nexus' from environment.yml and the Windows meta-pkg environments (they already list hdf5) - remove 3rdparty/nexus-package (it packaged libNeXus) with its sync.sh clone and the GhostBSD mkdist line - mccode-build-conda: no -DNEXUSLIB/-DNEXUSINCLUDE, the conda NEXUSFLAGS already use $CONDA_PREFIX/include and lib - basictest workflows: install libhdf5-dev (Linux) / hdf5 (Homebrew) instead of libnexus-dev / nexusformat, no -DNEXUSLIB/-DNEXUSINCLUDE - toolchains: Debian ones set NEXUSFLAGS fully, drop the unused NEXUSLIB/NEXUSINCLUDE; macOS/MinGW NEXUSINCLUDE points at the conda include dir holding hdf5.h instead of include/nexus - code generator: hint '-DUSE_NEXUS -lhdf5' instead of '-lNeXus' - manuals (McStas, McXtrace), ILL_H16_IN5_Mantid and the macOS dependency README: HDF5 instead of libNeXus, -lhdf5 instead of -lNeXus Co-Authored-By: Claude Opus 5.5 --- .github/workflows/mcstas-basictest.yml | 5 +-- .github/workflows/mcxtrace-basictest.yml | 5 +-- 3rdparty/nexus-package/CMakeLists.txt | 44 ------------------- 3rdparty/nexus-package/cmake | 1 - 3rdparty/nexus-package/nexus-code | 1 - 3rdparty/sync.sh | 10 ----- buildscripts/build_pkg_ghostbsd | 1 - cmake/Modules/PlatformDefaults.cmake | 2 +- cmake/toolchains/deb32.cmake | 2 - cmake/toolchains/deb64.cmake | 2 - cmake/toolchains/deb64arm.cmake | 2 - cmake/toolchains/mac-silicon.cmake | 2 +- cmake/toolchains/mac.cmake | 2 +- cmake/toolchains/mingw64.cmake | 4 +- devel/bin/mccode-build-conda | 6 --- devel/bin/mccode-create-conda-yml | 2 +- docs/manuals/mcstas/kernel.tex | 5 +-- docs/manuals/mcstas/links_othercode.tex | 5 +-- docs/manuals/mcstas/running.tex | 4 +- docs/manuals/mcxtrace/kernel.tex | 4 +- docs/manuals/mcxtrace/running.tex | 4 +- environment.yml | 1 - mccode/src/cogen.c.in | 4 +- .../ILL_H16_IN5_Mantid.instr | 2 +- .../Mantid/ILL_H16_IN5_Mantid/README.md | 2 +- meta-pkgs/windows/environment.yml | 1 - meta-pkgs/windows/mcstas-environment.yml | 1 - meta-pkgs/windows/mcxtrace-environment.yml | 1 - support/MacOSX/Check-dependencies/README.TXT | 6 +-- 29 files changed, 27 insertions(+), 104 deletions(-) delete mode 100644 3rdparty/nexus-package/CMakeLists.txt delete mode 120000 3rdparty/nexus-package/cmake delete mode 120000 3rdparty/nexus-package/nexus-code diff --git a/.github/workflows/mcstas-basictest.yml b/.github/workflows/mcstas-basictest.yml index 9d256dc9a8..f51abb26fe 100644 --- a/.github/workflows/mcstas-basictest.yml +++ b/.github/workflows/mcstas-basictest.yml @@ -86,13 +86,13 @@ jobs: id: setup-linux if: matrix.method == 'source' && runner.os == 'Linux' run: | - sudo apt -y install libgsl-dev libnexus-dev + sudo apt -y install libgsl-dev libhdf5-dev - name: Setup (macOS, source build) id: setup-macos if: matrix.method == 'source' && runner.os == 'macOS' run: | - brew install bison flex gfortran gsl homebrew/science/nexusformat + brew install bison flex gfortran gsl hdf5 - name: Setup (Windows, source build) id: setup-Windows @@ -161,7 +161,6 @@ jobs: if [ "$RUNNER_OS" == "macOS" ] && [ -f "/opt/homebrew/opt/flex/bin/flex" ]; then export HOMEBRW="/opt/homebrew/opt"; export GSLFLAGS="-lgsl -lgslcblas -L/opt/homebrew/lib -I/opt/homebrew/include"; fi if [ "$RUNNER_OS" == "macOS" ] && [ -f "/usr/local/opt/flex/bin/flex" ]; then export HOMEBRW="/usr/local/opt"; export GSLFLAGS="-lgsl -lgslcblas -L/usr/local/lib -I/usr/local/include"; fi if [ "$RUNNER_OS" == "macOS" ]; then export EXTRA_ARGS_FOR_CMAKE="-DBISON_EXECUTABLE=${HOMEBRW}/bison/bin/bison -DFLEX_EXECUTABLE=${HOMEBRW}/flex/bin/flex -DCMAKE_Fortran_COMPILER=${HOMEBRW}/gfortran/bin/gfortran"; fi - if [ "$RUNNER_OS" == "Linux" ]; then export EXTRA_ARGS_FOR_CMAKE="-DNEXUSLIB=/usr/lib -DNEXUSINCLUDE=/usr/include/nexus"; fi if [ "$RUNNER_OS" == "Windows" ]; then export MPIINC=`cygpath -m -s "${MSMPI_INC}"`; export MPILIB=`cygpath -m -s "${MSMPI_LIB64}"`; export EXTRA_ARGS_FOR_CMAKE="-DMPILIBDIR=${MPILIB} -DMPIINCLUDEDIR=${MPIINC}"; fi cmake \ -DCMAKE_INSTALL_PREFIX=../install_mcstas \ diff --git a/.github/workflows/mcxtrace-basictest.yml b/.github/workflows/mcxtrace-basictest.yml index 14edd63075..b447a0832d 100644 --- a/.github/workflows/mcxtrace-basictest.yml +++ b/.github/workflows/mcxtrace-basictest.yml @@ -86,7 +86,7 @@ jobs: id: setup-linux if: matrix.method == 'source' && runner.os == 'Linux' run: | - sudo apt -y install libgsl-dev libxrl-dev libnexus-dev + sudo apt -y install libgsl-dev libxrl-dev libhdf5-dev - name: Setup (macOS, source build) id: setup-macos @@ -95,7 +95,7 @@ jobs: # cause build-issues. Will soon port basictest scripts to conda # for Windows/Linux run: | - brew install bison flex gfortran gsl + brew install bison flex gfortran gsl hdf5 #if [ "${{ matrix.os }}" != "macos-15" ]; #then # brew tap tschoonj/tap @@ -169,7 +169,6 @@ jobs: if [ "$RUNNER_OS" == "macOS" ] && [ -f "/opt/homebrew/opt/flex/bin/flex" ]; then export HOMEBRW="/opt/homebrew/opt"; export GSLFLAGS="-lgsl -lgslcblas -L/opt/homebrew/lib -I/opt/homebrew/include"; export XRLFLAGS="-lxrl -L/opt/homebrew/lib -I/opt/homebrew/include"; fi if [ "$RUNNER_OS" == "macOS" ] && [ -f "/usr/local/opt/flex/bin/flex" ]; then export HOMEBRW="/usr/local/opt"; export GSLFLAGS="-lgsl -lgslcblas -L/usr/local/lib -I/usr/local/include"; export XRLFLAGS="-lxrl -L/usr/local/lib -I/usr/local/include"; fi if [ "$RUNNER_OS" == "macOS" ]; then export EXTRA_ARGS_FOR_CMAKE="-DBISON_EXECUTABLE=${HOMEBRW}/bison/bin/bison -DFLEX_EXECUTABLE=${HOMEBRW}/flex/bin/flex -DCMAKE_Fortran_COMPILER=${HOMEBRW}/gfortran/bin/gfortran"; fi - if [ "$RUNNER_OS" == "Linux" ]; then export EXTRA_ARGS_FOR_CMAKE="-DNEXUSLIB=/usr/lib -DNEXUSINCLUDE=/usr/include/nexus"; fi if [ "$RUNNER_OS" == "Windows" ]; then export MPIINC=`cygpath -m -s "${MSMPI_INC}"`; export MPILIB=`cygpath -m -s "${MSMPI_LIB64}"`; export EXTRA_ARGS_FOR_CMAKE="-DMPILIBDIR=${MPILIB} -DMPIINCLUDEDIR=${MPIINC}"; fi cmake \ -DCMAKE_INSTALL_PREFIX=../install_mcxtrace \ diff --git a/3rdparty/nexus-package/CMakeLists.txt b/3rdparty/nexus-package/CMakeLists.txt deleted file mode 100644 index c9d1aa8732..0000000000 --- a/3rdparty/nexus-package/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -cmake_minimum_required(VERSION 3.17) - -# This will install NeXus components -project(NeXus_wrapper) - -message(STATUS "Configure ${CMAKE_PROJECT_NAME} ${FLAVOR} ${PROJECT_NAME}") - -# Choose between McStas or McXtrace -option(BUILD_MCSTAS "Choose the McStas flavor" off) -option(BUILD_MCXTRACE "Choose the McXtrace flavor (trumps BUILD_MCSTAS)" Off) - -# Set module path -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") -# Set McCode values (from mkdist or defaults) -include(MCUtil) - -if (${BUILD_MCXTRACE}) - set(FLAVOR "mcxtrace") -elseif(${BUILD_MCSTAS}) - set(FLAVOR "mcstas") -else() - message(FATAL_ERROR "No flavor specified!\n" - "Please use either -DBUILD_MCSTAS=1 or -DBUILD_MCXTRACE=1") -endif() -setupMCCODE("${FLAVOR}") - -set(CPACK_PACKAGE_NAME "${FLAVOR}-nexus-${MCCODE_VERSION}") -set(CPACK_PACKAGE_CONTACT "pkwi@fysik.dtu.dk") -set(CPACK_PACKAGE_VERSION "0.0.1") -set(CPACK_PACKAGE_VERSION_MAJOR "0.0") -set(CPACK_PACKAGE_VERSION_MINOR "1") -set(CPACK_NSIS_PACKAGE_NAME "NeXus 0.0.1") -set(CPACK_NSIS_DISPLAY_NAME "NeXus 0.0.1") - -# NSIS -set(NSIS_NAME "${CPACK_PACKAGE_NAME}") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${NSIS_NAME}") - -include(CPack) - -set(WORK "${PROJECT_BINARY_DIR}/work") - -add_subdirectory(nexus-code) - diff --git a/3rdparty/nexus-package/cmake b/3rdparty/nexus-package/cmake deleted file mode 120000 index c06bb027c6..0000000000 --- a/3rdparty/nexus-package/cmake +++ /dev/null @@ -1 +0,0 @@ -../../cmake/ \ No newline at end of file diff --git a/3rdparty/nexus-package/nexus-code b/3rdparty/nexus-package/nexus-code deleted file mode 120000 index 911aa9caa1..0000000000 --- a/3rdparty/nexus-package/nexus-code +++ /dev/null @@ -1 +0,0 @@ -../nexus-code/ \ No newline at end of file diff --git a/3rdparty/sync.sh b/3rdparty/sync.sh index 2ce8b67cce..102d4c9a00 100755 --- a/3rdparty/sync.sh +++ b/3rdparty/sync.sh @@ -27,16 +27,6 @@ else git clone https://github.com/mctools/ncrystal.git fi -if [ -d nexus-code ]; then - echo Updating existing NeXus clone - cd nexus-code - git pull - cd .. -else - echo Cloning NeXus - git clone https://github.com/nexusformat/code nexus-code -fi - if [ -d xraylib ]; then echo Updating existing xraylib clone cd xraylib diff --git a/buildscripts/build_pkg_ghostbsd b/buildscripts/build_pkg_ghostbsd index b907c52c78..b58706f9ed 100755 --- a/buildscripts/build_pkg_ghostbsd +++ b/buildscripts/build_pkg_ghostbsd @@ -39,5 +39,4 @@ export MCINSTALL_PREFIX=/usr/local ./mkdist mcstas-ncrystal ${1} 3rdparty/ncrystal-package "" ghostbsd "" -- pkg ./mkdist mcstas-mcpl ${1} 3rdparty/mcpl-package "" ghostbsd "" -- pkg -./mkdist mcstas-nexus ${1} 3rdparty/nexus-package "" ghostbsd "" -- pkg diff --git a/cmake/Modules/PlatformDefaults.cmake b/cmake/Modules/PlatformDefaults.cmake index e275524a5e..184ef84908 100644 --- a/cmake/Modules/PlatformDefaults.cmake +++ b/cmake/Modules/PlatformDefaults.cmake @@ -223,7 +223,7 @@ function( detect_platform_variables resultvarname ) provide_var( MPICC ) - #NeXus-flags: + #NeXus-flags: NeXus output is written with HDF5 (NEXUSLIB/NEXUSINCLUDE: where libhdf5 and hdf5.h are) if ( NOT NEXUSFLAGS ) set(NEXUSFLAGS "-DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5") if ( MCCODE_BUILD_CONDA_PKG ) diff --git a/cmake/toolchains/deb32.cmake b/cmake/toolchains/deb32.cmake index fdba187052..009d646332 100644 --- a/cmake/toolchains/deb32.cmake +++ b/cmake/toolchains/deb32.cmake @@ -10,8 +10,6 @@ set(MPICC "mpicc") set(MPILIB "mpi") set(OACCFLAGS "-fast -Minfo=accel -acc=gpu -gpu=managed -DOPENACC") set(MPIRUN "mpirun") -set(NEXUSLIB "/usr/lib") -set(NEXUSINCLUDE "/usr/include/nexus") # Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") diff --git a/cmake/toolchains/deb64.cmake b/cmake/toolchains/deb64.cmake index 2fdeb49f44..908e6a2498 100644 --- a/cmake/toolchains/deb64.cmake +++ b/cmake/toolchains/deb64.cmake @@ -10,8 +10,6 @@ set(MPICC "mpicc") set(OACCFLAGS "-fast -Minfo=accel -acc=gpu -gpu=managed -DOPENACC") set(MPILIB "mpi") set(MPIRUN "mpirun") -set(NEXUSLIB "/usr/lib") -set(NEXUSINCLUDE "/usr/include/nexus") # Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") diff --git a/cmake/toolchains/deb64arm.cmake b/cmake/toolchains/deb64arm.cmake index 6bc400f5a7..d761dcd81a 100644 --- a/cmake/toolchains/deb64arm.cmake +++ b/cmake/toolchains/deb64arm.cmake @@ -10,8 +10,6 @@ set(MPICC "mpicc") set(MPILIB "mpi") set(OACCFLAGS "-fast -Minfo=accel -acc=gpu -gpu=managed -DOPENACC") set(MPIRUN "mpirun") -set(NEXUSLIB "/usr/lib") -set(NEXUSINCLUDE "/usr/include/nexus") # Packages are built before libhdf5-dev is installed: set the # full flags. Debian's serial HDF5 is in /usr/include/hdf5/serial, linked as libhdf5_serial set(NEXUSFLAGS "-Wl,-rpath,/usr/lib -L/usr/lib -DUSE_NEXUS -DNX_COMPRESSION=NX_COMP_LZW_LVL1 -lhdf5_serial -I/usr/include/hdf5/serial") diff --git a/cmake/toolchains/mac-silicon.cmake b/cmake/toolchains/mac-silicon.cmake index 8cbb8b05ce..ddf84297b4 100644 --- a/cmake/toolchains/mac-silicon.cmake +++ b/cmake/toolchains/mac-silicon.cmake @@ -16,6 +16,6 @@ set(XRLFLAGS "-IGETPATH(miniconda3/include) -Wl,-rpath,GETPATH(miniconda3/lib) - set(MPIRUN "mpirun") set(MPILIB "mpi") set(NEXUSLIB "GETPATH(miniconda3/lib)") -set(NEXUSINCLUDE "GETPATH(miniconda3/include/nexus)") +set(NEXUSINCLUDE "GETPATH(miniconda3/include)") set(MCCODE_CFLAGS "-g -O2 -DNDEBUG -lm -std=c99") set(EDITOR "open") diff --git a/cmake/toolchains/mac.cmake b/cmake/toolchains/mac.cmake index eeb7a5105f..fff151f297 100644 --- a/cmake/toolchains/mac.cmake +++ b/cmake/toolchains/mac.cmake @@ -16,6 +16,6 @@ set(XRLFLAGS "-IGETPATH(miniconda3/include) -Wl,-rpath,GETPATH(miniconda3/lib) - set(MPIRUN "mpirun") set(MPILIB "mpi") set(NEXUSLIB "GETPATH(miniconda3/lib)") -set(NEXUSINCLUDE "GETPATH(miniconda3/include/nexus)") +set(NEXUSINCLUDE "GETPATH(miniconda3/include)") set(MCCODE_CFLAGS "-g -O2 -DNDEBUG -lm -std=c99") set(EDITOR "open") diff --git a/cmake/toolchains/mingw64.cmake b/cmake/toolchains/mingw64.cmake index 1e82ea2522..a2824c6088 100644 --- a/cmake/toolchains/mingw64.cmake +++ b/cmake/toolchains/mingw64.cmake @@ -41,8 +41,8 @@ set(MPILIB "msmpi") set(MCCODE_CFLAGS "-flto -O3 -mtune=native -fno-math-errno -ftree-vectorize -g -DNDEBUG -D_POSIX_SOURCE -std=c99 -lm") set(EDITOR "start") -# NeXus location defaults -set(NEXUSINCLUDE "GETPATH(../miniconda3/Library/include/nexus)") +# HDF5 location for NeXus output +set(NEXUSINCLUDE "GETPATH(../miniconda3/Library/include)") set(NEXUSLIB "GETPATH(../miniconda3/Library/lib)") # HDFviewer diff --git a/devel/bin/mccode-build-conda b/devel/bin/mccode-build-conda index 660c024027..4d47a7304a 100755 --- a/devel/bin/mccode-build-conda +++ b/devel/bin/mccode-build-conda @@ -53,12 +53,6 @@ def mccode_build_conda ( cfg ): extra_args = [] if ( cfg.is_unix ): - nexus_incdir = conda_prefix.joinpath('include/nexus') - nexus_libdir = conda_prefix.joinpath('lib') - if nexus_libdir.is_dir(): - extra_args.append( f'-DNEXUSLIB={nexus_libdir}' ) - if nexus_incdir.is_dir(): - extra_args.append( f'-DNEXUSINCLUDE={nexus_incdir}' ) GENERATOR = 'Unix Makefiles' if ( cfg.is_win ): diff --git a/devel/bin/mccode-create-conda-yml b/devel/bin/mccode-create-conda-yml index 17a4ec81ff..bdd41375eb 100755 --- a/devel/bin/mccode-create-conda-yml +++ b/devel/bin/mccode-create-conda-yml @@ -41,7 +41,7 @@ def create_deplist( cfg ): if cfg.is_win: deps += ['m2-which','m2-sed','m2-gzip','m2-coreutils'] if cfg.include_toplevel_extras: - deps += ['gsl','libnexus','nexpy','cif2hkl >=1.5.1','mcpl-extra >=2.2.8','kdsource'] + deps += ['gsl','hdf5','nexpy','cif2hkl >=1.5.1','mcpl-extra >=2.2.8','kdsource'] if cfg.is_mcstas: deps += ['ncrystal >=4.4.2'] if cfg.is_mcxtrace: diff --git a/docs/manuals/mcstas/kernel.tex b/docs/manuals/mcstas/kernel.tex index 3ccc16ad04..9882ad9fac 100644 --- a/docs/manuals/mcstas/kernel.tex +++ b/docs/manuals/mcstas/kernel.tex @@ -465,11 +465,10 @@ \subsection{The \texttt{NEXUS} extension} \label{s:nexus} The NeXus format~\cite{nexus_webpage} requires to link the simulation to -additional libraries (HDF and NeXus) which must have been -pre-installed. Preferably, \MCS should have been installed with the +the HDF5 library, which must have been pre-installed. Preferably, \MCS should have been installed with the \verb+./configure --with-nexus+ on Unix/Linux systems. To activate the NeXus output, the compilation of the instrument must be done with flag -\verb+-DUSE_NEXUS+ \verb+-lNeXus+. +\verb+-DUSE_NEXUS+ \verb+-lhdf5+. The resulting executable is no longer portable. The default NeXus format is HDF5 with compression. diff --git a/docs/manuals/mcstas/links_othercode.tex b/docs/manuals/mcstas/links_othercode.tex index 942185d063..f67399ee1a 100644 --- a/docs/manuals/mcstas/links_othercode.tex +++ b/docs/manuals/mcstas/links_othercode.tex @@ -15,8 +15,7 @@ \subsection{System requirements} your system: \begin{itemize} \item{\MCS 2.1 or newer} -\item{NeXus libraries from \verb+http://nexusformat.org+ \footnote{For Mac OS X you may get - better milage via the Mantid github site}} +\item{The HDF5 library from \verb+https://www.hdfgroup.org+ (also in conda-forge, Homebrew and the Linux distributions)} \item{Mantid 3.2 or newer} \item{On Mac OS X 10.8 and newer you may need to install one of the below compilers, as the default clang on OS X causes problems for @@ -77,7 +76,7 @@ \subsection{Compiling and running your simulation for Mantid output} \begin{enumerate} \item First of all, enable NeXus in the compilation process: \begin{mcstas} - export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lNeXus" + export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lhdf5" \end{mcstas} \item Compile the instrument via the mcrun utility (add \verb+--mpi+ if you need parallelization support): diff --git a/docs/manuals/mcstas/running.tex b/docs/manuals/mcstas/running.tex index d958ab37ff..557f028b78 100644 --- a/docs/manuals/mcstas/running.tex +++ b/docs/manuals/mcstas/running.tex @@ -1764,9 +1764,9 @@ \subsection{NeXus format} The NeXus format~\cite{nexus_webpage} is a platform independent HDF binary data file. To have \MCS use it \begin{enumerate} -\item the HDF and NeXus libraries must have been installed (libNeXus and headers) +\item the HDF5 library must have been installed (libhdf5 and headers) \item the compilation of instruments must be done with the - \verb+-DUSE_NEXUS -lNeXus+ flag (see Section \ref{s:nexus}). This is + \verb+-DUSE_NEXUS -lhdf5+ flag (see Section \ref{s:nexus}). This is automated with the \verb+mcrun+ tool (Section \ref{s:mcrun}). \end{enumerate} All results are saved in a single file, containing 'groups' of data. To view diff --git a/docs/manuals/mcxtrace/kernel.tex b/docs/manuals/mcxtrace/kernel.tex index 3ef343ffeb..400a2014f4 100644 --- a/docs/manuals/mcxtrace/kernel.tex +++ b/docs/manuals/mcxtrace/kernel.tex @@ -300,9 +300,9 @@ \subsection{The \texttt{NEXUS} extension} \index{Keyword!NEXUS} \index{Tools!NeXus} \index{Tools!HDF} \index{Data formats} \label{s:nexus} -To use the NeXus format~\cite{nexus_webpage} the simulation must be linked with additional libraries (HDF and NeXus) which must have been pre-installed. Preferrably, \MCX should +To use the NeXus format~\cite{nexus_webpage} the simulation must be linked with the HDF5 library, which must have been pre-installed. Preferrably, \MCX should have been installed with the \verb+./configure --with-nexus+ on Unix/Linux systems. To activate the NeXus output, the instrument must be compiled with the flags -\verb+-DUSE_NEXUS -lNeXus+. +\verb+-DUSE_NEXUS -lhdf5+. The default NeXus format is NeXus 5 with compression. However, that format may be changed with the optional keyword \verb+NEXUS+ to follow the INITIALIZE section, namely: diff --git a/docs/manuals/mcxtrace/running.tex b/docs/manuals/mcxtrace/running.tex index 36de00bd41..202f852b4f 100644 --- a/docs/manuals/mcxtrace/running.tex +++ b/docs/manuals/mcxtrace/running.tex @@ -1653,9 +1653,9 @@ \subsection{NeXus format} The NeXus format~\cite{nexus_webpage} is a platform independent HDF binary data file. To have \MCX use it \begin{enumerate} -\item the HDF and NeXus libraries must have been installed (libNeXus and headers) +\item the HDF5 library must have been installed (libhdf5 and headers) \item the compilation of instruments must be done with the - \verb+-DUSE_NEXUS -lNeXus+ flag (see Section \ref{s:nexus}). This is + \verb+-DUSE_NEXUS -lhdf5+ flag (see Section \ref{s:nexus}). This is automated with the \verb+mxrun+ tool (Section \ref{s:mxrun}). \end{enumerate} All results are saved in a single file, containing 'groups' of data. To view diff --git a/environment.yml b/environment.yml index c75b1d2e4e..3235f5de32 100644 --- a/environment.yml +++ b/environment.yml @@ -25,7 +25,6 @@ dependencies: - pyqtgraph - qtpy - qscintilla2 - - nexus - nexusformat - nexpy - hdf5 diff --git a/mccode/src/cogen.c.in b/mccode/src/cogen.c.in index ca24eff9fc..0afe71323e 100644 --- a/mccode/src/cogen.c.in +++ b/mccode/src/cogen.c.in @@ -2917,7 +2917,7 @@ cogen_header(struct instr_def *instr, char *output_name) embed_file("mcxtrace-r.h"); #endif /* NeXus support, only active with -DUSE_NEXUS */ - if (verbose) fprintf(stderr, "Compile '%s -DUSE_NEXUS -lNeXus' to enable NeXus support\n", output_name); + if (verbose) fprintf(stderr, "Compile '%s -DUSE_NEXUS -lhdf5' to enable NeXus support\n", output_name); embed_file("mccode-r.c"); #if MCCODE_PROJECT == 1 /* neutron */ embed_file("mcstas-r.c"); @@ -2937,7 +2937,7 @@ cogen_header(struct instr_def *instr, char *output_name) fprintf(stderr,"Dependency: %s.o\n", "mcxtrace-r"); #endif - fprintf(stderr,"Dependency: '-DUSE_NEXUS -lNeXus' to enable NeXus support\n"); + fprintf(stderr,"Dependency: '-DUSE_NEXUS -lhdf5' to enable NeXus support\n"); fprintf(stderr,"To build instrument '%s', compile and link with these libraries (in %s%sshare)\n", instrument_definition->quoted_source, sysdir_new, pathsep); } diff --git a/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/ILL_H16_IN5_Mantid.instr b/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/ILL_H16_IN5_Mantid.instr index 52bfef43e9..5f3446690d 100644 --- a/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/ILL_H16_IN5_Mantid.instr +++ b/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/ILL_H16_IN5_Mantid.instr @@ -18,7 +18,7 @@ *
    *
  1. Compile the instrument with NeXus support - zero neutrons: *
      -*
    • export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lNeXus" +*
    • export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lhdf5" *
    • mcrun -c -n0 --format=NeXus ILL_H16_IN5_Mantid.instr --no-output-files *
    *
  2. Generate the IDF in XML format using the (Perl) mcdisplay tool: diff --git a/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/README.md b/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/README.md index d3924acf52..c24244f52a 100644 --- a/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/README.md +++ b/mcstas-comps/examples/Mantid/ILL_H16_IN5_Mantid/README.md @@ -18,7 +18,7 @@ use with Mantid-friendly NeXus output. To compile and run, use these steps:
    1. Compile the instrument with NeXus support - zero neutrons:
        -
      • export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lNeXus" +
      • export MCSTAS_CFLAGS="-g -lm -O2 -DUSE_NEXUS -lhdf5"
      • mcrun -c -n0 --format=NeXus ILL_H16_IN5_Mantid.instr --no-output-files
    2. Generate the IDF in XML format using the (Perl) mcdisplay tool: diff --git a/meta-pkgs/windows/environment.yml b/meta-pkgs/windows/environment.yml index 7134de3539..2cf675c0bd 100644 --- a/meta-pkgs/windows/environment.yml +++ b/meta-pkgs/windows/environment.yml @@ -25,7 +25,6 @@ dependencies: - pyqtgraph - qtpy - qscintilla2 - - nexus - nexusformat - nexpy - hdf5 diff --git a/meta-pkgs/windows/mcstas-environment.yml b/meta-pkgs/windows/mcstas-environment.yml index bea4d70219..744658f49a 100644 --- a/meta-pkgs/windows/mcstas-environment.yml +++ b/meta-pkgs/windows/mcstas-environment.yml @@ -25,7 +25,6 @@ dependencies: - pyqtgraph - qtpy - qscintilla2 - - nexus - nexusformat - nexpy - hdf5 diff --git a/meta-pkgs/windows/mcxtrace-environment.yml b/meta-pkgs/windows/mcxtrace-environment.yml index 11ee228800..e7452aa3ed 100644 --- a/meta-pkgs/windows/mcxtrace-environment.yml +++ b/meta-pkgs/windows/mcxtrace-environment.yml @@ -25,7 +25,6 @@ dependencies: - pyqtgraph - qtpy - qscintilla2 - - nexus - nexusformat - nexpy - hdf5 diff --git a/support/MacOSX/Check-dependencies/README.TXT b/support/MacOSX/Check-dependencies/README.TXT index da1c01a464..52cbf49727 100644 --- a/support/MacOSX/Check-dependencies/README.TXT +++ b/support/MacOSX/Check-dependencies/README.TXT @@ -31,9 +31,9 @@ Contents of this folder: - Will ask you to take ownership of /usr/local - Will ask you to configure the firewall to allow simple usage of openmpi -6) If you need support for NeXus output, we propose to install this via homebrew (brew install nexusformat). - To allow compilation with the NeXus libraries, please redefine your CFLAGS through the mcgui/mxgui preferences: - -g -lm -O2 -DUSE_NEXUS -lNeXus -I/usr/local/include/nexus +6) If you need support for NeXus output, we propose to install HDF5 via homebrew (brew install hdf5). + To allow compilation with HDF5, please redefine your CFLAGS through the mcgui/mxgui preferences: + -g -lm -O2 -DUSE_NEXUS -I/usr/local/include -L/usr/local/lib -lhdf5 7) Now open either the newly installed app or environment script